1 /*
2  * Copyright (C) 2007 Novell, Inc (http://www.novell.com)
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
5  * and associated documentation files (the "Software"), to deal in the Software without restriction,
6  * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
7  * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
8  * subject to the following conditions:
9  *
10  * The above copyright notice and this permission notice shall be included in all copies or substantial
11  * portions of the Software.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
14  * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
15  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
16  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
17  * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
18  *
19  * Authors:
20  *	Sebastien Pouliot  <sebastien@ximian.com>
21  */
22 
23 #include "metafile-private.h"
24 #include "solidbrush-private.h"
25 #include "general-private.h"
26 #include "graphics.h"
27 #include "graphics-path-private.h"
28 #include "hatchbrush-private.h"
29 #include "pen.h"
30 
31 //#define DEBUG_METAFILE
32 
33 /* http://wvware.sourceforge.net/caolan/SaveDC.html */
34 GpStatus
gdip_metafile_SaveDC(MetafilePlayContext * context)35 gdip_metafile_SaveDC (MetafilePlayContext *context)
36 {
37 #ifdef DEBUG_METAFILE
38 	printf ("SaveDC");
39 #endif
40 	/* TODO */
41 	return Ok;
42 }
43 
44 /* http://wvware.sourceforge.net/caolan/SetBkMode.html */
45 GpStatus
gdip_metafile_SetBkMode(MetafilePlayContext * context,DWORD bkMode)46 gdip_metafile_SetBkMode (MetafilePlayContext *context, DWORD bkMode)
47 {
48 #ifdef DEBUG_METAFILE
49 	printf ("SetBkMode %d", bkMode);
50 #endif
51 	/* TODO - currently unused elsewhere */
52 	context->bk_mode = bkMode;
53 	return Ok;
54 }
55 
56 /* http://wvware.sourceforge.net/caolan/SetMapMode.html */
57 GpStatus
gdip_metafile_SetMapMode(MetafilePlayContext * context,DWORD mode)58 gdip_metafile_SetMapMode (MetafilePlayContext *context, DWORD mode)
59 {
60 	GpStatus status;
61 	float scale;
62 #ifdef DEBUG_METAFILE
63 	printf ("SetMapMode %d", mode);
64 #endif
65 	context->map_mode = mode;
66 
67 	switch (mode) {
68 	case MM_HIENGLISH:
69 		/* 1 logical unit == 0.001 inch */
70 		scale = gdip_get_display_dpi () * 0.001;
71 		break;
72 	case MM_LOENGLISH:
73 		/* 1 logical unit == 0.01 inch */
74 		scale = gdip_get_display_dpi () * 0.01;
75 		break;
76 	case MM_HIMETRIC:
77 		/* 1 logical unit == 0.01 mm */
78 		scale = gdip_get_display_dpi () / (MM_PER_INCH * 100);
79 		break;
80 	case MM_LOMETRIC:
81 		/* 1 logical unit == 0.1 mm */
82 		scale = gdip_get_display_dpi () / (MM_PER_INCH * 10);
83 		break;
84 	case MM_TWIPS:
85 		/* 1 logical point == 1/1440 inch (1/20 of a "old" printer point ;-) */
86 		scale = gdip_get_display_dpi () / 1440;
87 		break;
88 	case MM_TEXT:
89 	default:
90 		/* 1 logical unit == 1 pixel */
91 		scale = 1.0f;
92 		context->map_mode = MM_TEXT;
93 		break;
94 	case MM_ISOTROPIC:
95 	case MM_ANISOTROPIC:
96 		/* SetWindowExt will calculate the correct ratio */
97 		return Ok;
98 	}
99 
100 	/* this isn't cumulative (and we get a lot of "junk" calls) */
101 	GdipSetWorldTransform (context->graphics, &context->matrix);
102 	status = GdipScaleWorldTransform (context->graphics, scale, scale, MatrixOrderPrepend);
103 #ifdef DEBUG_METAFILE_2
104 	printf ("\n\tGdipScaleWorldTransform sx %g, sy %g (status %d)", scale, scale, status);
105 #endif
106 	return status;
107 }
108 
109 /* http://wvware.sourceforge.net/caolan/SetROP2.html */
110 GpStatus
gdip_metafile_SetROP2(MetafilePlayContext * context,DWORD rop)111 gdip_metafile_SetROP2 (MetafilePlayContext *context, DWORD rop)
112 {
113 #ifdef DEBUG_METAFILE
114 	printf ("SetROP2 %d", rop);
115 #endif
116 	/* TODO */
117 	return Ok;
118 }
119 
120 /* http://www.ousob.com/ng/win2api/ng30859.php */
121 GpStatus
gdip_metafile_SetRelabs(MetafilePlayContext * context,DWORD mode)122 gdip_metafile_SetRelabs (MetafilePlayContext *context, DWORD mode)
123 {
124 #ifdef DEBUG_METAFILE
125 	printf ("SetRelabs %d", mode);
126 #endif
127 	/* TODO */
128 	return Ok;
129 }
130 
131 /* http://wvware.sourceforge.net/caolan/SetPolyFillMode.html */
132 GpStatus
gdip_metafile_SetPolyFillMode(MetafilePlayContext * context,DWORD fillmode)133 gdip_metafile_SetPolyFillMode (MetafilePlayContext *context, DWORD fillmode)
134 {
135 #ifdef DEBUG_METAFILE
136 	printf ("SetPolyFillMode %d", fillmode);
137 #endif
138 	switch (fillmode) {
139 	case WINDING:
140 		context->fill_mode = FillModeWinding;
141 		break;
142 	default:
143 		g_warning ("Unknown fillmode %d, assuming ALTERNATE", fillmode);
144 		/* fall through */
145 	case ALTERNATE:
146 		context->fill_mode = FillModeAlternate;
147 		break;
148 	}
149 	return Ok;
150 }
151 
152 /* http://wvware.sourceforge.net/caolan/SetStretchBltMode.html */
153 GpStatus
gdip_metafile_SetStretchBltMode(MetafilePlayContext * context,int iStretchMode)154 gdip_metafile_SetStretchBltMode (MetafilePlayContext *context, int iStretchMode)
155 {
156 #ifdef DEBUG_METAFILE
157 	printf ("SetStretchBltMode %d", iStretchMode);
158 #endif
159 	/* TODO */
160 	return Ok;
161 }
162 
163 /* http://wvware.sourceforge.net/caolan/RestoreDC.html */
164 GpStatus
gdip_metafile_RestoreDC(MetafilePlayContext * context)165 gdip_metafile_RestoreDC (MetafilePlayContext *context)
166 {
167 #ifdef DEBUG_METAFILE
168 	printf ("RestoreDC");
169 #endif
170 	/* TODO */
171 	return Ok;
172 }
173 
174 /* http://wvware.sourceforge.net/caolan/SelectObject.html */
175 GpStatus
gdip_metafile_SelectObject(MetafilePlayContext * context,DWORD slot)176 gdip_metafile_SelectObject (MetafilePlayContext *context, DWORD slot)
177 {
178 	// EMF have some stock objects that do not requires creation
179 	if (slot & ENHMETA_STOCK_OBJECT) {
180 #ifdef DEBUG_METAFILE
181 		printf ("SelectObject stock #%X", slot);
182 #endif
183 		switch (slot - ENHMETA_STOCK_OBJECT) {
184 		case WHITE_BRUSH:
185 		case LTGRAY_BRUSH:
186 		case GRAY_BRUSH:
187 		case DKGRAY_BRUSH:
188 		case BLACK_BRUSH:
189 		case NULL_BRUSH:
190 			context->selected_brush = slot;
191 			break;
192 		case WHITE_PEN:
193 		case BLACK_PEN:
194 		case NULL_PEN:
195 			context->selected_pen = slot;
196 			break;
197 		case OEM_FIXED_FONT:
198 		case ANSI_FIXED_FONT:
199 		case ANSI_VAR_FONT:
200 		case SYSTEM_FONT:
201 		case DEVICE_DEFAULT_FONT:
202 		case SYSTEM_FIXED_FONT:
203 			context->selected_font = slot;
204 			break;
205 		case DEFAULT_PALETTE:
206 			context->selected_palette = slot;
207 			break;
208 		default:
209 			return InvalidParameter;
210 		}
211 		return Ok;
212 	}
213 
214 #ifdef DEBUG_METAFILE
215 	printf ("SelectObject %d (out of %d slots)", slot, context->objects_count);
216 #endif
217 	if (slot >= context->objects_count) {
218 		g_warning ("SelectObject %d, invalid slot number.", slot);
219 		return InvalidParameter;
220 	}
221 
222 	/* FIXME - what's first ? the created object or what's in the slot ? */
223 	switch (context->created.type) {
224 	case METAOBJECT_TYPE_EMPTY:
225 		/* if nothing is "created" (and not yet selected into a slot) then we "reselect" the object */
226 		switch (context->objects [slot].type) {
227 		case METAOBJECT_TYPE_EMPTY:
228 			g_warning ("SelectObject %d, no created object, slot empty.", slot);
229 			break;
230 		case METAOBJECT_TYPE_PEN:
231 			context->selected_pen = slot;
232 			break;
233 		case METAOBJECT_TYPE_BRUSH:
234 			context->selected_brush = slot;
235 			break;
236 		}
237 		return Ok;
238 	case METAOBJECT_TYPE_PEN:
239 		context->selected_pen = slot;
240 		break;
241 	case METAOBJECT_TYPE_BRUSH:
242 		context->selected_brush = slot;
243 		break;
244 	}
245 
246 	context->objects [slot].type = context->created.type;
247 	context->objects [slot].ptr = context->created.ptr;
248 
249 	context->created.type = METAOBJECT_TYPE_EMPTY;
250 	context->created.ptr = NULL;
251 	return Ok;
252 }
253 
254 GpStatus
gdip_metafile_ModifyWorldTransform(MetafilePlayContext * context,XFORM * lpXform,DWORD iMode)255 gdip_metafile_ModifyWorldTransform (MetafilePlayContext *context, XFORM *lpXform, DWORD iMode)
256 {
257 	GpMatrix matrix;
258 	GpMatrixOrder order;
259 
260 	switch (iMode) {
261 	case MWT_IDENTITY:
262 		/* This is a reset and it ignores lpXform in this case */
263 		/* we can't use GdipResetWorldTransform as we still want the original parameters */
264 		return GdipSetWorldTransform (context->graphics, &context->matrix);
265 	case MWT_LEFTMULTIPLY:
266 		order = MatrixOrderPrepend;
267 		break;
268 	case MWT_RIGHTMULTIPLY:
269 		order = MatrixOrderPrepend;
270 		break;
271 	default:
272 		return InvalidParameter;
273 	}
274 
275 	if (!lpXform)
276 		return InvalidParameter;
277 	matrix.xx = lpXform->eM11;
278 	matrix.yx = lpXform->eM12;
279 	matrix.xy = lpXform->eM21;
280 	matrix.yy = lpXform->eM22;
281 	matrix.x0 = lpXform->eDx;
282 	matrix.y0 = lpXform->eDy;
283 	return GdipMultiplyWorldTransform (context->graphics, &matrix, order);
284 }
285 
286 
287 /* http://wvware.sourceforge.net/caolan/SetTextAlign.html */
288 GpStatus
gdip_metafile_SetTextAlign(MetafilePlayContext * context,DWORD textalign)289 gdip_metafile_SetTextAlign (MetafilePlayContext *context, DWORD textalign)
290 {
291 #ifdef DEBUG_METAFILE
292 	printf ("SetTextAlign %d", textalign);
293 #endif
294 	/* TODO */
295 	return Ok;
296 }
297 
298 /* http://wvware.sourceforge.net/caolan/DeleteObject.html */
299 GpStatus
gdip_metafile_DeleteObject(MetafilePlayContext * context,DWORD slot)300 gdip_metafile_DeleteObject (MetafilePlayContext *context, DWORD slot)
301 {
302 	GpStatus status = Ok;
303 	MetaObject *obj;
304 
305 	if (slot >= context->objects_count) {
306 		g_warning ("DeleteObject failure");
307 		return InvalidParameter;
308 	}
309 
310 	obj = &context->objects [slot];
311 	switch (obj->type) {
312 	case METAOBJECT_TYPE_PEN:
313 		status = GdipDeletePen ((GpPen*)obj->ptr);
314 		break;
315 	case METAOBJECT_TYPE_BRUSH:
316 		status = GdipDeleteBrush ((GpBrush*)obj->ptr);
317 		break;
318 	case METAOBJECT_TYPE_EMPTY:
319 		break;
320 	}
321 
322 #ifdef DEBUG_METAFILE
323 	printf ("DeleteObject %d (type %d, ptr %p, status %d)", slot, obj->type, obj->ptr, status);
324 #endif
325 	obj->type = METAOBJECT_TYPE_EMPTY;
326 	obj->ptr = NULL;
327 	return status;
328 }
329 
330 /* http://wvware.sourceforge.net/caolan/SetBkColor.html */
331 GpStatus
gdip_metafile_SetBkColor(MetafilePlayContext * context,DWORD color)332 gdip_metafile_SetBkColor (MetafilePlayContext *context, DWORD color)
333 {
334 #ifdef DEBUG_METAFILE
335 	printf ("SetBkColor %X", color);
336 #endif
337 	/* TODO - currently unused elsewhere */
338 	context->bk_color = color;
339 	return Ok;
340 }
341 
342 /* http://wvware.sourceforge.net/caolan/SetWindowOrg.html */
343 GpStatus
gdip_metafile_SetWindowOrg(MetafilePlayContext * context,int x,int y)344 gdip_metafile_SetWindowOrg (MetafilePlayContext *context, int x, int y)
345 {
346 #ifdef DEBUG_METAFILE
347 	printf ("SetWindowOrg X: %d, Y: %d", x, y);
348 #endif
349 	/* TODO */
350 	return Ok;
351 }
352 
353 /* http://wvware.sourceforge.net/caolan/SetWindowExt.html */
354 GpStatus
gdip_metafile_SetWindowExt(MetafilePlayContext * context,int height,int width)355 gdip_metafile_SetWindowExt (MetafilePlayContext *context, int height, int width)
356 {
357 	GpStatus status;
358 	float sx, sy;
359 
360 #ifdef DEBUG_METAFILE
361 	printf ("SetWindowExt height %d, width %d", height, width);
362 #endif
363 	switch (context->map_mode) {
364 	case MM_ISOTROPIC:
365 		sx = (float)context->metafile->metafile_header.Width / width;
366 		sy = (float)context->metafile->metafile_header.Height / height;
367 		/* keeps ratio to 1:1 */
368 		if (sx < sy)
369 			sy = sx;
370 		break;
371 	case MM_ANISOTROPIC:
372 		sx = (float)context->metafile->metafile_header.Width / width;
373 		sy = (float)context->metafile->metafile_header.Height / height;
374 		break;
375 	default:
376 		/* most cases are handled by SetMapMode */
377 		return Ok;
378 	}
379 
380 	/* this isn't cumulative (and we get a lot of "junk" calls) */
381 	GdipSetWorldTransform (context->graphics, &context->matrix);
382 	status = GdipScaleWorldTransform (context->graphics, sx, sy, MatrixOrderPrepend);
383 #ifdef DEBUG_METAFILE_2
384 	printf ("\n\tGdipScaleWorldTransform sx %g, sy %g (status %d)", sx, sy, status);
385 #endif
386 	return status;
387 }
388 
389 /* http://wvware.sourceforge.net/caolan/LineTo.html */
390 GpStatus
gdip_metafile_LineTo(MetafilePlayContext * context,int x,int y)391 gdip_metafile_LineTo (MetafilePlayContext *context, int x, int y)
392 {
393 	GpStatus status;
394 #ifdef DEBUG_METAFILE
395 	printf ("LineTo %s%d, %d", context->use_path ? "Path " : " ", x, y);
396 #endif
397 	if (context->use_path) {
398 		status = GdipAddPathLine (context->path, context->current_x, context->current_y, x, y);
399 	} else {
400 		GpPen *pen = gdip_metafile_GetSelectedPen (context);
401 		status = GdipDrawLine (context->graphics, pen, context->current_x, context->current_y, x, y);
402 	}
403 	context->current_x = x;
404 	context->current_y = y;
405 	return status;
406 }
407 
408 /* http://wvware.sourceforge.net/caolan/MoveTo.html */
409 GpStatus
gdip_metafile_MoveTo(MetafilePlayContext * context,int x,int y)410 gdip_metafile_MoveTo (MetafilePlayContext *context, int x, int y)
411 {
412 #ifdef DEBUG_METAFILE
413 	printf ("MoveTo X: %d, Y: %d", x, y);
414 #endif
415 	/* seems to always be called before Arc but, according to documentation,
416 	   Arc doesn't depend on the current cursor position */
417 	context->current_x = x;
418 	context->current_y = y;
419 	return Ok;
420 }
421 
422 GpStatus
gdip_metafile_SetMiterLimit(MetafilePlayContext * context,float eNewLimit,float * peOldLimit)423 gdip_metafile_SetMiterLimit (MetafilePlayContext *context, float eNewLimit, float *peOldLimit)
424 {
425 	if (peOldLimit)
426 		*peOldLimit = context->miter_limit;
427 	/* GDI+ keeps the miter limit with it's pen, not the context, definition */
428 	context->miter_limit = eNewLimit;
429 	return Ok;
430 }
431 
432 /* http://wvware.sourceforge.net/caolan/CreatePenIndirect.html */
433 GpStatus
gdip_metafile_CreatePenIndirect(MetafilePlayContext * context,DWORD style,DWORD width,DWORD color)434 gdip_metafile_CreatePenIndirect (MetafilePlayContext *context, DWORD style, DWORD width, DWORD color)
435 {
436 	LOGBRUSH brush;
437 	brush.lbStyle = 0;
438 	brush.lbColor = color;
439 	brush.lbHatch = 0;
440 	return gdip_metafile_ExtCreatePen (context, style, width, &brush, 0, NULL);
441 }
442 
443 GpStatus
gdip_metafile_ExtCreatePen(MetafilePlayContext * context,DWORD dwPenStyle,DWORD dwWidth,CONST LOGBRUSH * lplb,DWORD dwStyleCount,CONST DWORD * lpStyle)444 gdip_metafile_ExtCreatePen (MetafilePlayContext *context, DWORD dwPenStyle, DWORD dwWidth, CONST LOGBRUSH *lplb,
445 	DWORD dwStyleCount, CONST DWORD *lpStyle)
446 {
447 	/* TODO - there's more cases to consider */
448 	GpStatus status;
449 	GpPen *pen = NULL;
450 	GpLineCap line_cap = LineCapRound;
451 	GpLineJoin line_join = LineJoinRound;
452 	int s = dwPenStyle & PS_STYLE_MASK;
453 	DWORD color = lplb->lbColor;
454 
455 #ifdef DEBUG_METAFILE
456 	printf ("ExtCreatePenIndirect style %d, width %d, color %X", dwPenStyle, dwWidth, color);
457 #endif
458 	if (s == PS_NULL)
459 		color &= 0x00FFFFFF;
460 	else
461 		color |= 0xFF000000;
462 
463 	status = GdipCreatePen1 (color, dwWidth, UnitPixel, &pen);
464 	if (status != Ok)
465 		return status;
466 
467 	/* The style is always solid for width > 1 */
468 	if (dwWidth > 1) {
469 		switch (s) {
470 		default:
471 			g_warning ("Invalid pen style %d, style & PS_STYLE_MASK %d", dwPenStyle, s);
472 			/* fall through */
473 		case PS_NULL:
474 		case PS_SOLID:
475 			break;
476 		case PS_DASH:
477 		case PS_DOT:
478 		case PS_DASHDOT:
479 		case PS_DASHDOTDOT:
480 			status = GdipSetPenDashStyle (pen, s);
481 			if (status != Ok) {
482 				GdipDeletePen (pen);
483 				return status;
484 			}
485 			break;
486 		}
487 	}
488 
489 	/* at this stage we got a pen, so we won't abort drawing on it's style */
490 	s = (dwPenStyle & PS_TYPE_MASK);
491 	if (s == PS_GEOMETRIC) {
492 		s = (dwPenStyle & PS_ENDCAP_MASK);
493 		switch (s) {
494 		default:
495 			g_warning ("Invalid pen endcap, style %d, (style & PS_ENDCAP_MASK) %d", dwPenStyle, s);
496 			/* fall through */
497 		case PS_ENDCAP_ROUND:
498 			line_cap = LineCapRound;
499 			break;
500 		case PS_ENDCAP_SQUARE:
501 			line_cap = LineCapSquare;
502 			break;
503 		case PS_ENDCAP_FLAT:
504 			line_cap = LineCapFlat;
505 			break;
506 		}
507 		GdipSetPenStartCap (pen, line_cap);
508 		GdipSetPenEndCap (pen, line_cap);
509 
510 		s = (dwPenStyle & PS_JOIN_MASK);
511 		switch (s) {
512 		default:
513 			g_warning ("Invalid pen join, style %d, (style & PS_JOIN_MASK) %d", dwPenStyle, s);
514 			/* fall through */
515 		case PS_JOIN_ROUND:
516 			line_join = LineJoinRound;
517 			break;
518 		case PS_JOIN_BEVEL:
519 			line_join = LineJoinBevel;
520 			break;
521 		case PS_JOIN_MITER:
522 			line_join = LineJoinMiter;
523 			break;
524 		}
525 		GdipSetPenLineJoin (pen, line_join);
526 	}
527 
528 	context->created.type = METAOBJECT_TYPE_PEN;
529 	context->created.ptr = pen;
530 	return Ok;
531 }
532 
533 /* http://wvware.sourceforge.net/caolan/CreateBrushIndirect.html */
534 GpStatus
gdip_metafile_CreateBrushIndirect(MetafilePlayContext * context,DWORD style,DWORD color,DWORD hatch)535 gdip_metafile_CreateBrushIndirect (MetafilePlayContext *context, DWORD style, DWORD color, DWORD hatch)
536 {
537 	GpStatus status = Ok;
538 	void *brush;
539 
540 	switch (style) {
541 	case BS_SOLID:
542 		color |= 0xFF000000;
543 		status = GdipCreateSolidFill (color, (GpSolidFill **) &brush);
544 		break;
545 	case BS_NULL:
546 		color &= 0x00FFFFFF;
547 		status = GdipCreateSolidFill (color, (GpSolidFill **) &brush);
548 		break;
549 	case BS_HATCHED:
550 		color |= 0xFF000000;
551 		status = GdipCreateHatchBrush (hatch, color, 0xFFFFFFFF, (GpHatch **) &brush);
552 		break;
553 	default:
554 		g_warning ("gdip_metafile_CreateBrushIndirect unimplemented style %d", style);
555 		status = GdipCreateSolidFill (color, (GpSolidFill **) &brush);
556 		break;
557 	}
558 
559 #ifdef DEBUG_METAFILE
560 	printf ("CreateBrushIndirect style %d, color %X, hatch %d (%p, status %d)", style, color, hatch, brush, status);
561 #endif
562 	context->created.type = METAOBJECT_TYPE_BRUSH;
563 	context->created.ptr = brush;
564 	return status;
565 }
566 
567 /* http://wvware.sourceforge.net/caolan/Arc.html */
568 GpStatus
gdip_metafile_Arc(MetafilePlayContext * context,int left,int top,int right,int bottom,int xstart,int ystart,int xend,int yend)569 gdip_metafile_Arc (MetafilePlayContext *context, int left, int top, int right, int bottom,
570 	int xstart, int ystart, int xend, int yend)
571 {
572 #ifdef DEBUG_METAFILE
573 	printf ("Arc left %d, top %d, right %d, bottom %d, xstart %d, ystart %d, xend %d, yend %d",
574 		left, top, right, bottom, xstart, ystart, xend, yend);
575 #endif
576 
577 	/* Don't draw if the width or height are empty. */
578 	if ((right - left <= 0) || (bottom - top <= 0))
579 		return Ok;
580 
581 	return GdipDrawArc (context->graphics, gdip_metafile_GetSelectedPen (context), left, top,
582 		(right - left), (bottom - top), atan2 (ystart, xstart), atan2 (yend, xend));
583 }
584 
585 /* http://wvware.sourceforge.net/caolan/Rectangle.html */
586 GpStatus
gdip_metafile_Rectangle(MetafilePlayContext * context,int bottomRect,int rightRect,int topRect,int leftRect)587 gdip_metafile_Rectangle (MetafilePlayContext *context, int bottomRect, int rightRect, int topRect, int leftRect)
588 {
589 	GpStatus status;
590 	int x = min (leftRect, rightRect);
591 	int y = min (topRect, bottomRect);
592 	int width = abs (rightRect - leftRect);
593 	int height = abs (bottomRect - topRect);
594 
595 #ifdef DEBUG_METAFILE
596 	printf ("Rectangle bottom %d, right %d, top %d, left %d",
597 		bottomRect, rightRect, topRect, leftRect);
598 #endif
599 
600 	status = GdipFillRectangleI (context->graphics, gdip_metafile_GetSelectedBrush (context), x, y, width, height);
601 	if (status != Ok)
602 		return status;
603 
604 	return GdipDrawRectangleI (context->graphics, gdip_metafile_GetSelectedPen (context), x, y, width, height);
605 }
606 
607 GpStatus
gdip_metafile_SetPixel(MetafilePlayContext * context,DWORD color,int x,int y)608 gdip_metafile_SetPixel (MetafilePlayContext *context, DWORD color, int x, int y)
609 {
610 	color |= 0xFF000000;
611 
612 	GpBrush *fill;
613 	GpStatus status = GdipCreateSolidFill (color, (GpSolidFill **) &fill);
614 	if (status != Ok)
615 		return status;
616 
617 	status = GdipFillRectangle (context->graphics, fill, x, y, 1, 1);
618 	GdipDeleteBrush (fill);
619 	return status;
620 }
621 
622 
623 /* http://wvware.sourceforge.net/caolan/ora-wmf.html */
624 GpStatus
gdip_metafile_StretchDIBits(MetafilePlayContext * context,int XDest,int YDest,int nDestWidth,int nDestHeight,int XSrc,int YSrc,int nSrcWidth,int nSrcHeight,CONST void * lpBits,CONST BITMAPINFO * lpBitsInfo,UINT iUsage,DWORD dwRop)625 gdip_metafile_StretchDIBits (MetafilePlayContext *context, int XDest, int YDest, int nDestWidth, int nDestHeight,
626 	int XSrc, int YSrc, int nSrcWidth, int nSrcHeight, CONST void *lpBits, CONST BITMAPINFO *lpBitsInfo,
627 	UINT iUsage, DWORD dwRop)
628 {
629 	GpStatus status = OutOfMemory;
630 	GpImage *image = NULL;
631 	MemorySource ms;
632 #ifdef DEBUG_METAFILE
633 	printf ("StretchDIBits\n\t[XDest %d, YDest %d, nDestWidth %d, nDestHeight %d]", XDest, YDest, nDestWidth, nDestHeight);
634 	printf ("\n\t[XSrc %d, YSrc %d, nSrcWidth %d, nSrcHeight %d]", XSrc, YSrc, nSrcWidth, nSrcHeight);
635 	printf ("\n\tlpBits %p, lpBitsInfo %p, iUsage %d, dwRop %d", lpBits, lpBitsInfo, iUsage, dwRop);
636 	printf ("\n\tBITMAPINFO\n\t\tSize: %d", lpBitsInfo->bmiHeader.biSize);
637 	printf ("\n\t\tWidth: %d", lpBitsInfo->bmiHeader.biWidth);
638 	printf ("\n\t\tHeight: %d", lpBitsInfo->bmiHeader.biHeight);
639 	printf ("\n\t\tPlanes: %d", lpBitsInfo->bmiHeader.biPlanes);
640 	printf ("\n\t\tBitCount: %d", lpBitsInfo->bmiHeader.biBitCount);
641 	printf ("\n\t\tCompression: %d", lpBitsInfo->bmiHeader.biCompression);
642 	printf ("\n\t\tSizeImage: %d", lpBitsInfo->bmiHeader.biSizeImage);
643 	printf ("\n\t\tXPelsPerMeter: %d", lpBitsInfo->bmiHeader.biXPelsPerMeter);
644 	printf ("\n\t\tYPelsPerMeter: %d", lpBitsInfo->bmiHeader.biXPelsPerMeter);
645 	printf ("\n\t\tClrUsed: %d", lpBitsInfo->bmiHeader.biClrUsed);
646 	printf ("\n\t\tClrImportant: %d", lpBitsInfo->bmiHeader.biClrImportant);
647 #endif
648 	ms.ptr = (BYTE*)lpBitsInfo;
649 	if (lpBitsInfo->bmiHeader.biCompression == BI_RGB) {
650 		// Per the spec, if compression is RGB ImageSize must be ignored (and it should be zero anyway)
651 		// and calculated according to the following formula.
652 		ms.size = (floor ((lpBitsInfo->bmiHeader.biWidth * lpBitsInfo->bmiHeader.biPlanes *
653 			lpBitsInfo->bmiHeader.biBitCount + 31) / 32) * 4) * abs (lpBitsInfo->bmiHeader.biHeight);
654 	} else {
655 		ms.size = lpBitsInfo->bmiHeader.biSizeImage;
656 	}
657 	ms.pos = 0;
658 	status = gdip_read_bmp_image (&ms, &image, Memory);
659 	if (status == Ok) {
660 		status = GdipDrawImageRectRect (context->graphics, image, XDest, YDest,
661 			nDestWidth, nDestHeight, XSrc, YSrc, nSrcWidth, nSrcHeight, UnitPixel, NULL, NULL, NULL);
662 	}
663 	if (image)
664 		GdipDisposeImage (image);
665 	return status;
666 }
667 
668 /*
669  * Return the selected GDI+ Pen to draw on the metafile or NULL if none is selected/valid.
670  */
671 GpPen*
gdip_metafile_GetSelectedPen(MetafilePlayContext * context)672 gdip_metafile_GetSelectedPen (MetafilePlayContext *context)
673 {
674 	GpPen *pen;
675 
676 	// EMF have some stock objects that do not requires explicit creation
677 	if (context->selected_pen & ENHMETA_STOCK_OBJECT) {
678 		switch (context->selected_pen - ENHMETA_STOCK_OBJECT) {
679 		case WHITE_PEN:
680 			if (!context->stock_pen_white) {
681 				if (GdipCreatePen1 (0xFFFFFFFF, 0, UnitPixel, &context->stock_pen_white) != Ok)
682 					return NULL;
683 			}
684 			pen = context->stock_pen_white;
685 			break;
686 		case BLACK_PEN:
687 			if (!context->stock_pen_black) {
688 				if (GdipCreatePen1 (0xFF000000, 0, UnitPixel, &context->stock_pen_black) != Ok)
689 					return NULL;
690 			}
691 			pen = context->stock_pen_black;
692 			break;
693 		case NULL_PEN:
694 			if (!context->stock_pen_null) {
695 				if (GdipCreatePen1 (0x00000000, 0, UnitPixel, &context->stock_pen_null) != Ok)
696 					return NULL;
697 			}
698 			pen = context->stock_pen_null;
699 			break;
700 		default:
701 			return NULL;
702 		}
703 	} else {
704 		if ((context->selected_pen < 0) || (context->selected_pen >= context->objects_count)) {
705 			g_warning ("Invalid pen handle %d [0..%d[", context->selected_pen, context->objects_count);
706 			return NULL;
707 		}
708 
709 		if (context->objects [context->selected_pen].type != METAOBJECT_TYPE_PEN) {
710 			g_warning ("Wrong object type %d, expected pen (%d)", context->objects [context->selected_pen].type,
711 				METAOBJECT_TYPE_PEN);
712 			return NULL;
713 		}
714 #ifdef DEBUG_METAFILE
715 		printf ("\tusing pen #%d (%p)", context->selected_pen, context->objects [context->selected_pen].ptr);
716 #endif
717 		pen = (GpPen*) context->objects [context->selected_pen].ptr;
718 	}
719 
720 	/* miter limit was global (i.e. context not pen specific) in GDI */
721 	GdipSetPenMiterLimit (pen, context->miter_limit);
722 	return pen;
723 }
724 
725 /*
726  * Return the selected GDI+ Brush to fill on the metafile or NULL if none is selected/valid.
727  */
728 GpBrush*
gdip_metafile_GetSelectedBrush(MetafilePlayContext * context)729 gdip_metafile_GetSelectedBrush (MetafilePlayContext *context)
730 {
731 	// EMF have some stock objects that do not requires explicit creation
732 	if (context->selected_brush & ENHMETA_STOCK_OBJECT) {
733 		switch (context->selected_brush - ENHMETA_STOCK_OBJECT) {
734 		case WHITE_BRUSH:
735 			if (!context->stock_brush_white) {
736 				if (GdipCreateSolidFill (0xFFFFFFFF, &context->stock_brush_white) != Ok)
737 					return NULL;
738 			}
739 			return (GpBrush*)context->stock_brush_white;
740 		case LTGRAY_BRUSH:
741 			if (!context->stock_brush_ltgray) {
742 				if (GdipCreateSolidFill (0xFFBBBBBB, &context->stock_brush_ltgray) != Ok)
743 					return NULL;
744 			}
745 			return (GpBrush*)context->stock_brush_ltgray;
746 		case GRAY_BRUSH:
747 			if (!context->stock_brush_gray) {
748 				if (GdipCreateSolidFill (0xFF888888, &context->stock_brush_gray) != Ok)
749 					return NULL;
750 			}
751 			return (GpBrush*)context->stock_brush_gray;
752 		case DKGRAY_BRUSH:
753 			if (!context->stock_brush_dkgray) {
754 				if (GdipCreateSolidFill (0xFF444444, &context->stock_brush_dkgray) != Ok)
755 					return NULL;
756 			}
757 			return (GpBrush*)context->stock_brush_dkgray;
758 		case BLACK_BRUSH:
759 			if (!context->stock_brush_black) {
760 				if (GdipCreateSolidFill (0xFF000000, &context->stock_brush_black) != Ok)
761 					return NULL;
762 			}
763 			return (GpBrush*)context->stock_brush_black;
764 		case NULL_BRUSH:
765 			if (!context->stock_brush_null) {
766 				if (GdipCreateSolidFill (0x00000000, &context->stock_brush_null) != Ok)
767 					return NULL;
768 			}
769 			return (GpBrush*)context->stock_brush_null;
770 		default:
771 			return NULL;
772 		}
773 	}
774 
775 	if ((context->selected_brush < 0) || (context->selected_brush >= context->objects_count)) {
776 		g_warning ("Invalid brush handle %d [0..%d[", context->selected_brush, context->objects_count);
777 		return NULL;
778 	}
779 
780 	if (context->objects [context->selected_brush].type != METAOBJECT_TYPE_BRUSH) {
781 		g_warning ("Wrong object type %d, expected brush (%d)", context->objects [context->selected_brush].type,
782 			METAOBJECT_TYPE_BRUSH);
783 		return NULL;
784 	}
785 
786 #ifdef DEBUG_METAFILE
787 	printf ("\tusing brush #%d (%p)", context->selected_brush, context->objects [context->selected_brush].ptr);
788 #endif
789 	return (GpBrush*) context->objects [context->selected_brush].ptr;
790 }
791 
792 GpStatus
gdip_metafile_PolyBezier(MetafilePlayContext * context,GpPointF * points,int count)793 gdip_metafile_PolyBezier (MetafilePlayContext *context, GpPointF *points, int count)
794 {
795 	GpStatus status;
796 #ifdef DEBUG_METAFILE
797 	printf ("PolyBezier %s count %d", context->use_path ? "Path " : " ", count);
798 #endif
799 	if (context->use_path) {
800 		status = GdipAddPathBeziers (context->path, points, count);
801 	} else {
802 		GpPen *pen = gdip_metafile_GetSelectedPen (context);
803 		return GdipDrawCurve (context->graphics, pen, points, count);
804 	}
805 	return status;
806 }
807 
808 GpStatus
gdip_metafile_Polygon(MetafilePlayContext * context,GpPointF * points,int count)809 gdip_metafile_Polygon (MetafilePlayContext *context, GpPointF *points, int count)
810 {
811 #ifdef DEBUG_METAFILE
812 	printf ("Polygon %s count %d", context->use_path ? "Path " : " ", count);
813 #endif
814 	GpBrush *brush = gdip_metafile_GetSelectedBrush (context);
815 	GpStatus status = GdipFillPolygon (context->graphics, brush, points, count, context->fill_mode);
816 	if (status == Ok) {
817 		GpPen *pen = gdip_metafile_GetSelectedPen (context);
818 		status = GdipDrawPolygon (context->graphics, pen, points, count);
819 	}
820 	return status;
821 }
822 
823 GpStatus
gdip_metafile_BeginPath(MetafilePlayContext * context)824 gdip_metafile_BeginPath (MetafilePlayContext *context)
825 {
826 	GpStatus status;
827 
828 	if (context->path) {
829 		GdipDeletePath (context->path);
830 		context->path = NULL;
831 	}
832 	context->use_path = TRUE;
833 	status = GdipCreatePath (0, &context->path);
834 #ifdef DEBUG_METAFILE
835 	printf ("BeginPath %p", context->path);
836 #endif
837 	return status;
838 }
839 
840 GpStatus
gdip_metafile_EndPath(MetafilePlayContext * context)841 gdip_metafile_EndPath (MetafilePlayContext *context)
842 {
843 #ifdef DEBUG_METAFILE
844 	printf ("EndPath %p", context->path);
845 #endif
846 	context->use_path = FALSE;
847 	return Ok;
848 }
849 
850 GpStatus
gdip_metafile_CloseFigure(MetafilePlayContext * context)851 gdip_metafile_CloseFigure (MetafilePlayContext *context)
852 {
853 #ifdef DEBUG_METAFILE
854 	printf ("CloseFigure %p", context->path);
855 #endif
856 	return GdipClosePathFigures (context->path);
857 }
858 
859 GpStatus
gdip_metafile_FillPath(MetafilePlayContext * context)860 gdip_metafile_FillPath (MetafilePlayContext *context)
861 {
862 #ifdef DEBUG_METAFILE
863 	printf ("FillPath %p", context->path);
864 #endif
865 	GpBrush* brush = gdip_metafile_GetSelectedBrush (context);
866 	/* end path if required */
867 	if (context->use_path)
868 		gdip_metafile_EndPath (context);
869 	return GdipFillPath (context->graphics, brush, context->path);
870 }
871 
872 GpStatus
gdip_metafile_StrokePath(MetafilePlayContext * context)873 gdip_metafile_StrokePath (MetafilePlayContext *context)
874 {
875 #ifdef DEBUG_METAFILE
876 	printf ("StrokePath %p", context->path);
877 #endif
878 	GpPen *pen = gdip_metafile_GetSelectedPen (context);
879 	/* end path if required */
880 	if (context->use_path)
881 		gdip_metafile_EndPath (context);
882 	return GdipDrawPath (context->graphics, pen, context->path);
883 }
884 
885 GpStatus
gdip_metafile_StrokeAndFillPath(MetafilePlayContext * context)886 gdip_metafile_StrokeAndFillPath (MetafilePlayContext *context)
887 {
888 	GpBrush *brush;
889 	GpStatus status;
890 #ifdef DEBUG_METAFILE
891 	printf ("StrokeAndFillPath %p", context->path);
892 #endif
893 	/* end path if required */
894 	if (context->use_path)
895 		gdip_metafile_EndPath (context);
896 
897 	brush = gdip_metafile_GetSelectedBrush (context);
898 	status = GdipFillPath (context->graphics, brush, context->path);
899 	if (status == Ok) {
900 		GpPen *pen = gdip_metafile_GetSelectedPen (context);
901 		status = GdipDrawPath (context->graphics, pen, context->path);
902 	}
903 	return status;
904 }
905 
906 
907 static GpMetafile*
gdip_metafile_create()908 gdip_metafile_create ()
909 {
910 	GpMetafile* mf = (GpMetafile*) GdipAlloc (sizeof (GpMetafile));
911 	if (mf) {
912 		memset (&mf->base, 0, sizeof (GpImage));
913 
914 		mf->base.type = ImageTypeMetafile;
915 		mf->delete = FALSE;
916 		mf->data = NULL;
917 		mf->length = 0;
918 		mf->recording = FALSE;
919 		mf->fp = NULL;
920 		mf->stream = NULL;
921 	}
922 	return mf;
923 }
924 
925 GpStatus
gdip_metafile_clone(GpMetafile * metafile,GpMetafile ** clonedmetafile)926 gdip_metafile_clone (GpMetafile *metafile, GpMetafile **clonedmetafile)
927 {
928 	GpStatus status;
929 	GpMetafile *mf = gdip_metafile_create ();
930 	GpImage *base;
931 	if (!mf)
932 		return OutOfMemory;
933 
934 	status = gdip_bitmap_clone (&metafile->base, &base);
935 	if (status != Ok) {
936 		GdipFree (mf);
937 		return status;
938 	}
939 
940 	status = gdip_bitmap_setactive (base, NULL, 0);
941 	if (status != Ok) {
942 		GdipFree (mf);
943 		GdipFree (base);
944 		return status;
945 	}
946 
947 	mf->base = *base;
948 
949 	memcpy (&mf->metafile_header, &metafile->metafile_header, sizeof (MetafileHeader));
950 	if (metafile->length > 0) {
951 		mf->data = GdipAlloc (metafile->length);
952 		if (!mf->data) {
953 			GdipFree (mf);
954 			return OutOfMemory;
955 		}
956 
957 		memcpy (mf->data, metafile->data, metafile->length);
958 		mf->length = metafile->length;
959 	}
960 
961 	*clonedmetafile = mf;
962 	return Ok;
963 }
964 
965 GpStatus
gdip_metafile_dispose(GpMetafile * metafile)966 gdip_metafile_dispose (GpMetafile *metafile)
967 {
968 	if (!metafile)
969 		return InvalidParameter;
970 
971 	/* TODO deal with "delete" flag */
972 	metafile->length = 0;
973 	if (metafile->data) {
974 		GdipFree (metafile->data);
975 		metafile->data = NULL;
976 	}
977 
978 	if (metafile->recording)
979 		gdip_metafile_stop_recording (metafile);
980 
981 	GdipFree (metafile);
982 	return Ok;
983 }
984 
985 GpStatus
gdip_get_bitmap_from_metafile(GpMetafile * metafile,INT width,INT height,GpImage ** thumbnail)986 gdip_get_bitmap_from_metafile (GpMetafile *metafile, INT width, INT height, GpImage **thumbnail)
987 {
988 	if (width <= 0 || height <= 0) {
989 		switch (metafile->metafile_header.Type) {
990 		case MetafileTypeWmfPlaceable:
991 		case MetafileTypeWmf: {
992 			width = iround (metafile->metafile_header.Width / 1000.0f * gdip_get_display_dpi());
993 			height = iround (metafile->metafile_header.Height / 1000.0f * gdip_get_display_dpi());
994 			break;
995 		}
996 		case MetafileTypeEmf:
997 		case MetafileTypeEmfPlusOnly:
998 		case MetafileTypeEmfPlusDual: {
999 			width = metafile->metafile_header.Width;
1000 			height = metafile->metafile_header.Height;
1001 			break;
1002 		}
1003 		default:
1004 			return GenericError;
1005 		}
1006 	}
1007 
1008 	return GdipGetImageThumbnail ((GpImage *) metafile, width, height, thumbnail, NULL, NULL);
1009 }
1010 
1011 GpStatus
gdip_metafile_stop_recording(GpMetafile * metafile)1012 gdip_metafile_stop_recording (GpMetafile *metafile)
1013 {
1014 	/* TODO save current stuff */
1015 
1016 	if (metafile->fp) {
1017 		fclose (metafile->fp);
1018 		metafile->fp = NULL;
1019 	}
1020 	if (metafile->stream) {
1021 		/* it's not ours to close, just forget about it */
1022 		metafile->stream = NULL;
1023 	}
1024 	/* we cannot open a new graphics instance on this metafile - recording is over */
1025 	metafile->recording = FALSE;
1026 	return Ok;
1027 }
1028 
1029 MetafilePlayContext*
gdip_metafile_play_setup(GpMetafile * metafile,GpGraphics * graphics,int x,int y,int width,int height)1030 gdip_metafile_play_setup (GpMetafile *metafile, GpGraphics *graphics, int x, int y, int width, int height)
1031 {
1032 	int i;
1033 	MetaObject *obj;
1034 	MetafilePlayContext *context;
1035 	float scaleX;
1036 	float scaleY;
1037 	float translateX;
1038 	float translateY;
1039 	/* metafiles always render as 32bppRgb */
1040 
1041 	if (!metafile || !graphics)
1042 		return NULL;
1043 
1044 	context = GdipAlloc (sizeof (MetafilePlayContext));
1045 	if (!context) {
1046 		return NULL;
1047 	}
1048 
1049 	context->metafile = metafile;
1050 	context->graphics = graphics;
1051 	context->use_path = FALSE;
1052 	context->path = NULL;
1053 
1054 	/* keep a copy for clean up */
1055 	GdipGetWorldTransform (graphics, &context->initial);
1056 #ifdef DEBUG_METAFILE
1057 	MetafileHeader *mh = &metafile->metafile_header;
1058 	GpMatrix *m = &context->initial;
1059 	g_warning ("\nMetafileHeader X %d, Y %d, W %d, H %d", mh->X, mh->Y, mh->Width, mh->Height);
1060 	g_warning ("\n\tinitial matrix %g %g %g %g %g %g", m->xx, m->yx, m->xy, m->yy, m->x0, m->y0);
1061 #endif
1062 	context->x = x;
1063 	context->y = y;
1064 	context->width = width;
1065 	context->height = height;
1066 	/* and keep an adjusted copy for providing "resets" */
1067 	scaleX = (float) width / (float) metafile->metafile_header.Width;
1068 	scaleY = (float) height / (float) metafile->metafile_header.Height;
1069 	GdipScaleWorldTransform (graphics, scaleX, scaleY, MatrixOrderPrepend);
1070 	translateX = -metafile->metafile_header.X + x / scaleX;
1071 	translateY = -metafile->metafile_header.Y + y / scaleY;
1072 	GdipTranslateWorldTransform (graphics, translateX, translateY, MatrixOrderPrepend);
1073 	GdipGetWorldTransform (graphics, &context->matrix);
1074 
1075 	/* defaults */
1076 	context->fill_mode = FillModeAlternate;
1077 	switch (context->metafile->metafile_header.Type) {
1078 		case MetafileTypeWmfPlaceable:
1079 		case MetafileTypeWmf:
1080 			gdip_metafile_SetMapMode (context, MM_TWIPS);
1081 			break;
1082 		case MetafileTypeEmf:
1083 		case MetafileTypeEmfPlusOnly:
1084 		case MetafileTypeEmfPlusDual:
1085 			gdip_metafile_SetMapMode (context, MM_TEXT);
1086 			break;
1087 		default:
1088 			GdipFree (context);
1089 			return NULL;
1090 	}
1091 
1092 	context->miter_limit = 10.0f;
1093 	context->selected_pen =  ENHMETA_STOCK_OBJECT + BLACK_PEN;
1094 	context->selected_brush = ENHMETA_STOCK_OBJECT + WHITE_BRUSH;
1095 	context->selected_font = -1;
1096 	context->selected_palette = -1;
1097 
1098 	/* Create* functions store the object here */
1099 	context->created.type = METAOBJECT_TYPE_EMPTY;
1100 	context->created.ptr = NULL;
1101 
1102 	/* stock objects */
1103 	context->stock_pen_white = NULL;
1104 	context->stock_pen_black = NULL;
1105 	context->stock_pen_null = NULL;
1106 	context->stock_brush_white = NULL;
1107 	context->stock_brush_ltgray = NULL;
1108 	context->stock_brush_gray = NULL;
1109 	context->stock_brush_dkgray = NULL;
1110 	context->stock_brush_black = NULL;
1111 	context->stock_brush_null = NULL;
1112 
1113 	/* SelectObject | DeleteObject works on this array */
1114 	switch (context->metafile->metafile_header.Type) {
1115 	case MetafileTypeWmfPlaceable:
1116 	case MetafileTypeWmf:
1117 		context->objects_count = metafile->metafile_header.Header.Wmf.mtNoObjects;
1118 		break;
1119 	case MetafileTypeEmf:
1120 	case MetafileTypeEmfPlusOnly:
1121 	case MetafileTypeEmfPlusDual:
1122 		context->objects_count = metafile->metafile_header.Header.Emf.nHandles + 1; /* 0 is reserved */
1123 		break;
1124 	default:
1125 		GdipFree (context);
1126 		return NULL;
1127 	}
1128 	context->objects = (MetaObject*) GdipAlloc (context->objects_count * sizeof (MetaObject));
1129 	if (!context->objects) {
1130 		GdipFree (context);
1131 		return NULL;
1132 	}
1133 	obj = context->objects;
1134 	for (i = 0; i < context->objects_count; i++) {
1135 		obj->type = METAOBJECT_TYPE_EMPTY;
1136 		obj->ptr = NULL;
1137 		obj++;
1138 	}
1139 
1140 	return context;
1141 }
1142 
1143 GpStatus
gdip_metafile_play(MetafilePlayContext * context)1144 gdip_metafile_play (MetafilePlayContext *context)
1145 {
1146 	if (!context || !context->metafile)
1147 		return InvalidParameter;
1148 
1149 	switch (context->metafile->metafile_header.Type) {
1150 	case MetafileTypeWmfPlaceable:
1151 	case MetafileTypeWmf:
1152 		return gdip_metafile_play_wmf (context);
1153 	case MetafileTypeEmf:
1154 		return gdip_metafile_play_emf (context);
1155 	case MetafileTypeEmfPlusOnly:
1156 	case MetafileTypeEmfPlusDual:
1157 		return gdip_metafile_play_emf (context);
1158 	default:
1159 		g_warning ("Invalid metafile format %d", context->metafile->metafile_header.Type);
1160 		break;
1161 	}
1162 	return NotImplemented;
1163 }
1164 
1165 GpStatus
gdip_metafile_play_cleanup(MetafilePlayContext * context)1166 gdip_metafile_play_cleanup (MetafilePlayContext *context)
1167 {
1168 	if (!context)
1169 		return InvalidParameter;
1170 
1171 	GdipSetWorldTransform (context->graphics, &context->initial);
1172 	context->graphics = NULL;
1173 	if (context->path) {
1174 		GdipDeletePath (context->path);
1175 		context->path = NULL;
1176 	}
1177 	context->created.type = METAOBJECT_TYPE_EMPTY;
1178 	context->created.ptr = NULL;
1179 	if (context->objects) {
1180 		int i;
1181 		MetaObject *obj = context->objects;
1182 		/* free each object */
1183 		for (i = 0; i < context->objects_count; i++) {
1184 			gdip_metafile_DeleteObject (context, i);
1185 			obj++;
1186 		}
1187 		GdipFree (context->objects);
1188 		context->objects = NULL;
1189 	}
1190 
1191 	context->selected_pen = -1;
1192 	context->selected_brush = -1;
1193 	context->selected_font = -1;
1194 	context->selected_font = -1;
1195 	context->selected_palette = -1;
1196 
1197 	/* stock objects */
1198 	if (context->stock_pen_white)
1199 		GdipDeletePen (context->stock_pen_white);
1200 	if (context->stock_pen_black)
1201 		GdipDeletePen (context->stock_pen_black);
1202 	if (context->stock_pen_null)
1203 		GdipDeletePen (context->stock_pen_null);
1204 	if (context->stock_brush_white)
1205 		GdipDeleteBrush ((GpBrush*)context->stock_brush_white);
1206 	if (context->stock_brush_ltgray)
1207 		GdipDeleteBrush ((GpBrush*)context->stock_brush_ltgray);
1208 	if (context->stock_brush_gray)
1209 		GdipDeleteBrush ((GpBrush*)context->stock_brush_gray);
1210 	if (context->stock_brush_dkgray)
1211 		GdipDeleteBrush ((GpBrush*)context->stock_brush_dkgray);
1212 	if (context->stock_brush_black)
1213 		GdipDeleteBrush ((GpBrush*)context->stock_brush_black);
1214 	if (context->stock_brush_null)
1215 		GdipDeleteBrush ((GpBrush*)context->stock_brush_null);
1216 
1217 	GdipFree (context);
1218 	return Ok;
1219 }
1220 
1221 static void
WmfPlaceableFileHeaderLE(WmfPlaceableFileHeader * wmfPlaceableFileHeader)1222 WmfPlaceableFileHeaderLE (WmfPlaceableFileHeader *wmfPlaceableFileHeader)
1223 {
1224 #if G_BYTE_ORDER != G_LITTLE_ENDIAN
1225 	/* header->Key is already adjusted */
1226 	wmfPlaceableFileHeader->Hmf = GUINT16_FROM_LE (wmfPlaceableFileHeader->Hmf);
1227 	wmfPlaceableFileHeader->BoundingBox.Left = GUINT16_FROM_LE (wmfPlaceableFileHeader->BoundingBox.Left);
1228 	wmfPlaceableFileHeader->BoundingBox.Top = GUINT16_FROM_LE (wmfPlaceableFileHeader->BoundingBox.Top);
1229 	wmfPlaceableFileHeader->BoundingBox.Right = GUINT16_FROM_LE (wmfPlaceableFileHeader->BoundingBox.Right);
1230 	wmfPlaceableFileHeader->BoundingBox.Bottom = GUINT16_FROM_LE (wmfPlaceableFileHeader->BoundingBox.Bottom);
1231 	wmfPlaceableFileHeader->Inch = GUINT16_FROM_LE (wmfPlaceableFileHeader->Inch);
1232 	wmfPlaceableFileHeader->Reserved = GUINT32_FROM_LE (wmfPlaceableFileHeader->Reserved);
1233 	wmfPlaceableFileHeader->Checksum = GUINT16_FROM_LE (wmfPlaceableFileHeader->Checksum);
1234 #endif
1235 }
1236 
1237 static void
MetafileHeaderLE(MetafileHeader * header)1238 MetafileHeaderLE (MetafileHeader *header)
1239 {
1240 #if G_BYTE_ORDER != G_LITTLE_ENDIAN
1241 	header->Header.Wmf.mtType = GUINT16_FROM_LE (header->Header.Wmf.mtType);
1242 	header->Header.Wmf.mtHeaderSize = GUINT16_FROM_LE (header->Header.Wmf.mtHeaderSize);
1243 	header->Header.Wmf.mtVersion = GUINT16_FROM_LE (header->Header.Wmf.mtVersion);
1244 	header->Header.Wmf.mtSize = GUINT32_FROM_LE (header->Header.Wmf.mtSize);
1245 	header->Header.Wmf.mtNoObjects = GUINT16_FROM_LE (header->Header.Wmf.mtNoObjects);
1246 	header->Header.Wmf.mtMaxRecord = GUINT32_FROM_LE (header->Header.Wmf.mtMaxRecord);
1247 	header->Header.Wmf.mtNoParameters = GUINT16_FROM_LE (header->Header.Wmf.mtNoParameters);
1248 #endif
1249 }
1250 
1251 static void
EnhMetaHeaderLE(ENHMETAHEADER3 * emf)1252 EnhMetaHeaderLE (ENHMETAHEADER3 *emf)
1253 {
1254 #if G_BYTE_ORDER != G_LITTLE_ENDIAN
1255 	/* emf->iType is already adjusted */
1256 	emf->nSize = GUINT32_FROM_LE (emf->nSize);
1257 	emf->rclBounds.left = GUINT32_FROM_LE (emf->rclBounds.left);
1258 	emf->rclBounds.top = GUINT32_FROM_LE (emf->rclBounds.top);
1259 	emf->rclBounds.right = GUINT32_FROM_LE (emf->rclBounds.right);
1260 	emf->rclBounds.bottom = GUINT32_FROM_LE (emf->rclBounds.bottom);
1261 	emf->rclFrame.left = GUINT32_FROM_LE (emf->rclFrame.left);
1262 	emf->rclFrame.top = GUINT32_FROM_LE (emf->rclFrame.top);
1263 	emf->rclFrame.right = GUINT32_FROM_LE (emf->rclFrame.right);
1264 	emf->rclFrame.bottom = GUINT32_FROM_LE (emf->rclFrame.bottom);
1265 	emf->dSignature = GUINT32_FROM_LE (emf->dSignature);
1266 	emf->nVersion = GUINT32_FROM_LE (emf->nVersion);
1267 	emf->nBytes = GUINT32_FROM_LE (emf->nBytes);
1268 	emf->nRecords = GUINT32_FROM_LE (emf->nRecords);
1269 	emf->nHandles = GUINT32_FROM_LE (emf->nHandles);
1270 
1271 	emf->sReserved = GUINT32_FROM_LE (emf->sReserved);
1272 	emf->nDescription = GUINT32_FROM_LE (emf->nDescription);
1273 	emf->offDescription = GUINT32_FROM_LE (emf->offDescription);
1274 	emf->nPalEntries = GUINT32_FROM_LE (emf->nPalEntries);
1275 	emf->szlDevice.cx = GUINT32_FROM_LE (emf->szlDevice.cx);
1276 	emf->szlDevice.cy = GUINT32_FROM_LE (emf->szlDevice.cy);
1277 	emf->szlMillimeters.cx = GUINT32_FROM_LE (emf->szlMillimeters.cx);
1278 	emf->szlMillimeters.cy = GUINT32_FROM_LE (emf->szlMillimeters.cy);
1279 #endif
1280 }
1281 
1282 static GpStatus
combine_headers(GDIPCONST WmfPlaceableFileHeader * wmfPlaceableFileHeader,MetafileHeader * header)1283 combine_headers (GDIPCONST WmfPlaceableFileHeader *wmfPlaceableFileHeader, MetafileHeader *header)
1284 {
1285 	if (wmfPlaceableFileHeader) {
1286 		header->Type = MetafileTypeWmfPlaceable;
1287 
1288 		header->X = min (wmfPlaceableFileHeader->BoundingBox.Right, wmfPlaceableFileHeader->BoundingBox.Left);
1289 		header->Y = min (wmfPlaceableFileHeader->BoundingBox.Top, wmfPlaceableFileHeader->BoundingBox.Bottom);
1290 		header->Width = abs (wmfPlaceableFileHeader->BoundingBox.Right - wmfPlaceableFileHeader->BoundingBox.Left);
1291 		header->Height= abs (wmfPlaceableFileHeader->BoundingBox.Bottom - wmfPlaceableFileHeader->BoundingBox.Top);
1292 
1293 		/* The units of a metafile are twips (1/20 of a point). The Inch field contains the number
1294 		*  of twips per inch used to represent the image. There are 1440 twips per inch by default.
1295 		*  Use the default if the placeable header has no twips per inch specified. */
1296 		if (wmfPlaceableFileHeader->Inch == 0)
1297 			header->DpiX = 1440;
1298 		else
1299 			header->DpiX = wmfPlaceableFileHeader->Inch;
1300 	} else {
1301 		header->Type = MetafileTypeWmf;
1302 		header->X = 0;
1303 		header->Y = 0;
1304 		/* TODO: GDI+ uses screen resolution for non-placeable metafiles */
1305 		header->Width = 1280;
1306 		header->Height= 1024;
1307 		header->DpiX = gdip_get_display_dpi ();
1308 	}
1309 
1310 	header->DpiY = header->DpiX;
1311 	header->Size = header->Header.Wmf.mtSize * 2;
1312 	header->Version = header->Header.Wmf.mtVersion;
1313 	header->EmfPlusFlags = 0;
1314 	header->EmfPlusHeaderSize = 0;
1315 	header->LogicalDpiX = 0;
1316 	header->LogicalDpiY = 0;
1317 	return Ok;
1318 }
1319 
1320 /* Handle the record based headers that can exists in EMF files */
1321 /* Note: GDI+ doesn't report correctly all stuff within the headers :| */
1322 static GpStatus
update_emf_header(MetafileHeader * header,BYTE * data,int length)1323 update_emf_header (MetafileHeader *header, BYTE* data, int length)
1324 {
1325 	GpStatus status = Ok;
1326 	MetafilePlayContext context;
1327 	GpMetafile mf;
1328 	if (length < sizeof (DWORD))
1329 		return Ok;
1330 
1331 	DWORD *func = (DWORD*)data;
1332 
1333 	switch (*func) {
1334 	case EMR_HEADER:
1335 		g_warning ("TODO - EMR_HEADER. Not common, need test case :)");
1336 		break;
1337 	case EMR_GDICOMMENT:
1338 		/* this could be an embedded EmfPlusRecordTypeHeader */
1339 		context.metafile = &mf;
1340 		context.graphics = NULL; /* special case where we're not playing the metafile */
1341 		status = GdiComment (&context, data, length);
1342 		if (status == Ok) {
1343 			header->Type = mf.metafile_header.Type;
1344 			header->Version = mf.metafile_header.Version; /* GDI+ seems to report the object header */
1345 			/* Horizontal and Vertical Logical DPI are available but not reported */
1346 		}
1347 		break;
1348 	}
1349 	return status;
1350 }
1351 
1352 static GpStatus
gdip_read_emf_header_optionals(ENHMETAHEADER3 * header,void * pointer,ImageSource source)1353 gdip_read_emf_header_optionals (ENHMETAHEADER3 *header, void *pointer, ImageSource source)
1354 {
1355 	/* This algorithm is taken from the specification: https://msdn.microsoft.com/en-us/library/cc230635.aspx. */
1356 	const int HeaderRecordSize = sizeof (ENHMETAHEADER3);
1357 	const int HeaderExtension1Size = sizeof (ENHMETAHEADER3) + sizeof (HeaderExtension1);
1358 
1359 	/* Initialize header size to minimum. */
1360 	int headerSize = HeaderRecordSize;
1361 
1362 	/* Valid header record size? */
1363 	if (header->nSize >= HeaderRecordSize)
1364 	{
1365 		/* Set HeaderSize to header record size. */
1366 		headerSize = header->nSize;
1367 
1368 		/* Valid description values? If so, set HeaderSize to description offset. */
1369 		if (header->offDescription >= HeaderRecordSize && (header->offDescription + header->nDescription * 2) <= header->nSize)
1370 			headerSize = header->offDescription;
1371 
1372 		/* Header big enough to contain an extension? */
1373 		if (headerSize >= HeaderExtension1Size)
1374 		{
1375 			/* Match GDI+ behaviour where missing header data is set to 0. */
1376 			HeaderExtension1 extension;
1377 			memset (&extension, 0, sizeof (HeaderExtension1));
1378 			gdip_read_emf_data (pointer, (BYTE *) &extension, sizeof (HeaderExtension1), source);
1379 
1380 			/* Valid pixel format values? */
1381 			if (extension.offPixelFormat >= HeaderExtension1Size && (extension.offPixelFormat + extension.cbPixelFormat) <= header->nSize)
1382 			{
1383 				/* Pixel format before description? If so, set HeaderSize to pixel format offset. */
1384 				if (extension.offPixelFormat > header->offDescription)
1385 					headerSize = extension.offPixelFormat;
1386 			}
1387 		}
1388 	}
1389 
1390 	header->nSize = headerSize;
1391 	return Ok;
1392 }
1393 
1394 static GpStatus
gdip_get_metafileheader_from(void * pointer,MetafileHeader * header,ImageSource source)1395 gdip_get_metafileheader_from (void *pointer, MetafileHeader *header, ImageSource source)
1396 {
1397 	int size;
1398 	DWORD key;
1399 	GpStatus status = NotImplemented;
1400 	WmfPlaceableFileHeader aldus_header;
1401 	ENHMETAHEADER3 *emf;
1402 
1403 	/* peek at first DWORD to select the right format */
1404 	size = sizeof (DWORD);
1405 	if (gdip_read_wmf_data (pointer, (void*)&key, size, source) != size)
1406 		return OutOfMemory;
1407 
1408 #if G_BYTE_ORDER != G_LITTLE_ENDIAN
1409 	key = GUINT32_FROM_LE (key);
1410 #endif
1411 	switch (key) {
1412 	case ALDUS_PLACEABLE_METAFILE_KEY:
1413 		aldus_header.Key = key;
1414 		size = sizeof (WmfPlaceableFileHeader) - size;
1415 		if (gdip_read_wmf_data(pointer, (BYTE*)(&aldus_header) + sizeof(DWORD), size, source) != size)
1416 			return OutOfMemory;
1417 #ifdef DEBUG_METAFILE
1418 g_warning ("ALDUS_PLACEABLE_METAFILE key %d, hmf %d, L %d, T %d, R %d, B %d, inch %d, reserved %d, checksum %d",
1419 	aldus_header.Key, aldus_header.Hmf, aldus_header.BoundingBox.Left, aldus_header.BoundingBox.Top,
1420 	aldus_header.BoundingBox.Right, aldus_header.BoundingBox.Bottom, aldus_header.Inch, aldus_header.Reserved,
1421 	aldus_header.Checksum);
1422 #endif
1423 		size = sizeof (METAHEADER);
1424 		if (gdip_read_wmf_data (pointer, (BYTE*)&header->Header.Wmf, size, source) != size)
1425 			return OutOfMemory;
1426 
1427 		WmfPlaceableFileHeaderLE (&aldus_header);
1428 		MetafileHeaderLE (header);
1429 
1430 		if (header->Header.Wmf.mtType != 1 && header->Header.Wmf.mtType != 2)
1431 			return OutOfMemory;
1432 		if (header->Header.Wmf.mtHeaderSize != 9)
1433 			return OutOfMemory;
1434 		if (header->Header.Wmf.mtVersion != 0x0100 && header->Header.Wmf.mtVersion != 0x0300)
1435 			return OutOfMemory;
1436 
1437 		status = combine_headers (&aldus_header, header);
1438 		break;
1439 	case WMF_TYPE_AND_HEADERSIZE_KEY:
1440 		memcpy (&header->Header.Wmf, &key, size);
1441 
1442 		size = sizeof (METAHEADER) - size;
1443 		if (gdip_read_wmf_data (pointer, (BYTE*)(&header->Header.Wmf) + sizeof(DWORD), size, source) != size)
1444 			return InvalidParameter;
1445 
1446 		MetafileHeaderLE (header);
1447 		status = combine_headers (NULL, header);
1448 		break;
1449 	case EMF_EMR_HEADER_KEY:
1450 		emf = &header->Header.Emf;
1451 		emf->iType = key;
1452 
1453 		/* Match GDI+ behaviour where missing header data is set to 0. */
1454 		size = sizeof (ENHMETAHEADER3) - size;
1455 		memset ((BYTE *) emf + sizeof (DWORD), 0, size);
1456 		gdip_read_emf_data (pointer, (BYTE *) emf + sizeof (DWORD), size, source);
1457 
1458 		EnhMetaHeaderLE (&header->Header.Emf);
1459 
1460 #ifdef DEBUG_METAFILE
1461 g_warning ("EMF HEADER iType %d, nSize %d, Bounds L %d, T %d, R %d, B %d, Frame L %d, T %d, R %d, B %d, signature %X, version %d, bytes %d, records %d, handles %d, reserved %d, description %d, %d, palentries %d, device %d, %d, millimeters %d, %d",
1462 	emf->iType, emf->nSize,
1463 	emf->rclBounds.left, emf->rclBounds.top, emf->rclBounds.right, emf->rclBounds.bottom,
1464 	emf->rclFrame.left, emf->rclFrame.top, emf->rclFrame.right, emf->rclFrame.bottom,
1465 	emf->dSignature, emf->nVersion, emf->nBytes, emf->nRecords, emf->nHandles,
1466 	emf->sReserved, emf->nDescription, emf->offDescription, emf->nPalEntries,
1467 	emf->szlDevice.cx, emf->szlDevice.cy, emf->szlMillimeters.cx, emf->szlMillimeters.cy);
1468 #endif
1469 		if ((emf->iType != 1) || (emf->dSignature != 0x464D4520))
1470 			return OutOfMemory;
1471 		if (emf->nRecords < 2)
1472 			return OutOfMemory;
1473 		if (emf->nHandles == 0)
1474 			return OutOfMemory;
1475 		if (emf->nBytes < emf->nSize || emf->nBytes & 3)
1476 			return OutOfMemory;
1477 		if (emf->szlDevice.cx == 0 || emf->szlDevice.cy == 0)
1478 			return OutOfMemory;
1479 		if (emf->szlMillimeters.cx == 0 || emf->szlMillimeters.cy == 0)
1480 			return OutOfMemory;
1481 
1482 		header->Type = MetafileTypeEmf;
1483 
1484 		/* Convert millimetres to inches to get the DPI for each dimension. */
1485 		header->DpiX = emf->szlDevice.cx / (emf->szlMillimeters.cx / MM_PER_INCH);
1486 		header->DpiY = emf->szlDevice.cy / (emf->szlMillimeters.cy / MM_PER_INCH);
1487 
1488 		/* The bounding box of the metafile is derived from rclFrame, not rclBounds.
1489 		 * We need to perform some unit conversions from 100s of millimetres to pixels. */
1490 		REAL inchLeft = emf->rclFrame.left / (MM_PER_INCH * 100);
1491 		REAL inchTop = emf->rclFrame.top / (MM_PER_INCH * 100);
1492 		REAL inchRight = emf->rclFrame.right / (MM_PER_INCH * 100);
1493 		REAL inchBottom = emf->rclFrame.bottom / (MM_PER_INCH * 100);
1494 
1495 		header->X = iround (inchLeft * header->DpiX);
1496 		header->Y = iround (inchTop * header->DpiY);
1497 
1498 		/* The frame is inclusive, so we need to add 1. */
1499 		header->Width = iround ((inchRight - inchLeft) * header->DpiX) + 1;
1500 		header->Height = iround ((inchBottom - inchTop) * header->DpiY) + 1;
1501 
1502 		header->Size = emf->nBytes;
1503 		header->Version = emf->nVersion;
1504 		/* We need to check for the EmfHeader record (can't be done at this stage) but some files still returns
1505 		 * invalid values. E.g. Files with "strange" bounds also have insanely large EmfPlusHeaderSize and logical
1506 		 * DPI values (with MS GDI+) */
1507 		header->EmfPlusFlags = 0;
1508 		header->EmfPlusHeaderSize = 0;
1509 		header->LogicalDpiX = 0;
1510 		header->LogicalDpiY = 0;
1511 
1512 		status = gdip_read_emf_header_optionals (emf, pointer, source);
1513 		break;
1514 	default:
1515 		status = OutOfMemory;
1516 	}
1517 
1518 #ifdef DEBUG_METAFILE
1519 g_warning ("METAHEADER type %d, header %d, version %d, size %d, #obj %d, max rec %d, #params %d",
1520 	header->Header.Wmf.mtType, header->Header.Wmf.mtHeaderSize, header->Header.Wmf.mtVersion,
1521 	header->Header.Wmf.mtSize, header->Header.Wmf.mtNoObjects, header->Header.Wmf.mtMaxRecord,
1522 	header->Header.Wmf.mtNoParameters);
1523 #endif
1524 	return status;
1525 }
1526 
1527 GpStatus
gdip_get_metafile_from(void * pointer,GpMetafile ** metafile,ImageSource source)1528 gdip_get_metafile_from (void *pointer, GpMetafile **metafile, ImageSource source)
1529 {
1530 	BOOL adjust_emf_headers = FALSE;
1531 	GpStatus status = OutOfMemory;
1532 	GpMetafile *mf = gdip_metafile_create ();
1533 	if (!mf)
1534 		goto error;
1535 
1536 	/* decode metafile header */
1537 	status = gdip_get_metafileheader_from (pointer, &mf->metafile_header, source);
1538 	if (status != Ok)
1539 		goto error;
1540 
1541 	switch (mf->metafile_header.Type) {
1542 	case MetafileTypeWmfPlaceable:
1543 	case MetafileTypeWmf:
1544 		mf->base.image_format = WMF;
1545 		/* note: mtSize is in WORD, mtSize contains the METAHEADER, mf->length is in bytes */
1546 		mf->length = mf->metafile_header.Header.Wmf.mtSize * 2 - sizeof (METAHEADER);
1547 
1548 		/* The file size has to be enough for an EOF record. */
1549 		if (mf->length == 0)
1550 		{
1551 			if (mf->metafile_header.Header.Wmf.mtMaxRecord != 3)
1552 			{
1553 				status = OutOfMemory;
1554 				goto error;
1555 			}
1556 		}
1557 		else if (mf->length <= 4)
1558 		{
1559 			status = OutOfMemory;
1560 			goto error;
1561 		}
1562 
1563 		break;
1564 	case MetafileTypeEmf:
1565 	case MetafileTypeEmfPlusOnly:
1566 	case MetafileTypeEmfPlusDual:
1567 		mf->base.image_format = EMF;
1568 		mf->length = mf->metafile_header.Header.Emf.nBytes - mf->metafile_header.Header.Emf.nSize;
1569 		adjust_emf_headers = TRUE;
1570 		break;
1571 	default:
1572 		status = GenericError;
1573 		goto error;
1574 	}
1575 
1576 	mf->data = (BYTE*) GdipAlloc (mf->length);
1577 	if (!mf->data)
1578 		goto error;
1579 
1580 	/* Copy the data into memory for playback later. To match GDI+ behaviour, we don't validate that there is
1581 	 * as much data as the header says. Instead, if the data length is invalid and there is no EOF record before
1582 	 * we run out of space in the buffer playback will fail. */
1583 	mf->length = gdip_read_wmf_data (pointer, (void *) mf->data, mf->length, source);
1584 
1585 	if (adjust_emf_headers) {
1586 		/* if the first EMF record is an EmfHeader (or an Header inside a Comment) then we have extra data to extract */
1587 		status = update_emf_header (&mf->metafile_header, mf->data, mf->length);
1588 		if (status != Ok)
1589 			goto error;
1590 	}
1591 
1592 	*metafile = mf;
1593 	return Ok;
1594 error:
1595 	if (mf)
1596 		gdip_metafile_dispose (mf);
1597 	*metafile = NULL;
1598 	return status;
1599 }
1600 
1601 /* public (GDI+) functions */
1602 
1603 GpStatus
GdipCreateMetafileFromFile(GDIPCONST WCHAR * file,GpMetafile ** metafile)1604 GdipCreateMetafileFromFile (GDIPCONST WCHAR *file, GpMetafile **metafile)
1605 {
1606 	FILE *fp;
1607 	char *file_name;
1608 	GpStatus status;
1609 
1610 	if (!gdiplusInitialized)
1611 		return GdiplusNotInitialized;
1612 
1613 	if (!file || !metafile)
1614 		return InvalidParameter;
1615 
1616 	file_name = (char *) utf16_to_utf8 ((const gunichar2 *)file, -1);
1617 	if (!file_name)
1618 		return InvalidParameter;
1619 
1620 	fp = fopen (file_name, "rb");
1621 	if (!fp) {
1622 		GdipFree (file_name);
1623 		return GenericError;
1624 	}
1625 
1626 	/* Match GDI+ behaviour by either returning Ok or GenericError. */
1627 	status = gdip_get_metafile_from (fp, metafile, File);
1628 	if (status != Ok)
1629 		status = GenericError;
1630 
1631 	fclose (fp);
1632 	GdipFree (file_name);
1633 
1634 	return status;
1635 }
1636 
1637 /*
1638  * GdipCreateMetafileFromStream will never be implemented, as 'stream' is a COM IStream ...
1639  */
1640 GpStatus
GdipCreateMetafileFromStream(void * stream,GpMetafile ** metafile)1641 GdipCreateMetafileFromStream (void *stream, GpMetafile **metafile)
1642 {
1643 	if (!gdiplusInitialized)
1644 		return GdiplusNotInitialized;
1645 
1646 	if (!stream || !metafile)
1647 		return InvalidParameter;
1648 
1649 	return NotImplemented;
1650 }
1651 /*
1652  * instead we'll use delegates to load the metafile header with this function
1653  */
GdipCreateMetafileFromDelegate_linux(GetHeaderDelegate getHeaderFunc,GetBytesDelegate getBytesFunc,PutBytesDelegate putBytesFunc,SeekDelegate seekFunc,CloseDelegate closeFunc,SizeDelegate sizeFunc,GpMetafile ** metafile)1654 GpStatus GdipCreateMetafileFromDelegate_linux (GetHeaderDelegate getHeaderFunc, GetBytesDelegate getBytesFunc,
1655 	PutBytesDelegate putBytesFunc, SeekDelegate seekFunc, CloseDelegate closeFunc, SizeDelegate sizeFunc,
1656 	GpMetafile **metafile)
1657 {
1658 	GpStatus status = InvalidParameter;
1659 	dstream_t *loader;
1660 
1661 	if (!metafile)
1662 		return InvalidParameter;
1663 
1664 	loader = dstream_input_new (getBytesFunc, seekFunc);
1665 	if (loader) {
1666 		status = gdip_get_metafile_from (loader, metafile, DStream);
1667 		dstream_free (loader);
1668 	}
1669 	return status;
1670 }
1671 
1672 GpStatus
GdipCreateMetafileFromEmf(HENHMETAFILE hEmf,BOOL deleteEmf,GpMetafile ** metafile)1673 GdipCreateMetafileFromEmf (HENHMETAFILE hEmf, BOOL deleteEmf, GpMetafile **metafile)
1674 {
1675 	GpStatus status;
1676 
1677 	if (!gdiplusInitialized)
1678 		return GdiplusNotInitialized;
1679 
1680 	if (!hEmf || !metafile)
1681 		return InvalidParameter;
1682 
1683 	switch (((GpMetafile *) hEmf)->metafile_header.Type) {
1684 	case MetafileTypeEmf:
1685 	case MetafileTypeEmfPlusOnly:
1686 	case MetafileTypeEmfPlusDual:
1687 		status = gdip_metafile_clone ((GpMetafile*)hEmf, metafile);
1688 		if (status != Ok)
1689 			return status;
1690 
1691 		(*metafile)->delete = deleteEmf;
1692 		return Ok;
1693 	case MetafileTypeWmfPlaceable:
1694 	case MetafileTypeWmf:
1695 	default:
1696 		*metafile = NULL;
1697 		return GenericError;
1698 	}
1699 }
1700 
1701 GpStatus
GdipCreateMetafileFromWmf(HMETAFILE hWmf,BOOL deleteWmf,GDIPCONST WmfPlaceableFileHeader * wmfPlaceableFileHeader,GpMetafile ** metafile)1702 GdipCreateMetafileFromWmf (HMETAFILE hWmf, BOOL deleteWmf, GDIPCONST WmfPlaceableFileHeader *wmfPlaceableFileHeader, GpMetafile **metafile)
1703 {
1704 	GpStatus status;
1705 
1706 	if (!gdiplusInitialized)
1707 		return GdiplusNotInitialized;
1708 
1709 	if (!hWmf || !metafile)
1710 		return InvalidParameter;
1711 
1712 	status = gdip_metafile_clone ((GpMetafile*)hWmf, metafile);
1713 	if (status != Ok)
1714 		return status;
1715 
1716 	switch ((*metafile)->metafile_header.Type) {
1717 	case MetafileTypeWmfPlaceable:
1718 	case MetafileTypeWmf:
1719 		if (wmfPlaceableFileHeader) {
1720 			status = GdipGetMetafileHeaderFromWmf (hWmf, wmfPlaceableFileHeader, &(*metafile)->metafile_header);
1721 			if (status != Ok) {
1722 				GdipFree (*metafile);
1723 				return status;
1724 			}
1725 		}
1726 
1727 		(*metafile)->delete = deleteWmf;
1728 		return Ok;
1729 	case MetafileTypeEmf:
1730 	case MetafileTypeEmfPlusOnly:
1731 	case MetafileTypeEmfPlusDual:
1732 	default:
1733 		GdipFree (*metafile);
1734 		*metafile = NULL;
1735 		return GenericError;
1736 	}
1737 }
1738 
1739 GpStatus
GdipGetMetafileHeaderFromWmf(HMETAFILE hWmf,GDIPCONST WmfPlaceableFileHeader * wmfPlaceableFileHeader,MetafileHeader * header)1740 GdipGetMetafileHeaderFromWmf (HMETAFILE hWmf, GDIPCONST WmfPlaceableFileHeader *wmfPlaceableFileHeader, MetafileHeader *header)
1741 {
1742 	GpMetafile *mf;
1743 
1744 	if (!hWmf || !wmfPlaceableFileHeader || !header)
1745 		return InvalidParameter;
1746 
1747 	mf = (GpMetafile*)hWmf;
1748 	memcpy (header, &mf->metafile_header, sizeof (MetafileHeader));
1749 	return combine_headers (wmfPlaceableFileHeader, header);
1750 }
1751 
1752 GpStatus
GdipGetMetafileHeaderFromEmf(HENHMETAFILE hEmf,MetafileHeader * header)1753 GdipGetMetafileHeaderFromEmf (HENHMETAFILE hEmf, MetafileHeader *header)
1754 {
1755 	GpMetafile* metafile = (GpMetafile*)hEmf;
1756 
1757 	if (!metafile || !header)
1758 		return InvalidParameter;
1759 
1760 	switch (metafile->metafile_header.Type) {
1761 	case MetafileTypeEmf:
1762 	case MetafileTypeEmfPlusOnly:
1763 	case MetafileTypeEmfPlusDual:
1764 		return GdipGetMetafileHeaderFromMetafile (metafile, header);
1765 	/* you can get a HENHMETAFILE from a WMF metafile but it seems (unit tests) that you can't use it as such */
1766 	case MetafileTypeWmfPlaceable:
1767 	case MetafileTypeWmf:
1768 	default:
1769 		return InvalidParameter;
1770 	}
1771 }
1772 
1773 GpStatus
GdipGetMetafileHeaderFromFile(GDIPCONST WCHAR * filename,MetafileHeader * header)1774 GdipGetMetafileHeaderFromFile (GDIPCONST WCHAR *filename, MetafileHeader *header)
1775 {
1776 	FILE *fp;
1777 	char *file_name;
1778 	GpStatus status = InvalidParameter;
1779 
1780 	if (!filename || !header)
1781 		return InvalidParameter;
1782 
1783 	file_name = (char *) utf16_to_utf8 ((const gunichar2 *)filename, -1);
1784 	if (!file_name)
1785 		return InvalidParameter;
1786 
1787 	fp = fopen (file_name, "rb");
1788 	if (fp) {
1789 		status = gdip_get_metafileheader_from (fp, header, File);
1790 		/* if EMF check for additional header record */
1791 		if (header->Type == MetafileTypeEmf) {
1792 			/* look for more details, possibly upgrading the metafile to MetafileTypeEmfPlusOnly or EMFPLUSDUAL */
1793 			/* TODO */
1794 		}
1795 		fclose (fp);
1796 	}
1797 	GdipFree (file_name);
1798 	return status;
1799 }
1800 
1801 /*
1802  * GdipGetMetafileHeaderFromStream will never be implemented, as 'stream' is a COM IStream ...
1803  */
1804 GpStatus
GdipGetMetafileHeaderFromStream(void * stream,MetafileHeader * header)1805 GdipGetMetafileHeaderFromStream (void *stream, MetafileHeader *header)
1806 {
1807 	if (!gdiplusInitialized)
1808 		return GdiplusNotInitialized;
1809 
1810 	if (!stream || !header)
1811 		return InvalidParameter;
1812 
1813 	return NotImplemented;
1814 }
1815 /*
1816  * instead we'll use delegates to load the metafile header with this function
1817  */
1818 GpStatus
GdipGetMetafileHeaderFromDelegate_linux(GetHeaderDelegate getHeaderFunc,GetBytesDelegate getBytesFunc,PutBytesDelegate putBytesFunc,SeekDelegate seekFunc,CloseDelegate closeFunc,SizeDelegate sizeFunc,MetafileHeader * header)1819 GdipGetMetafileHeaderFromDelegate_linux (GetHeaderDelegate getHeaderFunc, GetBytesDelegate getBytesFunc,
1820 	PutBytesDelegate putBytesFunc, SeekDelegate seekFunc, CloseDelegate closeFunc, SizeDelegate sizeFunc,
1821 	MetafileHeader *header)
1822 {
1823 	GpStatus status = InvalidParameter;
1824 	dstream_t *loader;
1825 
1826 	if (!header)
1827 		return status;
1828 
1829 	loader = dstream_input_new (getBytesFunc, seekFunc);
1830 	if (loader) {
1831 		status = gdip_get_metafileheader_from (loader, header, DStream);
1832 		/* if EMF check for additional header record */
1833 		if (header->Type == MetafileTypeEmf) {
1834 			/* look for more details, possibly upgrading the metafile to MetafileTypeEmfPlusOnly or EMFPLUSDUAL */
1835 			/* TODO */
1836 		}
1837 		dstream_free (loader);
1838 	}
1839 	return status;
1840 }
1841 
1842 GpStatus
GdipGetMetafileHeaderFromMetafile(GpMetafile * metafile,MetafileHeader * header)1843 GdipGetMetafileHeaderFromMetafile (GpMetafile *metafile, MetafileHeader *header)
1844 {
1845 	if (!metafile || !header)
1846 		return InvalidParameter;
1847 
1848 	memcpy (header, &metafile->metafile_header, sizeof (MetafileHeader));
1849 	return Ok;
1850 }
1851 
1852 GpStatus
GdipGetHemfFromMetafile(GpMetafile * metafile,HENHMETAFILE * hEmf)1853 GdipGetHemfFromMetafile (GpMetafile *metafile, HENHMETAFILE *hEmf)
1854 {
1855 	if (!metafile || !hEmf)
1856 		return InvalidParameter;
1857 
1858 	*hEmf = (HENHMETAFILE)metafile;
1859 	return Ok;
1860 }
1861 
1862 GpStatus
GdipGetMetafileDownLevelRasterizationLimit(GpMetafile * metafile,UINT * metafileRasterizationLimitDpi)1863 GdipGetMetafileDownLevelRasterizationLimit (GpMetafile *metafile, UINT *metafileRasterizationLimitDpi)
1864 {
1865 	if (!metafile || !metafileRasterizationLimitDpi)
1866 		return InvalidParameter;
1867 
1868 	switch (metafile->metafile_header.Type) {
1869 	case MetafileTypeEmf:
1870 	case MetafileTypeEmfPlusDual:
1871 		/* TODO */
1872 		*metafileRasterizationLimitDpi = 0;
1873 		return Ok;
1874 	case MetafileTypeWmfPlaceable:
1875 	case MetafileTypeWmf:
1876 	case MetafileTypeEmfPlusOnly:
1877 	default:
1878 		return WrongState;
1879 	}
1880 }
1881 
1882 GpStatus
GdipSetMetafileDownLevelRasterizationLimit(GpMetafile * metafile,UINT metafileRasterizationLimitDpi)1883 GdipSetMetafileDownLevelRasterizationLimit (GpMetafile *metafile, UINT metafileRasterizationLimitDpi)
1884 {
1885 	if (!metafile)
1886 		return InvalidParameter;
1887 
1888 	switch (metafile->metafile_header.Type) {
1889 	case MetafileTypeEmf:
1890 	case MetafileTypeEmfPlusDual:
1891 		// values less than 10 left resolution unchanged (from GDI+ documentation)
1892 		if (metafileRasterizationLimitDpi >= 10) {
1893 			/* TODO */
1894 		}
1895 		return Ok;
1896 	case MetafileTypeWmfPlaceable:
1897 	case MetafileTypeWmf:
1898 	case MetafileTypeEmfPlusOnly:
1899 	default:
1900 		return WrongState;
1901 	}
1902 }
1903 
1904 GpStatus
GdipPlayMetafileRecord(GDIPCONST GpMetafile * metafile,EmfPlusRecordType recordType,UINT flags,UINT dataSize,GDIPCONST BYTE * data)1905 GdipPlayMetafileRecord (GDIPCONST GpMetafile *metafile, EmfPlusRecordType recordType, UINT flags, UINT dataSize, GDIPCONST BYTE* data)
1906 {
1907 	if (!metafile || (dataSize && !data))
1908 		return InvalidParameter;
1909 
1910 	/* TODO */
1911 	return NotImplemented;
1912 }
1913 
1914 GpStatus
GdipRecordMetafile(HDC referenceHdc,EmfType type,GDIPCONST GpRectF * frameRect,MetafileFrameUnit frameUnit,GDIPCONST WCHAR * description,GpMetafile ** metafile)1915 GdipRecordMetafile (HDC referenceHdc, EmfType type, GDIPCONST GpRectF *frameRect, MetafileFrameUnit frameUnit,
1916 	GDIPCONST WCHAR *description, GpMetafile **metafile)
1917 {
1918 	GpMetafile *mf;
1919 
1920 	if (!gdiplusInitialized)
1921 		return GdiplusNotInitialized;
1922 
1923 	if (!referenceHdc || !frameRect || !metafile)
1924 		return InvalidParameter;
1925 
1926 	if ((type < EmfTypeEmfOnly) || (type > EmfTypeEmfPlusDual))
1927 		return InvalidParameter;
1928 
1929 	if ((frameUnit < MetafileFrameUnitPixel) || (frameUnit > MetafileFrameUnitGdi))
1930 		return InvalidParameter;
1931 
1932 	if (((frameRect->Width == 0) || (frameRect->Height == 0)) && (frameUnit != MetafileFrameUnitGdi))
1933 		return GenericError;
1934 
1935 	mf = gdip_metafile_create ();
1936 	if (!mf)
1937 		return OutOfMemory;
1938 
1939 	mf->metafile_header.X = frameRect->X;
1940 	mf->metafile_header.Y = frameRect->Y;
1941 	mf->metafile_header.Width = frameRect->Width;
1942 	mf->metafile_header.Height = frameRect->Height;
1943 	mf->metafile_header.Size = 0;
1944 	mf->metafile_header.Type = (MetafileType)type;
1945 	mf->recording = TRUE;
1946 
1947 	/* TODO - more stuff here! */
1948 
1949 	*metafile = mf;
1950 	return Ok;
1951 }
1952 
1953 GpStatus
GdipRecordMetafileI(HDC referenceHdc,EmfType type,GDIPCONST GpRect * frameRect,MetafileFrameUnit frameUnit,GDIPCONST WCHAR * description,GpMetafile ** metafile)1954 GdipRecordMetafileI (HDC referenceHdc, EmfType type, GDIPCONST GpRect *frameRect, MetafileFrameUnit frameUnit,
1955 	GDIPCONST WCHAR *description, GpMetafile **metafile)
1956 {
1957 	GpRectF rect;
1958 
1959 	if (!frameRect)
1960 		return InvalidParameter;
1961 
1962 	rect.X = frameRect->X;
1963 	rect.Y = frameRect->Y;
1964 	rect.Width = frameRect->Width;
1965 	rect.Height = frameRect->Height;
1966 	return GdipRecordMetafile (referenceHdc, type, (GDIPCONST GpRectF*) &rect, frameUnit, description, metafile);
1967 }
1968 
1969 GpStatus
GdipRecordMetafileFileName(GDIPCONST WCHAR * fileName,HDC referenceHdc,EmfType type,GDIPCONST GpRectF * frameRect,MetafileFrameUnit frameUnit,GDIPCONST WCHAR * description,GpMetafile ** metafile)1970 GdipRecordMetafileFileName (GDIPCONST WCHAR *fileName, HDC referenceHdc, EmfType type, GDIPCONST GpRectF *frameRect,
1971 	MetafileFrameUnit frameUnit, GDIPCONST WCHAR *description, GpMetafile **metafile)
1972 {
1973 	GpStatus status;
1974 	GpMetafile *mf = NULL;
1975 	char *file_name;
1976 
1977 	if (!gdiplusInitialized)
1978 		return GdiplusNotInitialized;
1979 
1980 	if (!fileName)
1981 		return InvalidParameter;
1982 
1983 	file_name = (char *) utf16_to_utf8 ((const gunichar2 *)fileName, -1);
1984 	if (!file_name) {
1985 		*metafile = NULL;
1986 		return InvalidParameter;
1987 	}
1988 
1989 	status = GdipRecordMetafile (referenceHdc, type, frameRect, frameUnit, description, &mf);
1990 	if (status != Ok) {
1991 		GdipFree (file_name);
1992 		return status;
1993 	}
1994 
1995 	/* yep, an existing file is overwritten */
1996 	mf->fp = fopen (file_name, "wb");
1997 
1998 	GdipFree (file_name);
1999 	*metafile = mf;
2000 	return Ok;
2001 }
2002 
2003 GpStatus
GdipRecordMetafileFileNameI(GDIPCONST WCHAR * fileName,HDC referenceHdc,EmfType type,GDIPCONST GpRect * frameRect,MetafileFrameUnit frameUnit,GDIPCONST WCHAR * description,GpMetafile ** metafile)2004 GdipRecordMetafileFileNameI (GDIPCONST WCHAR *fileName, HDC referenceHdc, EmfType type, GDIPCONST GpRect *frameRect,
2005 	MetafileFrameUnit frameUnit, GDIPCONST WCHAR *description, GpMetafile **metafile)
2006 {
2007 	GpRectF rect;
2008 
2009 	if (!frameRect)
2010 		return InvalidParameter;
2011 
2012 	rect.X = frameRect->X;
2013 	rect.Y = frameRect->Y;
2014 	rect.Width = frameRect->Width;
2015 	rect.Height = frameRect->Height;
2016 	return GdipRecordMetafileFileName (fileName, referenceHdc, type, (GDIPCONST GpRectF*) &rect, frameUnit, description, metafile);
2017 }
2018 
2019 /*
2020  * GdipRecordMetafileStream and GdipRecordMetafileStreamI will never be implemented, as 'stream' is a COM IStream ...
2021  */
2022 GpStatus
GdipRecordMetafileStream(void * stream,HDC referenceHdc,EmfType type,GDIPCONST GpRectF * frameRect,MetafileFrameUnit frameUnit,GDIPCONST WCHAR * description,GpMetafile ** metafile)2023 GdipRecordMetafileStream (void /* IStream */ *stream, HDC referenceHdc, EmfType type, GDIPCONST GpRectF *frameRect,
2024 	MetafileFrameUnit frameUnit, GDIPCONST WCHAR *description, GpMetafile **metafile)
2025 {
2026 	if (!gdiplusInitialized)
2027 		return GdiplusNotInitialized;
2028 
2029 	return NotImplemented;
2030 }
2031 
2032 GpStatus
GdipRecordMetafileStreamI(void * stream,HDC referenceHdc,EmfType type,GDIPCONST GpRect * frameRect,MetafileFrameUnit frameUnit,GDIPCONST WCHAR * description,GpMetafile ** metafile)2033 GdipRecordMetafileStreamI (void /* IStream */ *stream, HDC referenceHdc, EmfType type, GDIPCONST GpRect *frameRect,
2034 	MetafileFrameUnit frameUnit, GDIPCONST WCHAR *description, GpMetafile **metafile)
2035 {
2036 	return NotImplemented;
2037 }
2038 /*
2039  * instead we'll use delegates to create the metafile header with these functions
2040  */
2041 GpStatus
GdipRecordMetafileFromDelegate_linux(GetHeaderDelegate getHeaderFunc,GetBytesDelegate getBytesFunc,PutBytesDelegate putBytesFunc,SeekDelegate seekFunc,CloseDelegate closeFunc,SizeDelegate sizeFunc,HDC referenceHdc,EmfType type,GDIPCONST GpRectF * frameRect,MetafileFrameUnit frameUnit,GDIPCONST WCHAR * description,GpMetafile ** metafile)2042 GdipRecordMetafileFromDelegate_linux (GetHeaderDelegate getHeaderFunc, GetBytesDelegate getBytesFunc,
2043 	PutBytesDelegate putBytesFunc, SeekDelegate seekFunc, CloseDelegate closeFunc, SizeDelegate sizeFunc,
2044 	HDC referenceHdc, EmfType type, GDIPCONST GpRectF *frameRect,
2045 	MetafileFrameUnit frameUnit, GDIPCONST WCHAR *description, GpMetafile **metafile)
2046 {
2047 	GpStatus status;
2048 
2049 	if (!putBytesFunc)
2050 		return InvalidParameter;
2051 
2052 	status = GdipRecordMetafile (referenceHdc, type, frameRect, frameUnit, description, metafile);
2053 	if (status != Ok)
2054 		return status;
2055 
2056 	/* TODO - keep delegates around to write stuff */
2057 
2058 	return Ok;
2059 }
2060 
2061 GpStatus
GdipRecordMetafileFromDelegateI_linux(GetHeaderDelegate getHeaderFunc,GetBytesDelegate getBytesFunc,PutBytesDelegate putBytesFunc,SeekDelegate seekFunc,CloseDelegate closeFunc,SizeDelegate sizeFunc,HDC referenceHdc,EmfType type,GDIPCONST GpRect * frameRect,MetafileFrameUnit frameUnit,GDIPCONST WCHAR * description,GpMetafile ** metafile)2062 GdipRecordMetafileFromDelegateI_linux (GetHeaderDelegate getHeaderFunc, GetBytesDelegate getBytesFunc,
2063 	PutBytesDelegate putBytesFunc, SeekDelegate seekFunc, CloseDelegate closeFunc, SizeDelegate sizeFunc,
2064 	HDC referenceHdc, EmfType type, GDIPCONST GpRect *frameRect,
2065 	MetafileFrameUnit frameUnit, GDIPCONST WCHAR *description, GpMetafile **metafile)
2066 {
2067 	GpRectF rect;
2068 
2069 	if (!frameRect)
2070 		return InvalidParameter;
2071 
2072 	rect.X = frameRect->X;
2073 	rect.Y = frameRect->Y;
2074 	rect.Width = frameRect->Width;
2075 	rect.Height = frameRect->Height;
2076 	return GdipRecordMetafileFromDelegate_linux (getHeaderFunc, getBytesFunc, putBytesFunc, seekFunc, closeFunc, sizeFunc,
2077 		referenceHdc, type, (GDIPCONST GpRectF*) &rect, frameUnit, description, metafile);
2078 }
2079