-
Notifications
You must be signed in to change notification settings - Fork 69
/
wmf.py
641 lines (554 loc) · 23.1 KB
/
wmf.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
"""
Hachoir parser of Microsoft Windows Metafile (WMF) file format.
Documentation:
- Microsoft Windows Metafile; also known as: WMF,
Enhanced Metafile, EMF, APM
http://wvware.sourceforge.net/caolan/ora-wmf.html
- libwmf source code:
- include/libwmf/defs.h: enums
- src/player/meta.h: arguments parsers
- libemf source code
Author: Victor Stinner
Creation date: 26 december 2006
"""
from hachoir.parser import Parser
from hachoir.field import (FieldSet, StaticFieldSet, Enum,
MissingField, ParserError,
UInt32, Int32, UInt16, Int16, UInt8, NullBytes, RawBytes, String)
from hachoir.core.endian import LITTLE_ENDIAN
from hachoir.core.text_handler import textHandler, hexadecimal
from hachoir.core.tools import createDict
from hachoir.parser.image.common import RGBA
MAX_FILESIZE = 50 * 1024 * 1024
POLYFILL_MODE = {1: "Alternate", 2: "Winding"}
BRUSH_STYLE = {
0: "Solid",
1: "Null",
2: "Hollow",
3: "Pattern",
4: "Indexed",
5: "DIB pattern",
6: "DIB pattern point",
7: "Pattern 8x8",
8: "DIB pattern 8x8",
}
HATCH_STYLE = {
0: "Horizontal", # -----
1: "Vertical", # |||||
2: "FDIAGONAL", # \\\\\
3: "BDIAGONAL", # /////
4: "Cross", # +++++
5: "Diagonal cross", # xxxxx
}
PEN_STYLE = {
0: "Solid",
1: "Dash", # -------
2: "Dot", # .......
3: "Dash dot", # _._._._
4: "Dash dot dot", # _.._.._
5: "Null",
6: "Inside frame",
7: "User style",
8: "Alternate",
}
# Binary raster operations
ROP2_DESC = {
1: "Black (0)",
2: "Not merge pen (DPon)",
3: "Mask not pen (DPna)",
4: "Not copy pen (PN)",
5: "Mask pen not (PDna)",
6: "Not (Dn)",
7: "Xor pen (DPx)",
8: "Not mask pen (DPan)",
9: "Mask pen (DPa)",
10: "Not xor pen (DPxn)",
11: "No operation (D)",
12: "Merge not pen (DPno)",
13: "Copy pen (P)",
14: "Merge pen not (PDno)",
15: "Merge pen (DPo)",
16: "White (1)",
}
def parseXY(parser):
yield Int16(parser, "x")
yield Int16(parser, "y")
def parseCreateBrushIndirect(parser):
yield Enum(UInt16(parser, "brush_style"), BRUSH_STYLE)
yield RGBA(parser, "color")
yield Enum(UInt16(parser, "brush_hatch"), HATCH_STYLE)
def parsePenIndirect(parser):
yield Enum(UInt16(parser, "pen_style"), PEN_STYLE)
yield UInt16(parser, "pen_width")
yield UInt16(parser, "pen_height")
yield RGBA(parser, "color")
def parsePolyFillMode(parser):
yield Enum(UInt16(parser, "operation"), POLYFILL_MODE)
def parseROP2(parser):
yield Enum(UInt16(parser, "operation"), ROP2_DESC)
def parseObjectID(parser):
yield UInt16(parser, "object_id")
class Point(FieldSet):
static_size = 32
def createFields(self):
yield Int16(self, "x")
yield Int16(self, "y")
def createDescription(self):
return "Point (%s, %s)" % (self["x"].value, self["y"].value)
def parsePolygon(parser):
yield UInt16(parser, "count")
for index in range(parser["count"].value):
yield Point(parser, "point[]")
META = {
0x0000: ("EOF", "End of file", None),
0x001E: ("SAVEDC", "Save device context", None),
0x0035: ("REALIZEPALETTE", "Realize palette", None),
0x0037: ("SETPALENTRIES", "Set palette entries", None),
0x00f7: ("CREATEPALETTE", "Create palette", None),
0x0102: ("SETBKMODE", "Set background mode", None),
0x0103: ("SETMAPMODE", "Set mapping mode", None),
0x0104: ("SETROP2", "Set foreground mix mode", parseROP2),
0x0106: ("SETPOLYFILLMODE", "Set polygon fill mode", parsePolyFillMode),
0x0107: ("SETSTRETCHBLTMODE", "Set bitmap streching mode", None),
0x0108: ("SETTEXTCHAREXTRA", "Set text character extra", None),
0x0127: ("RESTOREDC", "Restore device context", None),
0x012A: ("INVERTREGION", "Invert region", None),
0x012B: ("PAINTREGION", "Paint region", None),
0x012C: ("SELECTCLIPREGION", "Select clipping region", None),
0x012D: ("SELECTOBJECT", "Select object", parseObjectID),
0x012E: ("SETTEXTALIGN", "Set text alignment", None),
0x0142: ("CREATEDIBPATTERNBRUSH", "Create DIB brush with specified pattern", None),
0x01f0: ("DELETEOBJECT", "Delete object", parseObjectID),
0x0201: ("SETBKCOLOR", "Set background color", None),
0x0209: ("SETTEXTCOLOR", "Set text color", None),
0x020A: ("SETTEXTJUSTIFICATION", "Set text justification", None),
0x020B: ("SETWINDOWORG", "Set window origin", parseXY),
0x020C: ("SETWINDOWEXT", "Set window extends", parseXY),
0x020D: ("SETVIEWPORTORG", "Set view port origin", None),
0x020E: ("SETVIEWPORTEXT", "Set view port extends", None),
0x020F: ("OFFSETWINDOWORG", "Offset window origin", None),
0x0211: ("OFFSETVIEWPORTORG", "Offset view port origin", None),
0x0213: ("LINETO", "Draw a line to", None),
0x0214: ("MOVETO", "Move to", None),
0x0220: ("OFFSETCLIPRGN", "Offset clipping rectangle", None),
0x0228: ("FILLREGION", "Fill region", None),
0x0231: ("SETMAPPERFLAGS", "Set mapper flags", None),
0x0234: ("SELECTPALETTE", "Select palette", None),
0x02FB: ("CREATEFONTINDIRECT", "Create font indirect", None),
0x02FA: ("CREATEPENINDIRECT", "Create pen indirect", parsePenIndirect),
0x02FC: ("CREATEBRUSHINDIRECT", "Create brush indirect", parseCreateBrushIndirect),
0x0324: ("POLYGON", "Draw a polygon", parsePolygon),
0x0325: ("POLYLINE", "Draw a polyline", None),
0x0410: ("SCALEWINDOWEXT", "Scale window extends", None),
0x0412: ("SCALEVIEWPORTEXT", "Scale view port extends", None),
0x0415: ("EXCLUDECLIPRECT", "Exclude clipping rectangle", None),
0x0416: ("INTERSECTCLIPRECT", "Intersect clipping rectangle", None),
0x0418: ("ELLIPSE", "Draw an ellipse", None),
0x0419: ("FLOODFILL", "Flood fill", None),
0x041B: ("RECTANGLE", "Draw a rectangle", None),
0x041F: ("SETPIXEL", "Set pixel", None),
0x0429: ("FRAMEREGION", "Fram region", None),
0x0521: ("TEXTOUT", "Draw text", None),
0x0538: ("POLYPOLYGON", "Draw multiple polygons", None),
0x0548: ("EXTFLOODFILL", "Extend flood fill", None),
0x061C: ("ROUNDRECT", "Draw a rounded rectangle", None),
0x061D: ("PATBLT", "Pattern blitting", None),
0x0626: ("ESCAPE", "Escape", None),
0x06FF: ("CREATEREGION", "Create region", None),
0x0817: ("ARC", "Draw an arc", None),
0x081A: ("PIE", "Draw a pie", None),
0x0830: ("CHORD", "Draw a chord", None),
0x0940: ("DIBBITBLT", "DIB bit blitting", None),
0x0a32: ("EXTTEXTOUT", "Draw text (extra)", None),
0x0b41: ("DIBSTRETCHBLT", "DIB stretch blitting", None),
0x0d33: ("SETDIBTODEV", "Set DIB to device", None),
0x0f43: ("STRETCHDIB", "Stretch DIB", None),
}
META_NAME = createDict(META, 0)
META_DESC = createDict(META, 1)
# ----------------------------------------------------------------------------
# EMF constants
# EMF mapping modes
EMF_MAPPING_MODE = {
1: "TEXT",
2: "LOMETRIC",
3: "HIMETRIC",
4: "LOENGLISH",
5: "HIENGLISH",
6: "TWIPS",
7: "ISOTROPIC",
8: "ANISOTROPIC",
}
# ----------------------------------------------------------------------------
# EMF parser
def parseEmfMappingMode(parser):
yield Enum(Int32(parser, "mapping_mode"), EMF_MAPPING_MODE)
def parseXY32(parser):
yield Int32(parser, "x")
yield Int32(parser, "y")
def parseObjectID32(parser):
yield textHandler(UInt32(parser, "object_id"), hexadecimal)
def parseBrushIndirect(parser):
yield UInt32(parser, "ihBrush")
yield UInt32(parser, "style")
yield RGBA(parser, "color")
yield Int32(parser, "hatch")
class Point16(FieldSet):
static_size = 32
def createFields(self):
yield Int16(self, "x")
yield Int16(self, "y")
def createDescription(self):
return "Point16: (%i,%i)" % (self["x"].value, self["y"].value)
def parsePoint16array(parser):
yield RECT32(parser, "bounds")
yield UInt32(parser, "count")
for index in range(parser["count"].value):
yield Point16(parser, "point[]")
def parseGDIComment(parser):
yield UInt32(parser, "data_size")
size = parser["data_size"].value
if size:
yield RawBytes(parser, "data", size)
def parseICMMode(parser):
yield UInt32(parser, "icm_mode")
def parseExtCreatePen(parser):
yield UInt32(parser, "ihPen")
yield UInt32(parser, "offBmi")
yield UInt32(parser, "cbBmi")
yield UInt32(parser, "offBits")
yield UInt32(parser, "cbBits")
yield UInt32(parser, "pen_style")
yield UInt32(parser, "width")
yield UInt32(parser, "brush_style")
yield RGBA(parser, "color")
yield UInt32(parser, "hatch")
yield UInt32(parser, "nb_style")
for index in range(parser["nb_style"].value):
yield UInt32(parser, "style")
EMF_META = {
1: ("HEADER", "Header", None),
2: ("POLYBEZIER", "Draw poly bezier", None),
3: ("POLYGON", "Draw polygon", None),
4: ("POLYLINE", "Draw polyline", None),
5: ("POLYBEZIERTO", "Draw poly bezier to", None),
6: ("POLYLINETO", "Draw poly line to", None),
7: ("POLYPOLYLINE", "Draw poly polyline", None),
8: ("POLYPOLYGON", "Draw poly polygon", None),
9: ("SETWINDOWEXTEX", "Set window extend EX", parseXY32),
10: ("SETWINDOWORGEX", "Set window origin EX", parseXY32),
11: ("SETVIEWPORTEXTEX", "Set viewport extend EX", parseXY32),
12: ("SETVIEWPORTORGEX", "Set viewport origin EX", parseXY32),
13: ("SETBRUSHORGEX", "Set brush org EX", None),
14: ("EOF", "End of file", None),
15: ("SETPIXELV", "Set pixel V", None),
16: ("SETMAPPERFLAGS", "Set mapper flags", None),
17: ("SETMAPMODE", "Set mapping mode", parseEmfMappingMode),
18: ("SETBKMODE", "Set background mode", None),
19: ("SETPOLYFILLMODE", "Set polyfill mode", None),
20: ("SETROP2", "Set ROP2", None),
21: ("SETSTRETCHBLTMODE", "Set stretching blitting mode", None),
22: ("SETTEXTALIGN", "Set text align", None),
23: ("SETCOLORADJUSTMENT", "Set color adjustment", None),
24: ("SETTEXTCOLOR", "Set text color", None),
25: ("SETBKCOLOR", "Set background color", None),
26: ("OFFSETCLIPRGN", "Offset clipping region", None),
27: ("MOVETOEX", "Move to EX", parseXY32),
28: ("SETMETARGN", "Set meta region", None),
29: ("EXCLUDECLIPRECT", "Exclude clipping rectangle", None),
30: ("INTERSECTCLIPRECT", "Intersect clipping rectangle", None),
31: ("SCALEVIEWPORTEXTEX", "Scale viewport extend EX", None),
32: ("SCALEWINDOWEXTEX", "Scale window extend EX", None),
33: ("SAVEDC", "Save device context", None),
34: ("RESTOREDC", "Restore device context", None),
35: ("SETWORLDTRANSFORM", "Set world transform", None),
36: ("MODIFYWORLDTRANSFORM", "Modify world transform", None),
37: ("SELECTOBJECT", "Select object", parseObjectID32),
38: ("CREATEPEN", "Create pen", None),
39: ("CREATEBRUSHINDIRECT", "Create brush indirect", parseBrushIndirect),
40: ("DELETEOBJECT", "Delete object", parseObjectID32),
41: ("ANGLEARC", "Draw angle arc", None),
42: ("ELLIPSE", "Draw ellipse", None),
43: ("RECTANGLE", "Draw rectangle", None),
44: ("ROUNDRECT", "Draw rounded rectangle", None),
45: ("ARC", "Draw arc", None),
46: ("CHORD", "Draw chord", None),
47: ("PIE", "Draw pie", None),
48: ("SELECTPALETTE", "Select palette", None),
49: ("CREATEPALETTE", "Create palette", None),
50: ("SETPALETTEENTRIES", "Set palette entries", None),
51: ("RESIZEPALETTE", "Resize palette", None),
52: ("REALIZEPALETTE", "Realize palette", None),
53: ("EXTFLOODFILL", "EXT flood fill", None),
54: ("LINETO", "Draw line to", parseXY32),
55: ("ARCTO", "Draw arc to", None),
56: ("POLYDRAW", "Draw poly draw", None),
57: ("SETARCDIRECTION", "Set arc direction", None),
58: ("SETMITERLIMIT", "Set miter limit", None),
59: ("BEGINPATH", "Begin path", None),
60: ("ENDPATH", "End path", None),
61: ("CLOSEFIGURE", "Close figure", None),
62: ("FILLPATH", "Fill path", None),
63: ("STROKEANDFILLPATH", "Stroke and fill path", None),
64: ("STROKEPATH", "Stroke path", None),
65: ("FLATTENPATH", "Flatten path", None),
66: ("WIDENPATH", "Widen path", None),
67: ("SELECTCLIPPATH", "Select clipping path", None),
68: ("ABORTPATH", "Arbort path", None),
70: ("GDICOMMENT", "GDI comment", parseGDIComment),
71: ("FILLRGN", "Fill region", None),
72: ("FRAMERGN", "Frame region", None),
73: ("INVERTRGN", "Invert region", None),
74: ("PAINTRGN", "Paint region", None),
75: ("EXTSELECTCLIPRGN", "EXT select clipping region", None),
76: ("BITBLT", "Bit blitting", None),
77: ("STRETCHBLT", "Stretch blitting", None),
78: ("MASKBLT", "Mask blitting", None),
79: ("PLGBLT", "PLG blitting", None),
80: ("SETDIBITSTODEVICE", "Set DIB bits to device", None),
81: ("STRETCHDIBITS", "Stretch DIB bits", None),
82: ("EXTCREATEFONTINDIRECTW", "EXT create font indirect W", None),
83: ("EXTTEXTOUTA", "EXT text out A", None),
84: ("EXTTEXTOUTW", "EXT text out W", None),
85: ("POLYBEZIER16", "Draw poly bezier (16-bit)", None),
86: ("POLYGON16", "Draw polygon (16-bit)", parsePoint16array),
87: ("POLYLINE16", "Draw polyline (16-bit)", parsePoint16array),
88: ("POLYBEZIERTO16", "Draw poly bezier to (16-bit)", parsePoint16array),
89: ("POLYLINETO16", "Draw polyline to (16-bit)", parsePoint16array),
90: ("POLYPOLYLINE16", "Draw poly polyline (16-bit)", None),
91: ("POLYPOLYGON16", "Draw poly polygon (16-bit)", parsePoint16array),
92: ("POLYDRAW16", "Draw poly draw (16-bit)", None),
93: ("CREATEMONOBRUSH", "Create monobrush", None),
94: ("CREATEDIBPATTERNBRUSHPT", "Create DIB pattern brush PT", None),
95: ("EXTCREATEPEN", "EXT create pen", parseExtCreatePen),
96: ("POLYTEXTOUTA", "Poly text out A", None),
97: ("POLYTEXTOUTW", "Poly text out W", None),
98: ("SETICMMODE", "Set ICM mode", parseICMMode),
99: ("CREATECOLORSPACE", "Create color space", None),
100: ("SETCOLORSPACE", "Set color space", None),
101: ("DELETECOLORSPACE", "Delete color space", None),
102: ("GLSRECORD", "GLS record", None),
103: ("GLSBOUNDEDRECORD", "GLS bound ED record", None),
104: ("PIXELFORMAT", "Pixel format", None),
}
EMF_META_NAME = createDict(EMF_META, 0)
EMF_META_DESC = createDict(EMF_META, 1)
class Function(FieldSet):
def __init__(self, *args):
FieldSet.__init__(self, *args)
if self.root.isEMF():
self._size = self["size"].value * 8
else:
self._size = self["size"].value * 16
def createFields(self):
if self.root.isEMF():
yield Enum(UInt32(self, "function"), EMF_META_NAME)
yield UInt32(self, "size")
try:
parser = EMF_META[self["function"].value][2]
except KeyError:
parser = None
else:
yield UInt32(self, "size")
yield Enum(UInt16(self, "function"), META_NAME)
try:
parser = META[self["function"].value][2]
except KeyError:
parser = None
if parser:
yield from parser(self)
else:
size = (self.size - self.current_size) // 8
if size:
yield RawBytes(self, "data", size)
def isValid(self):
func = self["function"]
return func.value in func.getEnum()
def createDescription(self):
if self.root.isEMF():
return EMF_META_DESC[self["function"].value]
try:
return META_DESC[self["function"].value]
except KeyError:
return "Function %s" % self["function"].display
class RECT16(StaticFieldSet):
format = (
(Int16, "left"),
(Int16, "top"),
(Int16, "right"),
(Int16, "bottom"),
)
def createDescription(self):
return "%s: %ux%u at (%u,%u)" % (
self.__class__.__name__,
self["right"].value - self["left"].value,
self["bottom"].value - self["top"].value,
self["left"].value,
self["top"].value)
class RECT32(RECT16):
format = (
(Int32, "left"),
(Int32, "top"),
(Int32, "right"),
(Int32, "bottom"),
)
class PlaceableHeader(FieldSet):
"""
Header of Placeable Metafile (file extension .APM),
created by Aldus Corporation
"""
MAGIC = b"\xD7\xCD\xC6\x9A\0\0" # (magic, handle=0x0000)
def createFields(self):
yield textHandler(UInt32(self, "signature", "Placeable Metafiles signature (0x9AC6CDD7)"), hexadecimal)
yield UInt16(self, "handle")
yield RECT16(self, "rect")
yield UInt16(self, "inch")
yield NullBytes(self, "reserved", 4)
yield textHandler(UInt16(self, "checksum"), hexadecimal)
class EMF_Header(FieldSet):
MAGIC = b"\x20\x45\x4D\x46\0\0" # (magic, min_ver=0x0000)
def __init__(self, *args):
FieldSet.__init__(self, *args)
self._size = self["size"].value * 8
def createFields(self):
LONG = Int32
yield UInt32(self, "type", "Record type (always 1)")
yield UInt32(self, "size", "Size of the header in bytes")
yield RECT32(self, "Bounds", "Inclusive bounds")
yield RECT32(self, "Frame", "Inclusive picture frame")
yield textHandler(UInt32(self, "signature", "Signature ID (always 0x464D4520)"), hexadecimal)
yield UInt16(self, "min_ver", "Minor version")
yield UInt16(self, "maj_ver", "Major version")
yield UInt32(self, "file_size", "Size of the file in bytes")
yield UInt32(self, "NumOfRecords", "Number of records in the metafile")
yield UInt16(self, "NumOfHandles", "Number of handles in the handle table")
yield NullBytes(self, "reserved", 2)
yield UInt32(self, "desc_size", "Size of description in 16-bit words")
yield UInt32(self, "desc_ofst", "Offset of description string in metafile")
yield UInt32(self, "nb_colors", "Number of color palette entries")
yield LONG(self, "width_px", "Width of reference device in pixels")
yield LONG(self, "height_px", "Height of reference device in pixels")
yield LONG(self, "width_mm", "Width of reference device in millimeters")
yield LONG(self, "height_mm", "Height of reference device in millimeters")
# Read description (if any)
offset = self["desc_ofst"].value
current = (self.absolute_address + self.current_size) // 8
size = self["desc_size"].value * 2
if offset == current and size:
yield String(self, "description", size, charset="UTF-16-LE", strip="\0 ")
# Read padding (if any)
size = self["size"].value - self.current_size // 8
if size:
yield RawBytes(self, "padding", size)
class WMF_File(Parser):
PARSER_TAGS = {
"id": "wmf",
"category": "image",
"file_ext": ("wmf", "apm", "emf"),
"mime": (
"image/wmf", "image/x-wmf", "image/x-win-metafile",
"application/x-msmetafile", "application/wmf", "application/x-wmf",
"image/x-emf"),
"magic": (
(PlaceableHeader.MAGIC, 0),
(EMF_Header.MAGIC, 40 * 8),
# WMF: file_type=memory, header size=9, version=3.0
(b"\0\0\x09\0\0\3", 0),
# WMF: file_type=disk, header size=9, version=3.0
(b"\1\0\x09\0\0\3", 0),
),
"min_size": 40 * 8,
"description": "Microsoft Windows Metafile (WMF)",
}
endian = LITTLE_ENDIAN
FILE_TYPE = {0: "memory", 1: "disk"}
def validate(self):
if self.isEMF():
# Check EMF header
emf = self["emf_header"]
if emf["signature"].value != 0x464D4520:
return "Invalid signature"
if emf["type"].value != 1:
return "Invalid record type"
if emf["reserved"].value != b"\0\0":
return "Invalid reserved"
else:
# Check AMF header
if self.isAPM():
amf = self["amf_header"]
if amf["handle"].value != 0:
return "Invalid handle"
if amf["reserved"].value != b"\0\0\0\0":
return "Invalid reserved"
# Check common header
if self["file_type"].value not in (0, 1):
return "Invalid file type"
if self["header_size"].value != 9:
return "Invalid header size"
if self["nb_params"].value != 0:
return "Invalid number of parameters"
# Check first functions
for index in range(5):
try:
func = self["func[%u]" % index]
except MissingField:
if self.done:
return True
return "Unable to get function #%u" % index
except ParserError:
return "Unable to create function #%u" % index
# Check first frame values
if not func.isValid():
return "Function #%u is invalid" % index
return True
def createFields(self):
if self.isEMF():
yield EMF_Header(self, "emf_header")
else:
if self.isAPM():
yield PlaceableHeader(self, "amf_header")
yield Enum(UInt16(self, "file_type"), self.FILE_TYPE)
yield UInt16(self, "header_size", "Size of header in 16-bit words (always 9)")
yield UInt8(self, "win_ver_min", "Minor version of Microsoft Windows")
yield UInt8(self, "win_ver_maj", "Major version of Microsoft Windows")
yield UInt32(self, "file_size", "Total size of the metafile in 16-bit words")
yield UInt16(self, "nb_obj", "Number of objects in the file")
yield UInt32(self, "max_record_size", "The size of largest record in 16-bit words")
yield UInt16(self, "nb_params", "Not Used (always 0)")
while not self.eof:
yield Function(self, "func[]")
def isEMF(self):
"""File is in EMF format?"""
if 1 <= self.current_length:
return self[0].name == "emf_header"
if self.size < 44 * 8:
return False
magic = EMF_Header.MAGIC
return self.stream.readBytes(40 * 8, len(magic)) == magic
def isAPM(self):
"""File is in Aldus Placeable Metafiles format?"""
if 1 <= self.current_length:
return self[0].name == "amf_header"
else:
magic = PlaceableHeader.MAGIC
return (self.stream.readBytes(0, len(magic)) == magic)
def createDescription(self):
if self.isEMF():
return "Microsoft Enhanced Metafile (EMF) picture"
elif self.isAPM():
return "Aldus Placeable Metafile (APM) picture"
else:
return "Microsoft Windows Metafile (WMF) picture"
def createMimeType(self):
if self.isEMF():
return "image/x-emf"
else:
return "image/wmf"
def createContentSize(self):
if self.isEMF():
return None
start = self["func[0]"].absolute_address
end = self.stream.searchBytes(b"\3\0\0\0\0\0", start, MAX_FILESIZE * 8)
if end is not None:
return end + 6 * 8
return None