1 // This file is part of Deark.
2 // Copyright (C) 2016 Jason Summers
3 // See the file COPYING for terms of use.
5 // Windows Metafile (WMF)
7 #include <deark-config.h>
8 #include <deark-private.h>
9 #include <deark-fmtutil.h>
10 DE_DECLARE_MODULE(de_module_wmf
);
12 typedef struct localctx_struct
{
16 i64 wmf_windows_version
;
17 unsigned int num_objects
;
28 struct decoder_params
{
30 u8 rectype
; // low byte of recfunc
32 i64 recsize_words
; // total record size in 16-bit units
33 i64 recsize_bytes
; // total record size in bytes
38 // Handler functions return 0 on fatal error, otherwise 1.
39 typedef int (*record_decoder_fn
)(deark
*c
, lctx
*d
, struct decoder_params
*dp
);
41 struct wmf_func_info
{
42 u8 rectype
; // Low byte of the RecordFunction field
44 // 0x1: Creates an object
50 // Note: This is duplicated in emf.c
51 static u32
colorref_to_color(u32 colorref
)
54 r
= DE_COLOR_B(colorref
);
55 g
= DE_COLOR_G(colorref
);
56 b
= DE_COLOR_R(colorref
);
57 return DE_MAKE_RGB(r
,g
,b
);
60 // Note: This is duplicated in emf.c
61 static void do_dbg_colorref(deark
*c
, lctx
*d
, struct decoder_params
*dp
, u32 colorref
)
66 clr
= colorref_to_color(colorref
);
67 de_get_colorsample_code(c
, clr
, csamp
, sizeof(csamp
));
68 de_dbg(c
, "colorref: 0x%08x%s", (unsigned int)colorref
, csamp
);
71 static int handler_colorref(deark
*c
, lctx
*d
, struct decoder_params
*dp
)
75 if(dp
->dlen
<4) goto done
;
76 colorref
= (u32
)de_getu32le(dp
->dpos
);
77 do_dbg_colorref(c
, d
, dp
, colorref
);
82 static int wmf_handler_TEXTOUT(deark
*c
, lctx
*d
, struct decoder_params
*dp
)
86 de_ucstring
*s
= NULL
;
88 stringlen
= de_getu16le(pos
);
91 if(pos
+stringlen
> dp
->dpos
+dp
->dlen
) goto done
;
92 s
= ucstring_create(c
);
93 dbuf_read_to_ucstring_n(c
->infile
, pos
, stringlen
, DE_DBG_MAX_STRLEN
, s
,
94 0, d
->input_encoding
);
95 de_dbg(c
, "text: \"%s\"", ucstring_getpsz(s
));
102 static int wmf_handler_BITBLT_STRETCHBLT_DIBBITBLT(deark
*c
, lctx
*d
, struct decoder_params
*dp
)
106 unsigned int RasterOperation
;
111 has_src_bitmap
= (dp
->recsize_words
!= ((i64
)(dp
->recfunc
>>8)+3));
112 de_dbg(c
, "has src bitmap: %d", has_src_bitmap
);
113 if(!has_src_bitmap
) goto done
;
115 RasterOperation
= (unsigned int)de_getu32le(pos
);
116 de_dbg(c
, "RasterOperation: 0x%08x", RasterOperation
);
119 if(dp
->rectype
==0x23) { // STRETCHBLT
120 i64 SrcWidth
, SrcHeight
;
121 SrcHeight
= de_geti16le_p(&pos
);
122 SrcWidth
= de_geti16le_p(&pos
);
123 de_dbg(c
, "SrcWidth, SrcHeight: %d"DE_CHAR_TIMES
"%d",
124 (int)SrcWidth
, (int)SrcHeight
);
127 YSrc
= de_geti16le_p(&pos
);
128 XSrc
= de_geti16le_p(&pos
);
129 de_dbg(c
, "XSrc, YSrc: (%d, %d)", (int)XSrc
, (int)YSrc
);
131 Height
= de_geti16le_p(&pos
);
132 Width
= de_geti16le_p(&pos
);
133 de_dbg_dimensions(c
, Width
, Height
);
135 YDest
= de_geti16le_p(&pos
);
136 XDest
= de_geti16le_p(&pos
);
137 de_dbg(c
, "XDest, YDest: (%d, %d)", (int)XDest
, (int)YDest
);
139 // TODO: Bitmap16 object (if BITBLT or STRETCHBLT)
140 if(dp
->rectype
==0x40) { // DIBBITBLT
141 i64 dib_pos
, dib_len
;
143 // TODO: Merge this with the DIBSTRETCHBLT, STRETCHDIB code.
145 dib_len
= dp
->dpos
+ dp
->dlen
- dib_pos
;
147 if(dib_len
<12) goto done
;
148 de_dbg(c
, "DIB at %d, size=%d", (int)dib_pos
, (int)dib_len
);
151 de_run_module_by_id_on_slice(c
, "dib", NULL
, c
->infile
, dib_pos
, dib_len
);
152 de_dbg_indent(c
, -1);
159 static const struct escape_info escape_info_arr
[] = {
160 { 0x0001, "NEWFRAME", NULL
},
161 { 0x0002, "ABORTDOC", NULL
},
162 { 0x0003, "NEXTBAND", NULL
},
163 { 0x0004, "SETCOLORTABLE", NULL
},
164 { 0x0005, "GETCOLORTABLE", NULL
},
165 { 0x0006, "FLUSHOUT", NULL
},
166 { 0x0007, "DRAFTMODE", NULL
},
167 { 0x0008, "QUERYESCSUPPORT", NULL
},
168 { 0x0009, "SETABORTPROC", NULL
},
169 { 0x000a, "STARTDOC", NULL
},
170 { 0x000b, "ENDDOC", NULL
},
171 { 0x000c, "GETPHYSPAGESIZE", NULL
},
172 { 0x000d, "GETPRINTINGOFFSET", NULL
},
173 { 0x000e, "GETSCALINGFACTOR", NULL
},
174 { 0x000f, "MFCOMMENT", NULL
}, // a.k.a. META_ESCAPE_ENHANCED_METAFILE
175 { 0x0010, "SETPENWIDTH", NULL
},
176 { 0x0011, "SETCOPYCOUNT", NULL
},
177 { 0x0012, "SETPAPERSOURCE", NULL
},
178 { 0x0013, "PASSTHROUGH", NULL
},
179 { 0x0014, "GETTECHNOLOGY", NULL
},
180 { 0x0015, "SETLINECAP", NULL
},
181 { 0x0016, "SETLINEJOIN", NULL
},
182 { 0x0017, "SETMITERLIMIT", NULL
},
183 { 0x0018, "BANDINFO", NULL
},
184 { 0x0019, "DRAWPATTERNRECT", NULL
},
185 { 0x001a, "GETVECTORPENSIZE", NULL
},
186 { 0x001b, "GETVECTORBRUSHSIZE", NULL
},
187 { 0x001c, "ENABLEDUPLEX", NULL
},
188 { 0x001d, "GETSETPAPERBINS", NULL
},
189 { 0x001e, "GETSETPRINTORIENT", NULL
},
190 { 0x001f, "ENUMPAPERBINS", NULL
},
191 { 0x0020, "SETDIBSCALING", NULL
},
192 { 0x0021, "EPSPRINTING", NULL
},
193 { 0x0022, "ENUMPAPERMETRICS", NULL
},
194 { 0x0023, "GETSETPAPERMETRICS", NULL
},
195 { 0x0025, "POSTSCRIPT_DATA", NULL
},
196 { 0x0026, "POSTSCRIPT_IGNORE", NULL
},
197 { 0x002a, "GETDEVICEUNITS", NULL
},
198 { 0x0100, "GETEXTENDEDTEXTMETRICS", NULL
},
199 { 0x0102, "GETPAIRKERNTABLE", NULL
},
200 { 0x0200, "EXTTEXTOUT", NULL
},
201 { 0x0201, "GETFACENAME", NULL
},
202 { 0x0202, "DOWNLOADFACE", NULL
},
203 { 0x0801, "METAFILE_DRIVER", NULL
},
204 { 0x0c01, "QUERYDIBSUPPORT", NULL
},
205 { 0x1000, "BEGIN_PATH", NULL
},
206 { 0x1001, "CLIP_TO_PATH", NULL
},
207 { 0x1002, "END_PATH", NULL
},
208 { 0x100e, "OPEN_CHANNEL", NULL
},
209 { 0x100f, "DOWNLOADHEADER", NULL
},
210 { 0x1010, "CLOSE_CHANNEL", NULL
},
211 { 0x1013, "POSTSCRIPT_PASSTHROUGH", NULL
},
212 { 0x1014, "ENCAPSULATED_POSTSCRIPT", NULL
},
213 { 0x1015, "POSTSCRIPT_IDENTIFY", NULL
},
214 { 0x1016, "POSTSCRIPT_INJECTION", NULL
},
215 { 0x1017, "CHECKJPEGFORMAT", NULL
},
216 { 0x1018, "CHECKPNGFORMAT", NULL
},
217 { 0x1019, "GET_PS_FEATURESETTING", NULL
},
218 { 0x101a, "MXDC_ESCAPE", NULL
},
219 { 0x11d8, "SPCLPASSTHROUGH2", NULL
}
222 static void do_ESCAPE_MFCOMMENT_EMF(deark
*c
, lctx
*d
, struct decoder_params
*dp
,
223 i64 pos1
, i64 bytecount
)
226 i64 endpos
= dp
->dpos
+ dp
->dlen
;
227 unsigned int CommentId
;
229 i64 CommentRecordCount
, CurrentRecordSize
;
230 i64 RemainingBytes
, EnhancedMetafileDataSize
;
233 de_dbg(c
, "[bad/unsupported embedded EMF data (too short)]");
237 CommentId
= (unsigned int)de_getu32le_p(&pos
);
238 de_dbg(c
, "CommentIdentifier: 0x%08x", CommentId
);
239 if(CommentId
!=0x43464d57U
) goto done
;
241 n
= de_getu32le_p(&pos
);
242 de_dbg(c
, "CommentType: 0x%08x", (unsigned int)n
);
244 de_dbg(c
, "[bad/unsupported embedded EMF data (unsupported CommentType)]");
248 n
= de_getu32le_p(&pos
);
249 de_dbg(c
, "Version: 0x%08x", (unsigned int)n
);
250 n
= de_getu16le_p(&pos
);
251 de_dbg(c
, "CheckSum (reported): 0x%04x", (unsigned int)n
);
252 n
= de_getu32le_p(&pos
);
253 de_dbg(c
, "Flags: 0x%08x", (unsigned int)n
);
254 CommentRecordCount
= de_getu32le_p(&pos
);
255 de_dbg(c
, "CommentRecordCount: %d", (int)CommentRecordCount
);
256 CurrentRecordSize
= de_getu32le_p(&pos
);
257 de_dbg(c
, "CurrentRecordSize: %d", (int)CurrentRecordSize
);
258 RemainingBytes
= de_getu32le_p(&pos
);
259 de_dbg(c
, "RemainingBytes: %d", (int)RemainingBytes
);
261 // The spec says that the ByteCount field must be 34 +
262 // EnhancedMetafileDataSize, but that doesn't make sense to me.
263 // Maybe it was supposed to be 34 + CurrentRecordSize?
264 EnhancedMetafileDataSize
= de_getu32le_p(&pos
);
265 de_dbg(c
, "EnhancedMetafileDataSize: %d", (int)EnhancedMetafileDataSize
);
267 if(pos
+CurrentRecordSize
>endpos
) goto done
;
268 de_dbg(c
, "embedded EMF data at %d, len=%d", (int)pos
, (int)CurrentRecordSize
);
270 if(!d
->embedded_emf
&& (CurrentRecordSize
+RemainingBytes
==EnhancedMetafileDataSize
)) {
271 // Looks like the first record
272 d
->embedded_emf
= dbuf_create_output_file(c
, "emf", NULL
, 0);
275 if(d
->embedded_emf
) {
276 dbuf_copy(c
->infile
, pos
, CurrentRecordSize
, d
->embedded_emf
);
279 if(d
->embedded_emf
&& RemainingBytes
==0) {
280 // Looks like the last record
281 dbuf_close(d
->embedded_emf
);
282 d
->embedded_emf
= NULL
;
289 static void do_ESCAPE_MFCOMMENT(deark
*c
, lctx
*d
, struct decoder_params
*dp
,
295 const char *commenttype_name
= "?";
298 endpos
= dp
->dpos
+ dp
->dlen
;
299 pos
= dp
->dpos
+4; // Skip over EscapeFunction & ByteCount.
300 if(pos
+bytecount
> endpos
) goto done
;
303 sig
= (unsigned int)de_getu32le(pos
);
304 if(sig
==0x43464d57U
) {
306 commenttype_name
= "META_ESCAPE_ENHANCED_METAFILE";
310 de_dbg(c
, "identified as: %s", commenttype_name
);
312 do_ESCAPE_MFCOMMENT_EMF(c
, d
, dp
, pos
, bytecount
);
315 de_dbg_hexdump(c
, c
->infile
, pos
, bytecount
, 256, NULL
, 0x1);
322 static int wmf_handler_ESCAPE(deark
*c
, lctx
*d
, struct decoder_params
*dp
)
326 const struct escape_info
*einfo
= NULL
;
330 escfn
= (u16
)de_getu16le(dp
->dpos
);
332 // Find the name, etc. of this record type
333 for(k
=0; k
<DE_ARRAYCOUNT(escape_info_arr
); k
++) {
334 if(escape_info_arr
[k
].escfn
== escfn
) {
335 einfo
= &escape_info_arr
[k
];
340 if(einfo
&& einfo
->name
)
345 de_dbg(c
, "escape function: 0x%04x (%s)", (unsigned int)escfn
, name
);
348 bytecount
= de_getu16le(dp
->dpos
+2);
349 de_dbg(c
, "bytecount: %d (offset %d + %d = %d)", (int)bytecount
,
350 (int)(dp
->dpos
+4), (int)bytecount
, (int)(dp
->dpos
+4+bytecount
));
353 if(4+bytecount
> dp
->dlen
) {
358 do_ESCAPE_MFCOMMENT(c
, d
, dp
, bytecount
);
365 static int wmf_handler_EXTTEXTOUT(deark
*c
, lctx
*d
, struct decoder_params
*dp
)
369 de_ucstring
*s
= NULL
;
374 stringlen
= de_getu16le(pos
);
377 fwOpts
= (u32
)de_getu16le(pos
);
380 if(fwOpts
& 0x0004) {
381 // My best guess is that this flag determines whether the
382 // Rectangle field exists. The specification says the field is
383 // optional, but AFAICT does not say how to tell whether it exists.
384 pos
+= 8; // Rectangle
387 if(pos
+stringlen
> dp
->dpos
+dp
->dlen
) goto done
;
388 s
= ucstring_create(c
);
389 dbuf_read_to_ucstring_n(c
->infile
, pos
, stringlen
, DE_DBG_MAX_STRLEN
, s
,
390 0, d
->input_encoding
);
391 de_dbg(c
, "text: \"%s\"", ucstring_getpsz(s
));
398 static int wmf_handler_DIBSTRETCHBLT_STRETCHDIB(deark
*c
, lctx
*d
, struct decoder_params
*dp
)
403 int has_src_bitmap
= 1;
405 if(dp
->rectype
==0x41) { // DIBSTRETCHBLT
406 has_src_bitmap
= (dp
->recsize_words
!= ((i64
)(dp
->recfunc
>>8)+3));
407 de_dbg(c
, "has src bitmap: %d", has_src_bitmap
);
410 if(!has_src_bitmap
) goto done
;
411 if(dp
->rectype
==0x41) // DIBSTRETCHBLT
416 if(dp
->recsize_bytes
< hdrsize
) goto done
;
417 dib_pos
= dp
->recpos
+ hdrsize
;
418 dib_len
= dp
->recsize_bytes
- hdrsize
;
419 if(dib_len
< 12) goto done
;
420 de_dbg(c
, "DIB at %d, size=%d", (int)dib_pos
, (int)dib_len
);
423 de_run_module_by_id_on_slice(c
, "dib", NULL
, c
->infile
, dib_pos
, dib_len
);
424 de_dbg_indent(c
, -1);
430 static int handler_SELECTOBJECT(deark
*c
, lctx
*d
, struct decoder_params
*dp
)
433 oi
= (unsigned int)de_getu16le(dp
->dpos
);
434 de_dbg(c
, "object index: %u", oi
);
438 static int handler_DELETEOBJECT(deark
*c
, lctx
*d
, struct decoder_params
*dp
)
441 oi
= (unsigned int)de_getu16le(dp
->dpos
);
442 de_dbg(c
, "object index: %u", oi
);
443 if(d
->object_table
&& oi
<d
->num_objects
) {
444 d
->object_table
[oi
] = 0; // Mark this index as available
449 static const char* get_brushstyle_name(unsigned int n
)
451 static const char *names
[7] = { "BS_SOLID", "BS_NULL", "BS_HATCHED", "BS_PATTERN",
452 NULL
, "BS_DIBPATTERN", "BS_DIBPATTERNPT"};
453 const char *name
= NULL
;
458 return name
?name
:"?";
461 static int handler_CREATEBRUSHINDIRECT(deark
*c
, lctx
*d
, struct decoder_params
*dp
)
466 if(dp
->dlen
<8) goto done
;
467 style
= (unsigned int)de_getu16le_p(&pos
);
468 de_dbg(c
, "style: 0x%04x (%s)", style
, get_brushstyle_name(style
));
470 if(style
==0x0 || style
==0x2) {
472 colorref
= (u32
)de_getu32le(pos
);
473 do_dbg_colorref(c
, d
, dp
, colorref
);
479 h
= (unsigned int)de_getu16le(pos
);
480 de_dbg(c
, "hatch: %u", h
);
487 static const char *get_penbasestyle_name(unsigned int n
)
489 static const char *names
[9] = { "PS_SOLID", "PS_DASH", "PS_DOT", "PS_DASHDOT",
490 "PS_DASHDOTDOT", "PS_NULL", "PS_INSIDEFRAME", "PS_USERSTYLE", "PS_ALTERNATE" };
491 const char *name
= NULL
;
496 return name
?name
:"?";
499 static int handler_CREATEPENINDIRECT(deark
*c
, lctx
*d
, struct decoder_params
*dp
)
505 unsigned int base_style
;
506 de_ucstring
*style_descr
= NULL
;
508 if(dp
->dlen
<10) goto done
;
509 style
= (unsigned int)de_getu16le_p(&pos
);
510 base_style
= style
&0x0f; // ?
511 style_descr
= ucstring_create(c
);
512 ucstring_append_flags_item(style_descr
, get_penbasestyle_name(base_style
));
513 if((style
&0x0f00)==0x0100) ucstring_append_flags_item(style_descr
, "PS_ENDCAP_SQUARE");
514 if((style
&0x0f00)==0x0200) ucstring_append_flags_item(style_descr
, "PS_ENDCAP_FLAG");
515 if((style
&0xf000)==0x1000) ucstring_append_flags_item(style_descr
, "PS_JOIN_BEVEL");
516 if((style
&0xf000)==0x2000) ucstring_append_flags_item(style_descr
, "PS_JOIN_MITER");
517 de_dbg(c
, "style: 0x%04x (%s)", style
, ucstring_getpsz(style_descr
));
519 if(base_style
!=0x5) {
520 width
= (unsigned int)de_getu32le(pos
);
521 width
&= 0x0000ffffU
;
522 de_dbg(c
, "width: %u", width
);
526 if(base_style
!=0x5) {
527 colorref
= (u32
)de_getu32le(pos
);
528 do_dbg_colorref(c
, d
, dp
, colorref
);
532 ucstring_destroy(style_descr
);
536 static int handler_CREATEFONTINDIRECT(deark
*c
, lctx
*d
, struct decoder_params
*dp
)
543 n
= de_geti16le_p(&pos
);
544 n2
= de_geti16le_p(&pos
);
545 de_dbg(c
, "height,width: %d,%d", (int)n
, (int)n2
);
547 b
= de_getbyte_p(&pos
);
548 de_dbg(c
, "charset: 0x%02x (%s)", (unsigned int)b
,
549 fmtutil_get_windows_charset_name(b
));
551 facename_size
= dp
->dlen
-18;
552 if(facename_size
>32) facename_size
=32;
553 if(facename_size
>=2) {
554 de_ucstring
*facename
= NULL
;
555 facename
= ucstring_create(c
);
556 dbuf_read_to_ucstring(c
->infile
, dp
->dpos
+18, facename_size
, facename
,
557 DE_CONVFLAG_STOP_AT_NUL
, DE_ENCODING_WINDOWS1252
);
558 de_dbg(c
, "facename: \"%s\"", ucstring_getpsz_d(facename
));
559 ucstring_destroy(facename
);
564 static int handler_FILLREGION(deark
*c
, lctx
*d
, struct decoder_params
*dp
)
569 oi
= (unsigned int)de_getu16le_p(&pos
);
570 de_dbg(c
, "region object index: %u", oi
);
571 oi
= (unsigned int)de_getu16le_p(&pos
);
572 de_dbg(c
, "brush object index: %u", oi
);
576 static const struct wmf_func_info wmf_func_info_arr
[] = {
577 { 0x00, 0, "EOF", NULL
},
578 { 0x01, 0, "SETBKCOLOR", handler_colorref
},
579 { 0x02, 0, "SETBKMODE", NULL
},
580 { 0x03, 0, "SETMAPMODE", NULL
},
581 { 0x04, 0, "SETROP2", NULL
},
582 { 0x05, 0, "SETRELABS", NULL
},
583 { 0x06, 0, "SETPOLYFILLMODE", NULL
},
584 { 0x07, 0, "SETSTRETCHBLTMODE", NULL
},
585 { 0x08, 0, "SETTEXTCHAREXTRA", NULL
},
586 { 0x09, 0, "SETTEXTCOLOR", handler_colorref
},
587 { 0x0a, 0, "SETTEXTJUSTIFICATION", NULL
},
588 { 0x0b, 0, "SETWINDOWORG", NULL
},
589 { 0x0c, 0, "SETWINDOWEXT", NULL
},
590 { 0x0d, 0, "SETVIEWPORTORG", NULL
},
591 { 0x0e, 0, "SETVIEWPORTEXT", NULL
},
592 { 0x0f, 0, "OFFSETWINDOWORG", NULL
},
593 { 0x10, 0, "SCALEWINDOWEXT", NULL
},
594 { 0x11, 0, "OFFSETVIEWPORTORG", NULL
},
595 { 0x12, 0, "SCALEVIEWPORTEXT", NULL
},
596 { 0x13, 0, "LINETO", NULL
},
597 { 0x14, 0, "MOVETO", NULL
},
598 { 0x15, 0, "EXCLUDECLIPRECT", NULL
},
599 { 0x16, 0, "INTERSECTCLIPRECT", NULL
},
600 { 0x17, 0, "ARC", NULL
},
601 { 0x18, 0, "ELLIPSE", NULL
},
602 { 0x19, 0, "FLOODFILL", NULL
},
603 { 0x1a, 0, "PIE", NULL
},
604 { 0x1b, 0, "RECTANGLE", NULL
},
605 { 0x1c, 0, "ROUNDRECT", NULL
},
606 { 0x1d, 0, "PATBLT", NULL
},
607 { 0x1e, 0, "SAVEDC", NULL
},
608 { 0x1f, 0, "SETPIXEL", NULL
},
609 { 0x20, 0, "OFFSETCLIPRGN", NULL
},
610 { 0x21, 0, "TEXTOUT", wmf_handler_TEXTOUT
},
611 { 0x22, 0, "BITBLT", wmf_handler_BITBLT_STRETCHBLT_DIBBITBLT
},
612 { 0x23, 0, "STRETCHBLT", wmf_handler_BITBLT_STRETCHBLT_DIBBITBLT
},
613 { 0x24, 0, "POLYGON", NULL
},
614 { 0x25, 0, "POLYLINE", NULL
},
615 { 0x26, 0, "ESCAPE", wmf_handler_ESCAPE
},
616 { 0x27, 0, "RESTOREDC", NULL
},
617 { 0x28, 0, "FILLREGION", handler_FILLREGION
},
618 { 0x29, 0, "FRAMEREGION", NULL
},
619 { 0x2a, 0, "INVERTREGION", NULL
},
620 { 0x2b, 0, "PAINTREGION", NULL
},
621 { 0x2c, 0, "SELECTCLIPREGION", handler_SELECTOBJECT
},
622 { 0x2d, 0, "SELECTOBJECT", handler_SELECTOBJECT
},
623 { 0x2e, 0, "SETTEXTALIGN", NULL
},
624 { 0x30, 0, "CHORD", NULL
},
625 { 0x31, 0, "SETMAPPERFLAGS", NULL
},
626 { 0x32, 0, "EXTTEXTOUT", wmf_handler_EXTTEXTOUT
},
627 { 0x33, 0, "SETDIBTODEV", NULL
},
628 { 0x34, 0, "SELECTPALETTE", handler_SELECTOBJECT
},
629 { 0x35, 0, "REALIZEPALETTE", NULL
},
630 { 0x36, 0, "ANIMATEPALETTE", NULL
},
631 { 0x37, 0, "SETPALENTRIES", NULL
},
632 { 0x38, 0, "POLYPOLYGON", NULL
},
633 { 0x39, 0, "RESIZEPALETTE", NULL
},
634 { 0x40, 0, "DIBBITBLT", wmf_handler_BITBLT_STRETCHBLT_DIBBITBLT
},
635 { 0x41, 0, "DIBSTRETCHBLT", wmf_handler_DIBSTRETCHBLT_STRETCHDIB
},
636 { 0x42, 1, "DIBCREATEPATTERNBRUSH", NULL
},
637 { 0x43, 0, "STRETCHDIB", wmf_handler_DIBSTRETCHBLT_STRETCHDIB
},
638 { 0x48, 0, "EXTFLOODFILL", NULL
},
639 { 0x49, 0, "SETLAYOUT", NULL
},
640 { 0xf0, 0, "DELETEOBJECT", handler_DELETEOBJECT
},
641 { 0xf7, 1, "CREATEPALETTE", NULL
},
642 { 0xf9, 1, "CREATEPATTERNBRUSH", NULL
},
643 { 0xfa, 1, "CREATEPENINDIRECT", handler_CREATEPENINDIRECT
},
644 { 0xfb, 1, "CREATEFONTINDIRECT", handler_CREATEFONTINDIRECT
},
645 { 0xfc, 1, "CREATEBRUSHINDIRECT", handler_CREATEBRUSHINDIRECT
},
646 { 0xff, 1, "CREATEREGION", NULL
}
649 static void do_read_aldus_header(deark
*c
, lctx
*d
)
651 i64 left
, top
, right
, bottom
;
654 de_dbg(c
, "Aldus Placeable Metafile header at 0");
656 left
= de_geti16le(6);
657 top
= de_geti16le(8);
658 right
= de_geti16le(10);
659 bottom
= de_geti16le(12);
660 de_dbg(c
, "location: (%d,%d) - (%d,%d)", (int)left
, (int)top
,
661 (int)right
, (int)bottom
);
662 units_per_inch
= de_getu16le(14);
663 de_dbg(c
, "metafile units per inch: %d", (int)units_per_inch
);
664 de_dbg_indent(c
, -1);
667 static int do_read_wmf_header(deark
*c
, lctx
*d
, i64 pos
)
669 i64 hsize_words
, maxrecsize_words
, filesize_words
;
672 de_dbg(c
, "WMF header at %d", (int)pos
);
675 d
->wmf_file_type
= de_getu16le(pos
);
676 de_dbg(c
, "file type: %d", (int)d
->wmf_file_type
);
677 if(d
->wmf_file_type
!=1 && d
->wmf_file_type
!=2) {
678 de_err(c
, "Invalid or unsupported WMF file type (%d)", (int)d
->wmf_file_type
);
681 hsize_words
= de_getu16le(pos
+2);
682 de_dbg(c
, "header size: %d bytes", (int)(hsize_words
*2));
683 if(hsize_words
!= 9) {
684 de_err(c
, "Incorrect WMF header size (expected 9, is %d)", (int)hsize_words
);
687 d
->wmf_windows_version
= de_getu16le(pos
+4);
688 de_dbg(c
, "Windows version: %d.%d", (int)((d
->wmf_windows_version
&0xff00)>>8),
689 (int)(d
->wmf_windows_version
&0x00ff));
690 filesize_words
= de_getu32le(pos
+6);
691 de_dbg(c
, "reported file size: %d bytes", (int)(filesize_words
*2));
693 d
->num_objects
= (unsigned int)de_getu16le(pos
+10);
694 de_dbg(c
, "number of objects: %u", d
->num_objects
);
695 if(d
->object_table
) de_free(c
, d
->object_table
);
696 // d->num_objects is untrusted, but it can only be from 0 to 65535.
697 d
->object_table
= de_malloc(c
, d
->num_objects
);
699 maxrecsize_words
= de_getu32le(pos
+12);
700 de_dbg(c
, "max record size: %d bytes", (int)(maxrecsize_words
*2));
703 de_dbg_indent(c
, -1);
707 static const struct wmf_func_info
*find_wmf_func_info(u16 recfunc
)
710 u8 rectype_wanted
= (u8
)(recfunc
&0xff);
712 for(i
=0; i
<DE_ARRAYCOUNT(wmf_func_info_arr
); i
++) {
713 if(wmf_func_info_arr
[i
].rectype
== rectype_wanted
) {
714 return &wmf_func_info_arr
[i
];
720 static void on_create_object(deark
*c
, lctx
*d
, struct decoder_params
*dp
)
724 if(!d
->object_table
) return;
725 // The CREATE* opcodes assign an object index to the new object.
726 // Specifically, the first available index in the object table.
727 // The encoder and decoder must be very careful to use exactly the same
728 // algorithm for index assignment, or they could get out of sync.
729 for(k
=0; k
<d
->num_objects
; k
++) {
730 if(d
->object_table
[k
]==0) {
731 d
->object_table
[k
] = 1; // Mark this index as used
732 de_dbg(c
, "assigned object index: %u", k
);
736 de_warn(c
, "Out of space in object table");
740 // Returns 0 if EOF record was found.
741 static int do_wmf_record(deark
*c
, lctx
*d
, i64 recnum
, i64 recpos
,
744 const struct wmf_func_info
*fnci
;
745 struct decoder_params dp
;
747 de_zeromem(&dp
, sizeof(struct decoder_params
));
749 dp
.recsize_words
= recsize_bytes
*2;
750 dp
.recsize_bytes
= recsize_bytes
;
751 dp
.dpos
= recpos
+ 6;
752 dp
.dlen
= recsize_bytes
- 6;
754 dp
.recfunc
= (u16
)de_getu16le(recpos
+4);
755 dp
.rectype
= (u8
)(dp
.recfunc
&0xff);
757 fnci
= find_wmf_func_info(dp
.recfunc
);
759 de_dbg(c
, "record #%d at %d, func=0x%04x (%s), dpos=%d, dlen=%d", (int)recnum
,
760 (int)recpos
, (unsigned int)dp
.recfunc
,
761 fnci
? fnci
->name
: "?",
762 (int)dp
.dpos
, (int)dp
.dlen
);
765 if(fnci
&& (fnci
->flags
&0x1)) {
766 on_create_object(c
, d
, &dp
);
768 if(fnci
&& fnci
->fn
) {
771 de_dbg_indent(c
, -1);
773 return (dp
.rectype
==0x00)?0:1;
776 static void do_wmf_record_list(deark
*c
, lctx
*d
, i64 pos
)
779 i64 recsize_words
, recsize_bytes
;
782 de_dbg(c
, "record list at %d", (int)pos
);
788 if(recpos
>= c
->infile
->len
) break; // Unexpected EOF
790 recsize_words
= de_getu32le(recpos
);
791 recsize_bytes
= recsize_words
*2;
792 if(recpos
+ recsize_bytes
> c
->infile
->len
) break; // Unexpected EOF
793 if(recsize_bytes
< 6) break; // Invalid size
795 if(!do_wmf_record(c
, d
, count
, recpos
, recsize_bytes
)) {
799 pos
+= recsize_bytes
;
803 de_dbg_indent(c
, -1);
806 static void de_run_wmf(deark
*c
, de_module_params
*mparams
)
811 d
= de_malloc(c
, sizeof(lctx
));
813 d
->input_encoding
= de_get_input_encoding(c
, NULL
, DE_ENCODING_WINDOWS1252
);
815 if(!dbuf_memcmp(c
->infile
, 0, "\xd7\xcd\xc6\x9a", 4)) {
816 d
->has_aldus_header
= 1;
817 de_declare_fmt(c
, "WMF (placeable)");
820 de_declare_fmt(c
, "WMF (non-placeable)");
823 if(d
->has_aldus_header
) {
824 do_read_aldus_header(c
, d
);
828 if(!do_read_wmf_header(c
, d
, pos
)) {
833 do_wmf_record_list(c
, d
, pos
);
837 if(d
->embedded_emf
) dbuf_close(d
->embedded_emf
);
838 de_free(c
, d
->object_table
);
843 static int de_identify_wmf(deark
*c
)
849 if(!de_memcmp(buf
, "\xd7\xcd\xc6\x9a", 4))
852 if(de_input_file_has_ext(c
, "wmf")) {
854 ftype
= de_getu16le_direct(&buf
[0]);
855 hsize
= de_getu16le_direct(&buf
[2]);
856 if(hsize
==9 && (ftype
==1 || ftype
==2)) {
864 void de_module_wmf(deark
*c
, struct deark_module_info
*mi
)
867 mi
->desc
= "Windows Metafile";
868 mi
->desc2
= "extract bitmaps only";
869 mi
->run_fn
= de_run_wmf
;
870 mi
->identify_fn
= de_identify_wmf
;