WMF (Windows Metafile) is a relatively early vector image format introduced for Microsoft Windows in 1990.
Inside, it provides a serialized list of Windows GDI (Graphics Device Interface) function calls, which, if played back, result in an image being drawn on a given surface (display, off-screen buffer, printer, etc).
This page hosts a formal specification of Windows Metafile (WMF) vector image using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.
digraph {
rankdir=LR;
node [shape=plaintext];
subgraph cluster__wmf {
label="Wmf";
graph[style=dotted];
wmf__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
<TR><TD PORT="special_header_pos">0</TD><TD PORT="special_header_size">22</TD><TD>SpecialHeader</TD><TD PORT="special_header_type">special_header</TD></TR>
<TR><TD PORT="header_pos">22</TD><TD PORT="header_size">18</TD><TD>Header</TD><TD PORT="header_type">header</TD></TR>
<TR><TD PORT="records_pos">40</TD><TD PORT="records_size">...</TD><TD>Record</TD><TD PORT="records_type">records</TD></TR>
<TR><TD COLSPAN="4" PORT="records__repeat">repeat until _.function == :func_eof</TD></TR>
</TABLE>>];
subgraph cluster__params_setwindoworg {
label="Wmf::ParamsSetwindoworg";
graph[style=dotted];
params_setwindoworg__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
<TR><TD PORT="y_pos">0</TD><TD PORT="y_size">2</TD><TD>s2le</TD><TD PORT="y_type">y</TD></TR>
<TR><TD PORT="x_pos">2</TD><TD PORT="x_size">2</TD><TD>s2le</TD><TD PORT="x_type">x</TD></TR>
</TABLE>>];
}
subgraph cluster__params_setbkmode {
label="Wmf::ParamsSetbkmode";
graph[style=dotted];
params_setbkmode__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
<TR><TD PORT="bk_mode_pos">0</TD><TD PORT="bk_mode_size">2</TD><TD>u2le→MixMode</TD><TD PORT="bk_mode_type">bk_mode</TD></TR>
</TABLE>>];
}
subgraph cluster__point_s {
label="Wmf::PointS";
graph[style=dotted];
point_s__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
<TR><TD PORT="x_pos">0</TD><TD PORT="x_size">2</TD><TD>s2le</TD><TD PORT="x_type">x</TD></TR>
<TR><TD PORT="y_pos">2</TD><TD PORT="y_size">2</TD><TD>s2le</TD><TD PORT="y_type">y</TD></TR>
</TABLE>>];
}
subgraph cluster__params_setwindowext {
label="Wmf::ParamsSetwindowext";
graph[style=dotted];
params_setwindowext__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
<TR><TD PORT="y_pos">0</TD><TD PORT="y_size">2</TD><TD>s2le</TD><TD PORT="y_type">y</TD></TR>
<TR><TD PORT="x_pos">2</TD><TD PORT="x_size">2</TD><TD>s2le</TD><TD PORT="x_type">x</TD></TR>
</TABLE>>];
}
subgraph cluster__params_polygon {
label="Wmf::ParamsPolygon";
graph[style=dotted];
params_polygon__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
<TR><TD PORT="num_points_pos">0</TD><TD PORT="num_points_size">2</TD><TD>s2le</TD><TD PORT="num_points_type">num_points</TD></TR>
<TR><TD PORT="points_pos">2</TD><TD PORT="points_size">4</TD><TD>PointS</TD><TD PORT="points_type">points</TD></TR>
<TR><TD COLSPAN="4" PORT="points__repeat">repeat num_points times</TD></TR>
</TABLE>>];
}
subgraph cluster__header {
label="Wmf::Header";
graph[style=dotted];
header__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
<TR><TD PORT="metafile_type_pos">0</TD><TD PORT="metafile_type_size">2</TD><TD>u2le→MetafileType</TD><TD PORT="metafile_type_type">metafile_type</TD></TR>
<TR><TD PORT="header_size_pos">2</TD><TD PORT="header_size_size">2</TD><TD>u2le</TD><TD PORT="header_size_type">header_size</TD></TR>
<TR><TD PORT="version_pos">4</TD><TD PORT="version_size">2</TD><TD>u2le</TD><TD PORT="version_type">version</TD></TR>
<TR><TD PORT="size_pos">6</TD><TD PORT="size_size">4</TD><TD>u4le</TD><TD PORT="size_type">size</TD></TR>
<TR><TD PORT="number_of_objects_pos">10</TD><TD PORT="number_of_objects_size">2</TD><TD>u2le</TD><TD PORT="number_of_objects_type">number_of_objects</TD></TR>
<TR><TD PORT="max_record_pos">12</TD><TD PORT="max_record_size">4</TD><TD>u4le</TD><TD PORT="max_record_type">max_record</TD></TR>
<TR><TD PORT="number_of_members_pos">16</TD><TD PORT="number_of_members_size">2</TD><TD>u2le</TD><TD PORT="number_of_members_type">number_of_members</TD></TR>
</TABLE>>];
}
subgraph cluster__color_ref {
label="Wmf::ColorRef";
graph[style=dotted];
color_ref__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
<TR><TD PORT="red_pos">0</TD><TD PORT="red_size">1</TD><TD>u1</TD><TD PORT="red_type">red</TD></TR>
<TR><TD PORT="green_pos">1</TD><TD PORT="green_size">1</TD><TD>u1</TD><TD PORT="green_type">green</TD></TR>
<TR><TD PORT="blue_pos">2</TD><TD PORT="blue_size">1</TD><TD>u1</TD><TD PORT="blue_type">blue</TD></TR>
<TR><TD PORT="reserved_pos">3</TD><TD PORT="reserved_size">1</TD><TD>u1</TD><TD PORT="reserved_type">reserved</TD></TR>
</TABLE>>];
}
subgraph cluster__params_setrop2 {
label="Wmf::ParamsSetrop2";
graph[style=dotted];
params_setrop2__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
<TR><TD PORT="draw_mode_pos">0</TD><TD PORT="draw_mode_size">2</TD><TD>u2le→BinRasterOp</TD><TD PORT="draw_mode_type">draw_mode</TD></TR>
</TABLE>>];
}
subgraph cluster__params_setpolyfillmode {
label="Wmf::ParamsSetpolyfillmode";
graph[style=dotted];
params_setpolyfillmode__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
<TR><TD PORT="poly_fill_mode_pos">0</TD><TD PORT="poly_fill_mode_size">2</TD><TD>u2le→PolyFillMode</TD><TD PORT="poly_fill_mode_type">poly_fill_mode</TD></TR>
</TABLE>>];
}
subgraph cluster__params_polyline {
label="Wmf::ParamsPolyline";
graph[style=dotted];
params_polyline__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
<TR><TD PORT="num_points_pos">0</TD><TD PORT="num_points_size">2</TD><TD>s2le</TD><TD PORT="num_points_type">num_points</TD></TR>
<TR><TD PORT="points_pos">2</TD><TD PORT="points_size">4</TD><TD>PointS</TD><TD PORT="points_type">points</TD></TR>
<TR><TD COLSPAN="4" PORT="points__repeat">repeat num_points times</TD></TR>
</TABLE>>];
}
subgraph cluster__special_header {
label="Wmf::SpecialHeader";
graph[style=dotted];
special_header__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
<TR><TD PORT="magic_pos">0</TD><TD PORT="magic_size">4</TD><TD></TD><TD PORT="magic_type">magic</TD></TR>
<TR><TD PORT="handle_pos">4</TD><TD PORT="handle_size">2</TD><TD></TD><TD PORT="handle_type">handle</TD></TR>
<TR><TD PORT="left_pos">6</TD><TD PORT="left_size">2</TD><TD>s2le</TD><TD PORT="left_type">left</TD></TR>
<TR><TD PORT="top_pos">8</TD><TD PORT="top_size">2</TD><TD>s2le</TD><TD PORT="top_type">top</TD></TR>
<TR><TD PORT="right_pos">10</TD><TD PORT="right_size">2</TD><TD>s2le</TD><TD PORT="right_type">right</TD></TR>
<TR><TD PORT="bottom_pos">12</TD><TD PORT="bottom_size">2</TD><TD>s2le</TD><TD PORT="bottom_type">bottom</TD></TR>
<TR><TD PORT="inch_pos">14</TD><TD PORT="inch_size">2</TD><TD>u2le</TD><TD PORT="inch_type">inch</TD></TR>
<TR><TD PORT="reserved_pos">16</TD><TD PORT="reserved_size">4</TD><TD></TD><TD PORT="reserved_type">reserved</TD></TR>
<TR><TD PORT="checksum_pos">20</TD><TD PORT="checksum_size">2</TD><TD>u2le</TD><TD PORT="checksum_type">checksum</TD></TR>
</TABLE>>];
}
subgraph cluster__record {
label="Wmf::Record";
graph[style=dotted];
record__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
<TR><TD PORT="size_pos">0</TD><TD PORT="size_size">4</TD><TD>u4le</TD><TD PORT="size_type">size</TD></TR>
<TR><TD PORT="function_pos">4</TD><TD PORT="function_size">2</TD><TD>u2le→Func</TD><TD PORT="function_type">function</TD></TR>
<TR><TD PORT="params_pos">6</TD><TD PORT="params_size">...</TD><TD>switch (function)</TD><TD PORT="params_type">params</TD></TR>
</TABLE>>];
record__seq_params_switch [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#F0F2E4">case</TD><TD BGCOLOR="#F0F2E4">type</TD></TR>
<TR><TD>:func_setbkmode</TD><TD PORT="case0">ParamsSetbkmode</TD></TR>
<TR><TD>:func_polygon</TD><TD PORT="case1">ParamsPolygon</TD></TR>
<TR><TD>:func_setbkcolor</TD><TD PORT="case2">ColorRef</TD></TR>
<TR><TD>:func_setpolyfillmode</TD><TD PORT="case3">ParamsSetpolyfillmode</TD></TR>
<TR><TD>:func_setwindoworg</TD><TD PORT="case4">ParamsSetwindoworg</TD></TR>
<TR><TD>:func_setrop2</TD><TD PORT="case5">ParamsSetrop2</TD></TR>
<TR><TD>:func_setwindowext</TD><TD PORT="case6">ParamsSetwindowext</TD></TR>
<TR><TD>:func_polyline</TD><TD PORT="case7">ParamsPolyline</TD></TR>
</TABLE>>];
}
}
wmf__seq:special_header_type -> special_header__seq [style=bold];
wmf__seq:header_type -> header__seq [style=bold];
wmf__seq:records_type -> record__seq [style=bold];
record__seq:function_type -> wmf__seq:records__repeat [color="#404040"];
params_polygon__seq:points_type -> point_s__seq [style=bold];
params_polygon__seq:num_points_type -> params_polygon__seq:points__repeat [color="#404040"];
params_polyline__seq:points_type -> point_s__seq [style=bold];
params_polyline__seq:num_points_type -> params_polyline__seq:points__repeat [color="#404040"];
record__seq:params_type -> record__seq_params_switch [style=bold];
record__seq_params_switch:case0 -> params_setbkmode__seq [style=bold];
record__seq_params_switch:case1 -> params_polygon__seq [style=bold];
record__seq_params_switch:case2 -> color_ref__seq [style=bold];
record__seq_params_switch:case3 -> params_setpolyfillmode__seq [style=bold];
record__seq_params_switch:case4 -> params_setwindoworg__seq [style=bold];
record__seq_params_switch:case5 -> params_setrop2__seq [style=bold];
record__seq_params_switch:case6 -> params_setwindowext__seq [style=bold];
record__seq_params_switch:case7 -> params_polyline__seq [style=bold];
record__seq:function_type -> record__seq:params_type [color="#404040"];
}