hid: Stub HidP_TranslateUsagesToI8042ScanCodes.
[wine.git] / dlls / gdiplus / metafile.c
blob9de584c37bed864606d2772efffc0df884264992
1 /*
2 * Copyright (C) 2011 Vincent Povirk for CodeWeavers
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 #include <stdarg.h>
20 #include <math.h>
22 #include "windef.h"
23 #include "winbase.h"
24 #include "wingdi.h"
25 #include "wine/unicode.h"
27 #define COBJMACROS
28 #include "objbase.h"
29 #include "ocidl.h"
30 #include "olectl.h"
31 #include "ole2.h"
33 #include "winreg.h"
34 #include "shlwapi.h"
36 #include "gdiplus.h"
37 #include "gdiplus_private.h"
38 #include "wine/debug.h"
39 #include "wine/list.h"
41 WINE_DEFAULT_DEBUG_CHANNEL(gdiplus);
43 typedef struct EmfPlusRecordHeader
45 WORD Type;
46 WORD Flags;
47 DWORD Size;
48 DWORD DataSize;
49 } EmfPlusRecordHeader;
51 typedef struct EmfPlusHeader
53 EmfPlusRecordHeader Header;
54 DWORD Version;
55 DWORD EmfPlusFlags;
56 DWORD LogicalDpiX;
57 DWORD LogicalDpiY;
58 } EmfPlusHeader;
60 typedef struct EmfPlusClear
62 EmfPlusRecordHeader Header;
63 DWORD Color;
64 } EmfPlusClear;
66 typedef struct EmfPlusFillRects
68 EmfPlusRecordHeader Header;
69 DWORD BrushID;
70 DWORD Count;
71 } EmfPlusFillRects;
73 typedef struct EmfPlusSetClipRect
75 EmfPlusRecordHeader Header;
76 GpRectF ClipRect;
77 } EmfPlusSetClipRect;
79 typedef struct EmfPlusSetPageTransform
81 EmfPlusRecordHeader Header;
82 REAL PageScale;
83 } EmfPlusSetPageTransform;
85 typedef struct EmfPlusRect
87 SHORT X;
88 SHORT Y;
89 SHORT Width;
90 SHORT Height;
91 } EmfPlusRect;
93 typedef struct EmfPlusSetWorldTransform
95 EmfPlusRecordHeader Header;
96 REAL MatrixData[6];
97 } EmfPlusSetWorldTransform;
99 typedef struct EmfPlusScaleWorldTransform
101 EmfPlusRecordHeader Header;
102 REAL Sx;
103 REAL Sy;
104 } EmfPlusScaleWorldTransform;
106 typedef struct EmfPlusMultiplyWorldTransform
108 EmfPlusRecordHeader Header;
109 REAL MatrixData[6];
110 } EmfPlusMultiplyWorldTransform;
112 typedef struct EmfPlusRotateWorldTransform
114 EmfPlusRecordHeader Header;
115 REAL Angle;
116 } EmfPlusRotateWorldTransform;
118 typedef struct EmfPlusTranslateWorldTransform
120 EmfPlusRecordHeader Header;
121 REAL dx;
122 REAL dy;
123 } EmfPlusTranslateWorldTransform;
125 typedef struct EmfPlusBeginContainer
127 EmfPlusRecordHeader Header;
128 GpRectF DestRect;
129 GpRectF SrcRect;
130 DWORD StackIndex;
131 } EmfPlusBeginContainer;
133 typedef struct EmfPlusContainerRecord
135 EmfPlusRecordHeader Header;
136 DWORD StackIndex;
137 } EmfPlusContainerRecord;
139 enum container_type
141 BEGIN_CONTAINER,
142 SAVE_GRAPHICS
145 typedef struct container
147 struct list entry;
148 DWORD id;
149 enum container_type type;
150 GraphicsContainer state;
151 GpMatrix world_transform;
152 GpUnit page_unit;
153 REAL page_scale;
154 GpRegion *clip;
155 } container;
157 static GpStatus METAFILE_AllocateRecord(GpMetafile *metafile, DWORD size, void **result)
159 DWORD size_needed;
160 EmfPlusRecordHeader *record;
162 if (!metafile->comment_data_size)
164 DWORD data_size = max(256, size * 2 + 4);
165 metafile->comment_data = heap_alloc_zero(data_size);
167 if (!metafile->comment_data)
168 return OutOfMemory;
170 memcpy(metafile->comment_data, "EMF+", 4);
172 metafile->comment_data_size = data_size;
173 metafile->comment_data_length = 4;
176 size_needed = size + metafile->comment_data_length;
178 if (size_needed > metafile->comment_data_size)
180 DWORD data_size = size_needed * 2;
181 BYTE *new_data = heap_alloc_zero(data_size);
183 if (!new_data)
184 return OutOfMemory;
186 memcpy(new_data, metafile->comment_data, metafile->comment_data_length);
188 metafile->comment_data_size = data_size;
189 heap_free(metafile->comment_data);
190 metafile->comment_data = new_data;
193 *result = metafile->comment_data + metafile->comment_data_length;
194 metafile->comment_data_length += size;
196 record = (EmfPlusRecordHeader*)*result;
197 record->Size = size;
198 record->DataSize = size - sizeof(EmfPlusRecordHeader);
200 return Ok;
203 static void METAFILE_WriteRecords(GpMetafile *metafile)
205 if (metafile->comment_data_length > 4)
207 GdiComment(metafile->record_dc, metafile->comment_data_length, metafile->comment_data);
208 metafile->comment_data_length = 4;
212 static GpStatus METAFILE_WriteHeader(GpMetafile *metafile, HDC hdc)
214 GpStatus stat;
216 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
218 EmfPlusHeader *header;
220 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusHeader), (void**)&header);
221 if (stat != Ok)
222 return stat;
224 header->Header.Type = EmfPlusRecordTypeHeader;
226 if (metafile->metafile_type == MetafileTypeEmfPlusDual)
227 header->Header.Flags = 1;
228 else
229 header->Header.Flags = 0;
231 header->Version = 0xDBC01002;
233 if (GetDeviceCaps(hdc, TECHNOLOGY) == DT_RASDISPLAY)
234 header->EmfPlusFlags = 1;
235 else
236 header->EmfPlusFlags = 0;
238 header->LogicalDpiX = GetDeviceCaps(hdc, LOGPIXELSX);
239 header->LogicalDpiY = GetDeviceCaps(hdc, LOGPIXELSY);
241 METAFILE_WriteRecords(metafile);
244 return Ok;
247 static GpStatus METAFILE_WriteEndOfFile(GpMetafile *metafile)
249 GpStatus stat;
251 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
253 EmfPlusRecordHeader *record;
255 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusRecordHeader), (void**)&record);
256 if (stat != Ok)
257 return stat;
259 record->Type = EmfPlusRecordTypeEndOfFile;
260 record->Flags = 0;
262 METAFILE_WriteRecords(metafile);
265 return Ok;
268 GpStatus WINGDIPAPI GdipRecordMetafile(HDC hdc, EmfType type, GDIPCONST GpRectF *frameRect,
269 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
271 HDC record_dc;
272 REAL dpix, dpiy;
273 REAL framerect_factor_x, framerect_factor_y;
274 RECT rc, *lprc;
275 GpStatus stat;
277 TRACE("(%p %d %p %d %p %p)\n", hdc, type, frameRect, frameUnit, desc, metafile);
279 if (!hdc || type < EmfTypeEmfOnly || type > EmfTypeEmfPlusDual || !metafile)
280 return InvalidParameter;
282 dpix = (REAL)GetDeviceCaps(hdc, HORZRES) / GetDeviceCaps(hdc, HORZSIZE) * 25.4;
283 dpiy = (REAL)GetDeviceCaps(hdc, VERTRES) / GetDeviceCaps(hdc, VERTSIZE) * 25.4;
285 if (frameRect)
287 switch (frameUnit)
289 case MetafileFrameUnitPixel:
290 framerect_factor_x = 2540.0 / dpix;
291 framerect_factor_y = 2540.0 / dpiy;
292 break;
293 case MetafileFrameUnitPoint:
294 framerect_factor_x = framerect_factor_y = 2540.0 / 72.0;
295 break;
296 case MetafileFrameUnitInch:
297 framerect_factor_x = framerect_factor_y = 2540.0;
298 break;
299 case MetafileFrameUnitDocument:
300 framerect_factor_x = framerect_factor_y = 2540.0 / 300.0;
301 break;
302 case MetafileFrameUnitMillimeter:
303 framerect_factor_x = framerect_factor_y = 100.0;
304 break;
305 case MetafileFrameUnitGdi:
306 framerect_factor_x = framerect_factor_y = 1.0;
307 break;
308 default:
309 return InvalidParameter;
312 rc.left = framerect_factor_x * frameRect->X;
313 rc.top = framerect_factor_y * frameRect->Y;
314 rc.right = rc.left + framerect_factor_x * frameRect->Width;
315 rc.bottom = rc.top + framerect_factor_y * frameRect->Height;
317 lprc = &rc;
319 else
320 lprc = NULL;
322 record_dc = CreateEnhMetaFileW(hdc, NULL, lprc, desc);
324 if (!record_dc)
325 return GenericError;
327 *metafile = heap_alloc_zero(sizeof(GpMetafile));
328 if(!*metafile)
330 DeleteEnhMetaFile(CloseEnhMetaFile(record_dc));
331 return OutOfMemory;
334 (*metafile)->image.type = ImageTypeMetafile;
335 (*metafile)->image.flags = ImageFlagsNone;
336 (*metafile)->image.palette = NULL;
337 (*metafile)->image.xres = dpix;
338 (*metafile)->image.yres = dpiy;
339 (*metafile)->bounds.X = (*metafile)->bounds.Y = 0.0;
340 (*metafile)->bounds.Width = (*metafile)->bounds.Height = 1.0;
341 (*metafile)->unit = UnitPixel;
342 (*metafile)->metafile_type = type;
343 (*metafile)->record_dc = record_dc;
344 (*metafile)->comment_data = NULL;
345 (*metafile)->comment_data_size = 0;
346 (*metafile)->comment_data_length = 0;
347 (*metafile)->hemf = NULL;
348 list_init(&(*metafile)->containers);
350 if (!frameRect)
352 (*metafile)->auto_frame = TRUE;
353 (*metafile)->auto_frame_min.X = 0;
354 (*metafile)->auto_frame_min.Y = 0;
355 (*metafile)->auto_frame_max.X = -1;
356 (*metafile)->auto_frame_max.Y = -1;
359 stat = METAFILE_WriteHeader(*metafile, hdc);
361 if (stat != Ok)
363 DeleteEnhMetaFile(CloseEnhMetaFile(record_dc));
364 heap_free(*metafile);
365 *metafile = NULL;
366 return OutOfMemory;
369 return stat;
372 /*****************************************************************************
373 * GdipRecordMetafileI [GDIPLUS.@]
375 GpStatus WINGDIPAPI GdipRecordMetafileI(HDC hdc, EmfType type, GDIPCONST GpRect *frameRect,
376 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
378 GpRectF frameRectF, *pFrameRectF;
380 TRACE("(%p %d %p %d %p %p)\n", hdc, type, frameRect, frameUnit, desc, metafile);
382 if (frameRect)
384 frameRectF.X = frameRect->X;
385 frameRectF.Y = frameRect->Y;
386 frameRectF.Width = frameRect->Width;
387 frameRectF.Height = frameRect->Height;
388 pFrameRectF = &frameRectF;
390 else
391 pFrameRectF = NULL;
393 return GdipRecordMetafile(hdc, type, pFrameRectF, frameUnit, desc, metafile);
396 GpStatus WINGDIPAPI GdipRecordMetafileStream(IStream *stream, HDC hdc, EmfType type, GDIPCONST GpRectF *frameRect,
397 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
399 GpStatus stat;
401 TRACE("(%p %p %d %p %d %p %p)\n", stream, hdc, type, frameRect, frameUnit, desc, metafile);
403 if (!stream)
404 return InvalidParameter;
406 stat = GdipRecordMetafile(hdc, type, frameRect, frameUnit, desc, metafile);
408 if (stat == Ok)
410 (*metafile)->record_stream = stream;
411 IStream_AddRef(stream);
414 return stat;
417 static void METAFILE_AdjustFrame(GpMetafile* metafile, const GpPointF *points,
418 UINT num_points)
420 int i;
422 if (!metafile->auto_frame || !num_points)
423 return;
425 if (metafile->auto_frame_max.X < metafile->auto_frame_min.X)
426 metafile->auto_frame_max = metafile->auto_frame_min = points[0];
428 for (i=0; i<num_points; i++)
430 if (points[i].X < metafile->auto_frame_min.X)
431 metafile->auto_frame_min.X = points[i].X;
432 if (points[i].X > metafile->auto_frame_max.X)
433 metafile->auto_frame_max.X = points[i].X;
434 if (points[i].Y < metafile->auto_frame_min.Y)
435 metafile->auto_frame_min.Y = points[i].Y;
436 if (points[i].Y > metafile->auto_frame_max.Y)
437 metafile->auto_frame_max.Y = points[i].Y;
441 GpStatus METAFILE_GetGraphicsContext(GpMetafile* metafile, GpGraphics **result)
443 GpStatus stat;
445 if (!metafile->record_dc || metafile->record_graphics)
446 return InvalidParameter;
448 stat = graphics_from_image((GpImage*)metafile, &metafile->record_graphics);
450 if (stat == Ok)
452 *result = metafile->record_graphics;
453 metafile->record_graphics->xres = 96.0;
454 metafile->record_graphics->yres = 96.0;
457 return stat;
460 GpStatus METAFILE_GetDC(GpMetafile* metafile, HDC *hdc)
462 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
464 EmfPlusRecordHeader *record;
465 GpStatus stat;
467 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusRecordHeader), (void**)&record);
468 if (stat != Ok)
469 return stat;
471 record->Type = EmfPlusRecordTypeGetDC;
472 record->Flags = 0;
474 METAFILE_WriteRecords(metafile);
477 *hdc = metafile->record_dc;
479 return Ok;
482 GpStatus METAFILE_GraphicsClear(GpMetafile* metafile, ARGB color)
484 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
486 EmfPlusClear *record;
487 GpStatus stat;
489 stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusClear), (void**)&record);
490 if (stat != Ok)
491 return stat;
493 record->Header.Type = EmfPlusRecordTypeClear;
494 record->Header.Flags = 0;
495 record->Color = color;
497 METAFILE_WriteRecords(metafile);
500 return Ok;
503 static BOOL is_integer_rect(const GpRectF *rect)
505 SHORT x, y, width, height;
506 x = rect->X;
507 y = rect->Y;
508 width = rect->Width;
509 height = rect->Height;
510 if (rect->X != (REAL)x || rect->Y != (REAL)y ||
511 rect->Width != (REAL)width || rect->Height != (REAL)height)
512 return FALSE;
513 return TRUE;
516 GpStatus METAFILE_FillRectangles(GpMetafile* metafile, GpBrush* brush,
517 GDIPCONST GpRectF* rects, INT count)
519 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
521 EmfPlusFillRects *record;
522 GpStatus stat;
523 BOOL integer_rects = TRUE;
524 int i;
525 DWORD brushid;
526 int flags = 0;
528 if (brush->bt == BrushTypeSolidColor)
530 flags |= 0x8000;
531 brushid = ((GpSolidFill*)brush)->color;
533 else
535 FIXME("brush serialization not implemented\n");
536 return NotImplemented;
539 for (i=0; i<count; i++)
541 if (!is_integer_rect(&rects[i]))
543 integer_rects = FALSE;
544 break;
548 if (integer_rects)
549 flags |= 0x4000;
551 stat = METAFILE_AllocateRecord(metafile,
552 sizeof(EmfPlusFillRects) + count * (integer_rects ? sizeof(EmfPlusRect) : sizeof(GpRectF)),
553 (void**)&record);
554 if (stat != Ok)
555 return stat;
557 record->Header.Type = EmfPlusRecordTypeFillRects;
558 record->Header.Flags = flags;
559 record->BrushID = brushid;
560 record->Count = count;
562 if (integer_rects)
564 EmfPlusRect *record_rects = (EmfPlusRect*)(record+1);
565 for (i=0; i<count; i++)
567 record_rects[i].X = (SHORT)rects[i].X;
568 record_rects[i].Y = (SHORT)rects[i].Y;
569 record_rects[i].Width = (SHORT)rects[i].Width;
570 record_rects[i].Height = (SHORT)rects[i].Height;
573 else
574 memcpy(record+1, rects, sizeof(GpRectF) * count);
576 METAFILE_WriteRecords(metafile);
579 if (metafile->auto_frame)
581 GpPointF corners[4];
582 int i;
584 for (i=0; i<count; i++)
586 corners[0].X = rects[i].X;
587 corners[0].Y = rects[i].Y;
588 corners[1].X = rects[i].X + rects[i].Width;
589 corners[1].Y = rects[i].Y;
590 corners[2].X = rects[i].X;
591 corners[2].Y = rects[i].Y + rects[i].Height;
592 corners[3].X = rects[i].X + rects[i].Width;
593 corners[3].Y = rects[i].Y + rects[i].Height;
595 GdipTransformPoints(metafile->record_graphics, CoordinateSpaceDevice,
596 CoordinateSpaceWorld, corners, 4);
598 METAFILE_AdjustFrame(metafile, corners, 4);
602 return Ok;
605 GpStatus METAFILE_SetClipRect(GpMetafile* metafile, REAL x, REAL y, REAL width, REAL height, CombineMode mode)
607 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
609 EmfPlusSetClipRect *record;
610 GpStatus stat;
612 stat = METAFILE_AllocateRecord(metafile,
613 sizeof(EmfPlusSetClipRect),
614 (void**)&record);
615 if (stat != Ok)
616 return stat;
618 record->Header.Type = EmfPlusRecordTypeSetClipRect;
619 record->Header.Flags = (mode & 0xf) << 8;
620 record->ClipRect.X = x;
621 record->ClipRect.Y = y;
622 record->ClipRect.Width = width;
623 record->ClipRect.Height = height;
625 METAFILE_WriteRecords(metafile);
628 return Ok;
631 GpStatus METAFILE_SetPageTransform(GpMetafile* metafile, GpUnit unit, REAL scale)
633 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
635 EmfPlusSetPageTransform *record;
636 GpStatus stat;
638 stat = METAFILE_AllocateRecord(metafile,
639 sizeof(EmfPlusSetPageTransform),
640 (void**)&record);
641 if (stat != Ok)
642 return stat;
644 record->Header.Type = EmfPlusRecordTypeSetPageTransform;
645 record->Header.Flags = unit;
646 record->PageScale = scale;
648 METAFILE_WriteRecords(metafile);
651 return Ok;
654 GpStatus METAFILE_SetWorldTransform(GpMetafile* metafile, GDIPCONST GpMatrix* transform)
656 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
658 EmfPlusSetWorldTransform *record;
659 GpStatus stat;
661 stat = METAFILE_AllocateRecord(metafile,
662 sizeof(EmfPlusSetWorldTransform),
663 (void**)&record);
664 if (stat != Ok)
665 return stat;
667 record->Header.Type = EmfPlusRecordTypeSetWorldTransform;
668 record->Header.Flags = 0;
669 memcpy(record->MatrixData, transform->matrix, sizeof(record->MatrixData));
671 METAFILE_WriteRecords(metafile);
674 return Ok;
677 GpStatus METAFILE_ScaleWorldTransform(GpMetafile* metafile, REAL sx, REAL sy, MatrixOrder order)
679 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
681 EmfPlusScaleWorldTransform *record;
682 GpStatus stat;
684 stat = METAFILE_AllocateRecord(metafile,
685 sizeof(EmfPlusScaleWorldTransform),
686 (void**)&record);
687 if (stat != Ok)
688 return stat;
690 record->Header.Type = EmfPlusRecordTypeScaleWorldTransform;
691 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
692 record->Sx = sx;
693 record->Sy = sy;
695 METAFILE_WriteRecords(metafile);
698 return Ok;
701 GpStatus METAFILE_MultiplyWorldTransform(GpMetafile* metafile, GDIPCONST GpMatrix* matrix, MatrixOrder order)
703 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
705 EmfPlusMultiplyWorldTransform *record;
706 GpStatus stat;
708 stat = METAFILE_AllocateRecord(metafile,
709 sizeof(EmfPlusMultiplyWorldTransform),
710 (void**)&record);
711 if (stat != Ok)
712 return stat;
714 record->Header.Type = EmfPlusRecordTypeMultiplyWorldTransform;
715 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
716 memcpy(record->MatrixData, matrix->matrix, sizeof(record->MatrixData));
718 METAFILE_WriteRecords(metafile);
721 return Ok;
724 GpStatus METAFILE_RotateWorldTransform(GpMetafile* metafile, REAL angle, MatrixOrder order)
726 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
728 EmfPlusRotateWorldTransform *record;
729 GpStatus stat;
731 stat = METAFILE_AllocateRecord(metafile,
732 sizeof(EmfPlusRotateWorldTransform),
733 (void**)&record);
734 if (stat != Ok)
735 return stat;
737 record->Header.Type = EmfPlusRecordTypeRotateWorldTransform;
738 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
739 record->Angle = angle;
741 METAFILE_WriteRecords(metafile);
744 return Ok;
747 GpStatus METAFILE_TranslateWorldTransform(GpMetafile* metafile, REAL dx, REAL dy, MatrixOrder order)
749 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
751 EmfPlusTranslateWorldTransform *record;
752 GpStatus stat;
754 stat = METAFILE_AllocateRecord(metafile,
755 sizeof(EmfPlusTranslateWorldTransform),
756 (void**)&record);
757 if (stat != Ok)
758 return stat;
760 record->Header.Type = EmfPlusRecordTypeTranslateWorldTransform;
761 record->Header.Flags = (order == MatrixOrderAppend ? 0x2000 : 0);
762 record->dx = dx;
763 record->dy = dy;
765 METAFILE_WriteRecords(metafile);
768 return Ok;
771 GpStatus METAFILE_ResetWorldTransform(GpMetafile* metafile)
773 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
775 EmfPlusRecordHeader *record;
776 GpStatus stat;
778 stat = METAFILE_AllocateRecord(metafile,
779 sizeof(EmfPlusRecordHeader),
780 (void**)&record);
781 if (stat != Ok)
782 return stat;
784 record->Type = EmfPlusRecordTypeResetWorldTransform;
785 record->Flags = 0;
787 METAFILE_WriteRecords(metafile);
790 return Ok;
793 GpStatus METAFILE_BeginContainer(GpMetafile* metafile, GDIPCONST GpRectF *dstrect,
794 GDIPCONST GpRectF *srcrect, GpUnit unit, DWORD StackIndex)
796 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
798 EmfPlusBeginContainer *record;
799 GpStatus stat;
801 stat = METAFILE_AllocateRecord(metafile, sizeof(*record), (void**)&record);
802 if (stat != Ok)
803 return stat;
805 record->Header.Type = EmfPlusRecordTypeBeginContainer;
806 record->Header.Flags = unit & 0xff;
807 record->DestRect = *dstrect;
808 record->SrcRect = *srcrect;
809 record->StackIndex = StackIndex;
811 METAFILE_WriteRecords(metafile);
814 return Ok;
817 GpStatus METAFILE_BeginContainerNoParams(GpMetafile* metafile, DWORD StackIndex)
819 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
821 EmfPlusContainerRecord *record;
822 GpStatus stat;
824 stat = METAFILE_AllocateRecord(metafile,
825 sizeof(EmfPlusContainerRecord),
826 (void**)&record);
827 if (stat != Ok)
828 return stat;
830 record->Header.Type = EmfPlusRecordTypeBeginContainerNoParams;
831 record->Header.Flags = 0;
832 record->StackIndex = StackIndex;
834 METAFILE_WriteRecords(metafile);
837 return Ok;
840 GpStatus METAFILE_EndContainer(GpMetafile* metafile, DWORD StackIndex)
842 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
844 EmfPlusContainerRecord *record;
845 GpStatus stat;
847 stat = METAFILE_AllocateRecord(metafile,
848 sizeof(EmfPlusContainerRecord),
849 (void**)&record);
850 if (stat != Ok)
851 return stat;
853 record->Header.Type = EmfPlusRecordTypeEndContainer;
854 record->Header.Flags = 0;
855 record->StackIndex = StackIndex;
857 METAFILE_WriteRecords(metafile);
860 return Ok;
863 GpStatus METAFILE_SaveGraphics(GpMetafile* metafile, DWORD StackIndex)
865 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
867 EmfPlusContainerRecord *record;
868 GpStatus stat;
870 stat = METAFILE_AllocateRecord(metafile,
871 sizeof(EmfPlusContainerRecord),
872 (void**)&record);
873 if (stat != Ok)
874 return stat;
876 record->Header.Type = EmfPlusRecordTypeSave;
877 record->Header.Flags = 0;
878 record->StackIndex = StackIndex;
880 METAFILE_WriteRecords(metafile);
883 return Ok;
886 GpStatus METAFILE_RestoreGraphics(GpMetafile* metafile, DWORD StackIndex)
888 if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
890 EmfPlusContainerRecord *record;
891 GpStatus stat;
893 stat = METAFILE_AllocateRecord(metafile,
894 sizeof(EmfPlusContainerRecord),
895 (void**)&record);
896 if (stat != Ok)
897 return stat;
899 record->Header.Type = EmfPlusRecordTypeRestore;
900 record->Header.Flags = 0;
901 record->StackIndex = StackIndex;
903 METAFILE_WriteRecords(metafile);
906 return Ok;
909 GpStatus METAFILE_ReleaseDC(GpMetafile* metafile, HDC hdc)
911 if (hdc != metafile->record_dc)
912 return InvalidParameter;
914 return Ok;
917 GpStatus METAFILE_GraphicsDeleted(GpMetafile* metafile)
919 GpStatus stat;
921 stat = METAFILE_WriteEndOfFile(metafile);
922 metafile->record_graphics = NULL;
924 metafile->hemf = CloseEnhMetaFile(metafile->record_dc);
925 metafile->record_dc = NULL;
927 heap_free(metafile->comment_data);
928 metafile->comment_data = NULL;
929 metafile->comment_data_size = 0;
931 if (stat == Ok)
933 MetafileHeader header;
935 stat = GdipGetMetafileHeaderFromEmf(metafile->hemf, &header);
936 if (stat == Ok && metafile->auto_frame &&
937 metafile->auto_frame_max.X >= metafile->auto_frame_min.X)
939 RECTL bounds_rc, gdi_bounds_rc;
940 REAL x_scale = 2540.0 / header.DpiX;
941 REAL y_scale = 2540.0 / header.DpiY;
942 BYTE* buffer;
943 UINT buffer_size;
945 bounds_rc.left = floorf(metafile->auto_frame_min.X * x_scale);
946 bounds_rc.top = floorf(metafile->auto_frame_min.Y * y_scale);
947 bounds_rc.right = ceilf(metafile->auto_frame_max.X * x_scale);
948 bounds_rc.bottom = ceilf(metafile->auto_frame_max.Y * y_scale);
950 gdi_bounds_rc = header.EmfHeader.rclBounds;
951 if (gdi_bounds_rc.right > gdi_bounds_rc.left && gdi_bounds_rc.bottom > gdi_bounds_rc.top)
953 bounds_rc.left = min(bounds_rc.left, gdi_bounds_rc.left);
954 bounds_rc.top = min(bounds_rc.top, gdi_bounds_rc.top);
955 bounds_rc.right = max(bounds_rc.right, gdi_bounds_rc.right);
956 bounds_rc.bottom = max(bounds_rc.bottom, gdi_bounds_rc.bottom);
959 buffer_size = GetEnhMetaFileBits(metafile->hemf, 0, NULL);
960 buffer = heap_alloc(buffer_size);
961 if (buffer)
963 HENHMETAFILE new_hemf;
965 GetEnhMetaFileBits(metafile->hemf, buffer_size, buffer);
967 ((ENHMETAHEADER*)buffer)->rclFrame = bounds_rc;
969 new_hemf = SetEnhMetaFileBits(buffer_size, buffer);
971 if (new_hemf)
973 DeleteEnhMetaFile(metafile->hemf);
974 metafile->hemf = new_hemf;
976 else
977 stat = OutOfMemory;
979 heap_free(buffer);
981 else
982 stat = OutOfMemory;
984 if (stat == Ok)
985 stat = GdipGetMetafileHeaderFromEmf(metafile->hemf, &header);
987 if (stat == Ok)
989 metafile->bounds.X = header.X;
990 metafile->bounds.Y = header.Y;
991 metafile->bounds.Width = header.Width;
992 metafile->bounds.Height = header.Height;
996 if (stat == Ok && metafile->record_stream)
998 BYTE *buffer;
999 UINT buffer_size;
1001 buffer_size = GetEnhMetaFileBits(metafile->hemf, 0, NULL);
1003 buffer = heap_alloc(buffer_size);
1004 if (buffer)
1006 HRESULT hr;
1008 GetEnhMetaFileBits(metafile->hemf, buffer_size, buffer);
1010 hr = IStream_Write(metafile->record_stream, buffer, buffer_size, NULL);
1012 if (FAILED(hr))
1013 stat = hresult_to_status(hr);
1015 heap_free(buffer);
1017 else
1018 stat = OutOfMemory;
1021 if (metafile->record_stream)
1023 IStream_Release(metafile->record_stream);
1024 metafile->record_stream = NULL;
1027 return stat;
1030 GpStatus WINGDIPAPI GdipGetHemfFromMetafile(GpMetafile *metafile, HENHMETAFILE *hEmf)
1032 TRACE("(%p,%p)\n", metafile, hEmf);
1034 if (!metafile || !hEmf || !metafile->hemf)
1035 return InvalidParameter;
1037 *hEmf = metafile->hemf;
1038 metafile->hemf = NULL;
1040 return Ok;
1043 static GpStatus METAFILE_PlaybackGetDC(GpMetafile *metafile)
1045 GpStatus stat = Ok;
1047 stat = GdipGetDC(metafile->playback_graphics, &metafile->playback_dc);
1049 if (stat == Ok)
1051 /* The result of GdipGetDC always expects device co-ordinates, but the
1052 * device co-ordinates of the source metafile do not correspond to
1053 * device co-ordinates of the destination. Therefore, we set up the DC
1054 * so that the metafile's bounds map to the destination points where we
1055 * are drawing this metafile. */
1056 SetMapMode(metafile->playback_dc, MM_ANISOTROPIC);
1058 SetWindowOrgEx(metafile->playback_dc, metafile->bounds.X, metafile->bounds.Y, NULL);
1059 SetWindowExtEx(metafile->playback_dc, metafile->bounds.Width, metafile->bounds.Height, NULL);
1061 SetViewportOrgEx(metafile->playback_dc, metafile->playback_points[0].X, metafile->playback_points[0].Y, NULL);
1062 SetViewportExtEx(metafile->playback_dc,
1063 metafile->playback_points[1].X - metafile->playback_points[0].X,
1064 metafile->playback_points[2].Y - metafile->playback_points[0].Y, NULL);
1067 return stat;
1070 static void METAFILE_PlaybackReleaseDC(GpMetafile *metafile)
1072 if (metafile->playback_dc)
1074 GdipReleaseDC(metafile->playback_graphics, metafile->playback_dc);
1075 metafile->playback_dc = NULL;
1079 static GpStatus METAFILE_PlaybackUpdateClip(GpMetafile *metafile)
1081 GpStatus stat;
1082 stat = GdipCombineRegionRegion(metafile->playback_graphics->clip, metafile->base_clip, CombineModeReplace);
1083 if (stat == Ok)
1084 stat = GdipCombineRegionRegion(metafile->playback_graphics->clip, metafile->clip, CombineModeIntersect);
1085 return stat;
1088 static GpStatus METAFILE_PlaybackUpdateWorldTransform(GpMetafile *metafile)
1090 GpMatrix *real_transform;
1091 GpStatus stat;
1093 stat = GdipCreateMatrix3(&metafile->src_rect, metafile->playback_points, &real_transform);
1095 if (stat == Ok)
1097 REAL scale = units_to_pixels(1.0, metafile->page_unit, 96.0);
1099 if (metafile->page_unit != UnitDisplay)
1100 scale *= metafile->page_scale;
1102 stat = GdipScaleMatrix(real_transform, scale, scale, MatrixOrderPrepend);
1104 if (stat == Ok)
1105 stat = GdipMultiplyMatrix(real_transform, metafile->world_transform, MatrixOrderPrepend);
1107 if (stat == Ok)
1108 stat = GdipSetWorldTransform(metafile->playback_graphics, real_transform);
1110 GdipDeleteMatrix(real_transform);
1113 return stat;
1116 GpStatus WINGDIPAPI GdipPlayMetafileRecord(GDIPCONST GpMetafile *metafile,
1117 EmfPlusRecordType recordType, UINT flags, UINT dataSize, GDIPCONST BYTE *data)
1119 GpStatus stat;
1120 GpMetafile *real_metafile = (GpMetafile*)metafile;
1122 TRACE("(%p,%x,%x,%d,%p)\n", metafile, recordType, flags, dataSize, data);
1124 if (!metafile || (dataSize && !data) || !metafile->playback_graphics)
1125 return InvalidParameter;
1127 if (recordType >= 1 && recordType <= 0x7a)
1129 /* regular EMF record */
1130 if (metafile->playback_dc)
1132 ENHMETARECORD *record;
1134 record = heap_alloc_zero(dataSize + 8);
1136 if (record)
1138 record->iType = recordType;
1139 record->nSize = dataSize + 8;
1140 memcpy(record->dParm, data, dataSize);
1142 PlayEnhMetaFileRecord(metafile->playback_dc, metafile->handle_table,
1143 record, metafile->handle_count);
1145 heap_free(record);
1147 else
1148 return OutOfMemory;
1151 else
1153 EmfPlusRecordHeader *header = (EmfPlusRecordHeader*)(data)-1;
1155 METAFILE_PlaybackReleaseDC((GpMetafile*)metafile);
1157 switch(recordType)
1159 case EmfPlusRecordTypeHeader:
1160 case EmfPlusRecordTypeEndOfFile:
1161 break;
1162 case EmfPlusRecordTypeGetDC:
1163 METAFILE_PlaybackGetDC((GpMetafile*)metafile);
1164 break;
1165 case EmfPlusRecordTypeClear:
1167 EmfPlusClear *record = (EmfPlusClear*)header;
1169 return GdipGraphicsClear(metafile->playback_graphics, record->Color);
1171 case EmfPlusRecordTypeFillRects:
1173 EmfPlusFillRects *record = (EmfPlusFillRects*)header;
1174 GpBrush *brush, *temp_brush=NULL;
1175 GpRectF *rects, *temp_rects=NULL;
1177 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects))
1178 return InvalidParameter;
1180 if (flags & 0x4000)
1182 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects) + sizeof(EmfPlusRect) * record->Count)
1183 return InvalidParameter;
1185 else
1187 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusFillRects) + sizeof(GpRectF) * record->Count)
1188 return InvalidParameter;
1191 if (flags & 0x8000)
1193 stat = GdipCreateSolidFill((ARGB)record->BrushID, (GpSolidFill**)&temp_brush);
1194 brush = temp_brush;
1196 else
1198 FIXME("brush deserialization not implemented\n");
1199 return NotImplemented;
1202 if (stat == Ok)
1204 if (flags & 0x4000)
1206 EmfPlusRect *int_rects = (EmfPlusRect*)(record+1);
1207 int i;
1209 rects = temp_rects = heap_alloc_zero(sizeof(GpRectF) * record->Count);
1210 if (rects)
1212 for (i=0; i<record->Count; i++)
1214 rects[i].X = int_rects[i].X;
1215 rects[i].Y = int_rects[i].Y;
1216 rects[i].Width = int_rects[i].Width;
1217 rects[i].Height = int_rects[i].Height;
1220 else
1221 stat = OutOfMemory;
1223 else
1224 rects = (GpRectF*)(record+1);
1227 if (stat == Ok)
1229 stat = GdipFillRectangles(metafile->playback_graphics, brush, rects, record->Count);
1232 GdipDeleteBrush(temp_brush);
1233 heap_free(temp_rects);
1235 return stat;
1237 case EmfPlusRecordTypeSetClipRect:
1239 EmfPlusSetClipRect *record = (EmfPlusSetClipRect*)header;
1240 CombineMode mode = (CombineMode)((flags >> 8) & 0xf);
1241 GpRegion *region;
1242 GpMatrix world_to_device;
1244 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(*record))
1245 return InvalidParameter;
1247 stat = GdipCreateRegionRect(&record->ClipRect, &region);
1249 if (stat == Ok)
1251 get_graphics_transform(real_metafile->playback_graphics,
1252 CoordinateSpaceDevice, CoordinateSpaceWorld, &world_to_device);
1254 GdipTransformRegion(region, &world_to_device);
1256 GdipCombineRegionRegion(real_metafile->clip, region, mode);
1258 GdipDeleteRegion(region);
1261 return METAFILE_PlaybackUpdateClip(real_metafile);
1263 case EmfPlusRecordTypeSetPageTransform:
1265 EmfPlusSetPageTransform *record = (EmfPlusSetPageTransform*)header;
1266 GpUnit unit = (GpUnit)flags;
1268 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusSetPageTransform))
1269 return InvalidParameter;
1271 real_metafile->page_unit = unit;
1272 real_metafile->page_scale = record->PageScale;
1274 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
1276 case EmfPlusRecordTypeSetWorldTransform:
1278 EmfPlusSetWorldTransform *record = (EmfPlusSetWorldTransform*)header;
1280 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusSetWorldTransform))
1281 return InvalidParameter;
1283 memcpy(real_metafile->world_transform->matrix, record->MatrixData, sizeof(record->MatrixData));
1285 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
1287 case EmfPlusRecordTypeScaleWorldTransform:
1289 EmfPlusScaleWorldTransform *record = (EmfPlusScaleWorldTransform*)header;
1290 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
1292 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusScaleWorldTransform))
1293 return InvalidParameter;
1295 GdipScaleMatrix(real_metafile->world_transform, record->Sx, record->Sy, order);
1297 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
1299 case EmfPlusRecordTypeMultiplyWorldTransform:
1301 EmfPlusMultiplyWorldTransform *record = (EmfPlusMultiplyWorldTransform*)header;
1302 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
1303 GpMatrix matrix;
1305 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusMultiplyWorldTransform))
1306 return InvalidParameter;
1308 memcpy(matrix.matrix, record->MatrixData, sizeof(matrix.matrix));
1310 GdipMultiplyMatrix(real_metafile->world_transform, &matrix, order);
1312 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
1314 case EmfPlusRecordTypeRotateWorldTransform:
1316 EmfPlusRotateWorldTransform *record = (EmfPlusRotateWorldTransform*)header;
1317 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
1319 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusRotateWorldTransform))
1320 return InvalidParameter;
1322 GdipRotateMatrix(real_metafile->world_transform, record->Angle, order);
1324 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
1326 case EmfPlusRecordTypeTranslateWorldTransform:
1328 EmfPlusTranslateWorldTransform *record = (EmfPlusTranslateWorldTransform*)header;
1329 MatrixOrder order = (flags & 0x2000) ? MatrixOrderAppend : MatrixOrderPrepend;
1331 if (dataSize + sizeof(EmfPlusRecordHeader) < sizeof(EmfPlusTranslateWorldTransform))
1332 return InvalidParameter;
1334 GdipTranslateMatrix(real_metafile->world_transform, record->dx, record->dy, order);
1336 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
1338 case EmfPlusRecordTypeResetWorldTransform:
1340 GdipSetMatrixElements(real_metafile->world_transform, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
1342 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
1344 case EmfPlusRecordTypeBeginContainer:
1346 EmfPlusBeginContainer *record = (EmfPlusBeginContainer*)header;
1347 container* cont;
1348 GpUnit unit;
1349 REAL scale_x, scale_y;
1350 GpRectF scaled_srcrect;
1351 GpMatrix transform;
1353 cont = heap_alloc_zero(sizeof(*cont));
1354 if (!cont)
1355 return OutOfMemory;
1357 stat = GdipCloneRegion(metafile->clip, &cont->clip);
1358 if (stat != Ok)
1360 heap_free(cont);
1361 return stat;
1364 GdipBeginContainer2(metafile->playback_graphics, &cont->state);
1366 if (stat != Ok)
1368 GdipDeleteRegion(cont->clip);
1369 heap_free(cont);
1370 return stat;
1373 cont->id = record->StackIndex;
1374 cont->type = BEGIN_CONTAINER;
1375 cont->world_transform = *metafile->world_transform;
1376 cont->page_unit = metafile->page_unit;
1377 cont->page_scale = metafile->page_scale;
1378 list_add_head(&real_metafile->containers, &cont->entry);
1380 unit = record->Header.Flags & 0xff;
1382 scale_x = units_to_pixels(1.0, unit, metafile->image.xres);
1383 scale_y = units_to_pixels(1.0, unit, metafile->image.yres);
1385 scaled_srcrect.X = scale_x * record->SrcRect.X;
1386 scaled_srcrect.Y = scale_y * record->SrcRect.Y;
1387 scaled_srcrect.Width = scale_x * record->SrcRect.Width;
1388 scaled_srcrect.Height = scale_y * record->SrcRect.Height;
1390 transform.matrix[0] = record->DestRect.Width / scaled_srcrect.Width;
1391 transform.matrix[1] = 0.0;
1392 transform.matrix[2] = 0.0;
1393 transform.matrix[3] = record->DestRect.Height / scaled_srcrect.Height;
1394 transform.matrix[4] = record->DestRect.X - scaled_srcrect.X;
1395 transform.matrix[5] = record->DestRect.Y - scaled_srcrect.Y;
1397 GdipMultiplyMatrix(real_metafile->world_transform, &transform, MatrixOrderPrepend);
1399 return METAFILE_PlaybackUpdateWorldTransform(real_metafile);
1401 case EmfPlusRecordTypeBeginContainerNoParams:
1402 case EmfPlusRecordTypeSave:
1404 EmfPlusContainerRecord *record = (EmfPlusContainerRecord*)header;
1405 container* cont;
1407 cont = heap_alloc_zero(sizeof(*cont));
1408 if (!cont)
1409 return OutOfMemory;
1411 stat = GdipCloneRegion(metafile->clip, &cont->clip);
1412 if (stat != Ok)
1414 heap_free(cont);
1415 return stat;
1418 if (recordType == EmfPlusRecordTypeBeginContainerNoParams)
1419 stat = GdipBeginContainer2(metafile->playback_graphics, &cont->state);
1420 else
1421 stat = GdipSaveGraphics(metafile->playback_graphics, &cont->state);
1423 if (stat != Ok)
1425 GdipDeleteRegion(cont->clip);
1426 heap_free(cont);
1427 return stat;
1430 cont->id = record->StackIndex;
1431 if (recordType == EmfPlusRecordTypeBeginContainerNoParams)
1432 cont->type = BEGIN_CONTAINER;
1433 else
1434 cont->type = SAVE_GRAPHICS;
1435 cont->world_transform = *metafile->world_transform;
1436 cont->page_unit = metafile->page_unit;
1437 cont->page_scale = metafile->page_scale;
1438 list_add_head(&real_metafile->containers, &cont->entry);
1440 break;
1442 case EmfPlusRecordTypeEndContainer:
1443 case EmfPlusRecordTypeRestore:
1445 EmfPlusContainerRecord *record = (EmfPlusContainerRecord*)header;
1446 container* cont;
1447 enum container_type type;
1448 BOOL found=FALSE;
1450 if (recordType == EmfPlusRecordTypeEndContainer)
1451 type = BEGIN_CONTAINER;
1452 else
1453 type = SAVE_GRAPHICS;
1455 LIST_FOR_EACH_ENTRY(cont, &real_metafile->containers, container, entry)
1457 if (cont->id == record->StackIndex && cont->type == type)
1459 found = TRUE;
1460 break;
1464 if (found)
1466 container* cont2;
1468 /* pop any newer items on the stack */
1469 while ((cont2 = LIST_ENTRY(list_head(&real_metafile->containers), container, entry)) != cont)
1471 list_remove(&cont2->entry);
1472 GdipDeleteRegion(cont2->clip);
1473 heap_free(cont2);
1476 if (type == BEGIN_CONTAINER)
1477 GdipEndContainer(real_metafile->playback_graphics, cont->state);
1478 else
1479 GdipRestoreGraphics(real_metafile->playback_graphics, cont->state);
1481 *real_metafile->world_transform = cont->world_transform;
1482 real_metafile->page_unit = cont->page_unit;
1483 real_metafile->page_scale = cont->page_scale;
1484 GdipCombineRegionRegion(real_metafile->clip, cont->clip, CombineModeReplace);
1486 list_remove(&cont->entry);
1487 GdipDeleteRegion(cont->clip);
1488 heap_free(cont);
1491 break;
1493 default:
1494 FIXME("Not implemented for record type %x\n", recordType);
1495 return NotImplemented;
1499 return Ok;
1502 struct enum_metafile_data
1504 EnumerateMetafileProc callback;
1505 void *callback_data;
1506 GpMetafile *metafile;
1509 static int CALLBACK enum_metafile_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR,
1510 int nObj, LPARAM lpData)
1512 BOOL ret;
1513 struct enum_metafile_data *data = (struct enum_metafile_data*)lpData;
1514 const BYTE* pStr;
1516 data->metafile->handle_table = lpHTable;
1517 data->metafile->handle_count = nObj;
1519 /* First check for an EMF+ record. */
1520 if (lpEMFR->iType == EMR_GDICOMMENT)
1522 const EMRGDICOMMENT *comment = (const EMRGDICOMMENT*)lpEMFR;
1524 if (comment->cbData >= 4 && memcmp(comment->Data, "EMF+", 4) == 0)
1526 int offset = 4;
1528 while (offset + sizeof(EmfPlusRecordHeader) <= comment->cbData)
1530 const EmfPlusRecordHeader *record = (const EmfPlusRecordHeader*)&comment->Data[offset];
1532 if (record->DataSize)
1533 pStr = (const BYTE*)(record+1);
1534 else
1535 pStr = NULL;
1537 ret = data->callback(record->Type, record->Flags, record->DataSize,
1538 pStr, data->callback_data);
1540 if (!ret)
1541 return 0;
1543 offset += record->Size;
1546 return 1;
1550 if (lpEMFR->nSize != 8)
1551 pStr = (const BYTE*)lpEMFR->dParm;
1552 else
1553 pStr = NULL;
1555 return data->callback(lpEMFR->iType, 0, lpEMFR->nSize-8,
1556 pStr, data->callback_data);
1559 GpStatus WINGDIPAPI GdipEnumerateMetafileSrcRectDestPoints(GpGraphics *graphics,
1560 GDIPCONST GpMetafile *metafile, GDIPCONST GpPointF *destPoints, INT count,
1561 GDIPCONST GpRectF *srcRect, Unit srcUnit, EnumerateMetafileProc callback,
1562 VOID *callbackData, GDIPCONST GpImageAttributes *imageAttributes)
1564 struct enum_metafile_data data;
1565 GpStatus stat;
1566 GpMetafile *real_metafile = (GpMetafile*)metafile; /* whoever made this const was joking */
1567 GraphicsContainer state;
1568 GpPath *dst_path;
1570 TRACE("(%p,%p,%p,%i,%p,%i,%p,%p,%p)\n", graphics, metafile,
1571 destPoints, count, srcRect, srcUnit, callback, callbackData,
1572 imageAttributes);
1574 if (!graphics || !metafile || !destPoints || count != 3 || !srcRect)
1575 return InvalidParameter;
1577 if (!metafile->hemf)
1578 return InvalidParameter;
1580 if (metafile->playback_graphics)
1581 return ObjectBusy;
1583 TRACE("%s %i -> %s %s %s\n", debugstr_rectf(srcRect), srcUnit,
1584 debugstr_pointf(&destPoints[0]), debugstr_pointf(&destPoints[1]),
1585 debugstr_pointf(&destPoints[2]));
1587 data.callback = callback;
1588 data.callback_data = callbackData;
1589 data.metafile = real_metafile;
1591 real_metafile->playback_graphics = graphics;
1592 real_metafile->playback_dc = NULL;
1593 real_metafile->src_rect = *srcRect;
1595 memcpy(real_metafile->playback_points, destPoints, sizeof(PointF) * 3);
1596 stat = GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, real_metafile->playback_points, 3);
1598 if (stat == Ok)
1599 stat = GdipBeginContainer2(graphics, &state);
1601 if (stat == Ok)
1603 stat = GdipSetPageScale(graphics, 1.0);
1605 if (stat == Ok)
1606 stat = GdipSetPageUnit(graphics, UnitPixel);
1608 if (stat == Ok)
1609 stat = GdipResetWorldTransform(graphics);
1611 if (stat == Ok)
1612 stat = GdipCreateRegion(&real_metafile->base_clip);
1614 if (stat == Ok)
1615 stat = GdipGetClip(graphics, real_metafile->base_clip);
1617 if (stat == Ok)
1618 stat = GdipCreateRegion(&real_metafile->clip);
1620 if (stat == Ok)
1621 stat = GdipCreatePath(FillModeAlternate, &dst_path);
1623 if (stat == Ok)
1625 GpPointF clip_points[4];
1627 clip_points[0] = real_metafile->playback_points[0];
1628 clip_points[1] = real_metafile->playback_points[1];
1629 clip_points[2].X = real_metafile->playback_points[1].X + real_metafile->playback_points[2].X
1630 - real_metafile->playback_points[0].X;
1631 clip_points[2].Y = real_metafile->playback_points[1].Y + real_metafile->playback_points[2].Y
1632 - real_metafile->playback_points[0].Y;
1633 clip_points[3] = real_metafile->playback_points[2];
1635 stat = GdipAddPathPolygon(dst_path, clip_points, 4);
1637 if (stat == Ok)
1638 stat = GdipCombineRegionPath(real_metafile->base_clip, dst_path, CombineModeIntersect);
1640 GdipDeletePath(dst_path);
1643 if (stat == Ok)
1644 stat = GdipCreateMatrix(&real_metafile->world_transform);
1646 if (stat == Ok)
1648 real_metafile->page_unit = UnitDisplay;
1649 real_metafile->page_scale = 1.0;
1650 stat = METAFILE_PlaybackUpdateWorldTransform(real_metafile);
1653 if (stat == Ok)
1655 stat = METAFILE_PlaybackUpdateClip(real_metafile);
1658 if (stat == Ok && (metafile->metafile_type == MetafileTypeEmf ||
1659 metafile->metafile_type == MetafileTypeWmfPlaceable ||
1660 metafile->metafile_type == MetafileTypeWmf))
1661 stat = METAFILE_PlaybackGetDC(real_metafile);
1663 if (stat == Ok)
1664 EnumEnhMetaFile(0, metafile->hemf, enum_metafile_proc, &data, NULL);
1666 METAFILE_PlaybackReleaseDC(real_metafile);
1668 GdipDeleteMatrix(real_metafile->world_transform);
1669 real_metafile->world_transform = NULL;
1671 GdipDeleteRegion(real_metafile->base_clip);
1672 real_metafile->base_clip = NULL;
1674 GdipDeleteRegion(real_metafile->clip);
1675 real_metafile->clip = NULL;
1677 while (list_head(&real_metafile->containers))
1679 container* cont = LIST_ENTRY(list_head(&real_metafile->containers), container, entry);
1680 list_remove(&cont->entry);
1681 GdipDeleteRegion(cont->clip);
1682 heap_free(cont);
1685 GdipEndContainer(graphics, state);
1688 real_metafile->playback_graphics = NULL;
1690 return stat;
1693 GpStatus WINGDIPAPI GdipEnumerateMetafileDestRect(GpGraphics *graphics,
1694 GDIPCONST GpMetafile *metafile, GDIPCONST GpRectF *dest,
1695 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
1697 GpPointF points[3];
1699 if (!graphics || !metafile || !dest) return InvalidParameter;
1701 points[0].X = points[2].X = dest->X;
1702 points[0].Y = points[1].Y = dest->Y;
1703 points[1].X = dest->X + dest->Width;
1704 points[2].Y = dest->Y + dest->Height;
1706 return GdipEnumerateMetafileSrcRectDestPoints(graphics, metafile, points, 3,
1707 &metafile->bounds, metafile->unit, callback, cb_data, attrs);
1710 GpStatus WINGDIPAPI GdipEnumerateMetafileDestRectI(GpGraphics *graphics,
1711 GDIPCONST GpMetafile *metafile, GDIPCONST GpRect *dest,
1712 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
1714 GpRectF destf;
1716 if (!graphics || !metafile || !dest) return InvalidParameter;
1718 destf.X = dest->X;
1719 destf.Y = dest->Y;
1720 destf.Width = dest->Width;
1721 destf.Height = dest->Height;
1723 return GdipEnumerateMetafileDestRect(graphics, metafile, &destf, callback, cb_data, attrs);
1726 GpStatus WINGDIPAPI GdipEnumerateMetafileDestPoint(GpGraphics *graphics,
1727 GDIPCONST GpMetafile *metafile, GDIPCONST GpPointF *dest,
1728 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
1730 GpRectF destf;
1732 if (!graphics || !metafile || !dest) return InvalidParameter;
1734 destf.X = dest->X;
1735 destf.Y = dest->Y;
1736 destf.Width = units_to_pixels(metafile->bounds.Width, metafile->unit, metafile->image.xres);
1737 destf.Height = units_to_pixels(metafile->bounds.Height, metafile->unit, metafile->image.yres);
1739 return GdipEnumerateMetafileDestRect(graphics, metafile, &destf, callback, cb_data, attrs);
1742 GpStatus WINGDIPAPI GdipEnumerateMetafileDestPointI(GpGraphics *graphics,
1743 GDIPCONST GpMetafile *metafile, GDIPCONST GpPoint *dest,
1744 EnumerateMetafileProc callback, VOID *cb_data, GDIPCONST GpImageAttributes *attrs)
1746 GpPointF ptf;
1748 if (!graphics || !metafile || !dest) return InvalidParameter;
1750 ptf.X = dest->X;
1751 ptf.Y = dest->Y;
1753 return GdipEnumerateMetafileDestPoint(graphics, metafile, &ptf, callback, cb_data, attrs);
1756 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromMetafile(GpMetafile * metafile,
1757 MetafileHeader * header)
1759 GpStatus status;
1761 TRACE("(%p, %p)\n", metafile, header);
1763 if(!metafile || !header)
1764 return InvalidParameter;
1766 if (metafile->hemf)
1768 status = GdipGetMetafileHeaderFromEmf(metafile->hemf, header);
1769 if (status != Ok) return status;
1771 else
1773 memset(header, 0, sizeof(*header));
1774 header->Version = 0xdbc01002;
1777 header->Type = metafile->metafile_type;
1778 header->DpiX = metafile->image.xres;
1779 header->DpiY = metafile->image.yres;
1780 header->Width = gdip_round(metafile->bounds.Width);
1781 header->Height = gdip_round(metafile->bounds.Height);
1783 return Ok;
1786 static int CALLBACK get_emfplus_header_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR,
1787 int nObj, LPARAM lpData)
1789 EmfPlusHeader *dst_header = (EmfPlusHeader*)lpData;
1791 if (lpEMFR->iType == EMR_GDICOMMENT)
1793 const EMRGDICOMMENT *comment = (const EMRGDICOMMENT*)lpEMFR;
1795 if (comment->cbData >= 4 && memcmp(comment->Data, "EMF+", 4) == 0)
1797 const EmfPlusRecordHeader *header = (const EmfPlusRecordHeader*)&comment->Data[4];
1799 if (4 + sizeof(EmfPlusHeader) <= comment->cbData &&
1800 header->Type == EmfPlusRecordTypeHeader)
1802 memcpy(dst_header, header, sizeof(*dst_header));
1806 else if (lpEMFR->iType == EMR_HEADER)
1807 return TRUE;
1809 return FALSE;
1812 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromEmf(HENHMETAFILE hemf,
1813 MetafileHeader *header)
1815 ENHMETAHEADER3 emfheader;
1816 EmfPlusHeader emfplusheader;
1817 MetafileType metafile_type;
1819 TRACE("(%p,%p)\n", hemf, header);
1821 if(!hemf || !header)
1822 return InvalidParameter;
1824 if (GetEnhMetaFileHeader(hemf, sizeof(emfheader), (ENHMETAHEADER*)&emfheader) == 0)
1825 return GenericError;
1827 emfplusheader.Header.Type = 0;
1829 EnumEnhMetaFile(NULL, hemf, get_emfplus_header_proc, &emfplusheader, NULL);
1831 if (emfplusheader.Header.Type == EmfPlusRecordTypeHeader)
1833 if ((emfplusheader.Header.Flags & 1) == 1)
1834 metafile_type = MetafileTypeEmfPlusDual;
1835 else
1836 metafile_type = MetafileTypeEmfPlusOnly;
1838 else
1839 metafile_type = MetafileTypeEmf;
1841 header->Type = metafile_type;
1842 header->Size = emfheader.nBytes;
1843 header->DpiX = (REAL)emfheader.szlDevice.cx * 25.4 / emfheader.szlMillimeters.cx;
1844 header->DpiY = (REAL)emfheader.szlDevice.cy * 25.4 / emfheader.szlMillimeters.cy;
1845 header->X = gdip_round((REAL)emfheader.rclFrame.left / 2540.0 * header->DpiX);
1846 header->Y = gdip_round((REAL)emfheader.rclFrame.top / 2540.0 * header->DpiY);
1847 header->Width = gdip_round((REAL)(emfheader.rclFrame.right - emfheader.rclFrame.left) / 2540.0 * header->DpiX);
1848 header->Height = gdip_round((REAL)(emfheader.rclFrame.bottom - emfheader.rclFrame.top) / 2540.0 * header->DpiY);
1849 header->EmfHeader = emfheader;
1851 if (metafile_type == MetafileTypeEmfPlusDual || metafile_type == MetafileTypeEmfPlusOnly)
1853 header->Version = emfplusheader.Version;
1854 header->EmfPlusFlags = emfplusheader.EmfPlusFlags;
1855 header->EmfPlusHeaderSize = emfplusheader.Header.Size;
1856 header->LogicalDpiX = emfplusheader.LogicalDpiX;
1857 header->LogicalDpiY = emfplusheader.LogicalDpiY;
1859 else
1861 header->Version = emfheader.nVersion;
1862 header->EmfPlusFlags = 0;
1863 header->EmfPlusHeaderSize = 0;
1864 header->LogicalDpiX = 0;
1865 header->LogicalDpiY = 0;
1868 return Ok;
1871 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromWmf(HMETAFILE hwmf,
1872 GDIPCONST WmfPlaceableFileHeader *placeable, MetafileHeader *header)
1874 GpStatus status;
1875 GpMetafile *metafile;
1877 TRACE("(%p,%p,%p)\n", hwmf, placeable, header);
1879 status = GdipCreateMetafileFromWmf(hwmf, FALSE, placeable, &metafile);
1880 if (status == Ok)
1882 status = GdipGetMetafileHeaderFromMetafile(metafile, header);
1883 GdipDisposeImage(&metafile->image);
1885 return status;
1888 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromFile(GDIPCONST WCHAR *filename,
1889 MetafileHeader *header)
1891 GpStatus status;
1892 GpMetafile *metafile;
1894 TRACE("(%s,%p)\n", debugstr_w(filename), header);
1896 if (!filename || !header)
1897 return InvalidParameter;
1899 status = GdipCreateMetafileFromFile(filename, &metafile);
1900 if (status == Ok)
1902 status = GdipGetMetafileHeaderFromMetafile(metafile, header);
1903 GdipDisposeImage(&metafile->image);
1905 return status;
1908 GpStatus WINGDIPAPI GdipGetMetafileHeaderFromStream(IStream *stream,
1909 MetafileHeader *header)
1911 GpStatus status;
1912 GpMetafile *metafile;
1914 TRACE("(%p,%p)\n", stream, header);
1916 if (!stream || !header)
1917 return InvalidParameter;
1919 status = GdipCreateMetafileFromStream(stream, &metafile);
1920 if (status == Ok)
1922 status = GdipGetMetafileHeaderFromMetafile(metafile, header);
1923 GdipDisposeImage(&metafile->image);
1925 return status;
1928 GpStatus WINGDIPAPI GdipCreateMetafileFromEmf(HENHMETAFILE hemf, BOOL delete,
1929 GpMetafile **metafile)
1931 GpStatus stat;
1932 MetafileHeader header;
1934 TRACE("(%p,%i,%p)\n", hemf, delete, metafile);
1936 if(!hemf || !metafile)
1937 return InvalidParameter;
1939 stat = GdipGetMetafileHeaderFromEmf(hemf, &header);
1940 if (stat != Ok)
1941 return stat;
1943 *metafile = heap_alloc_zero(sizeof(GpMetafile));
1944 if (!*metafile)
1945 return OutOfMemory;
1947 (*metafile)->image.type = ImageTypeMetafile;
1948 (*metafile)->image.format = ImageFormatEMF;
1949 (*metafile)->image.frame_count = 1;
1950 (*metafile)->image.xres = header.DpiX;
1951 (*metafile)->image.yres = header.DpiY;
1952 (*metafile)->bounds.X = (REAL)header.EmfHeader.rclFrame.left / 2540.0 * header.DpiX;
1953 (*metafile)->bounds.Y = (REAL)header.EmfHeader.rclFrame.top / 2540.0 * header.DpiY;
1954 (*metafile)->bounds.Width = (REAL)(header.EmfHeader.rclFrame.right - header.EmfHeader.rclFrame.left)
1955 / 2540.0 * header.DpiX;
1956 (*metafile)->bounds.Height = (REAL)(header.EmfHeader.rclFrame.bottom - header.EmfHeader.rclFrame.top)
1957 / 2540.0 * header.DpiY;
1958 (*metafile)->unit = UnitPixel;
1959 (*metafile)->metafile_type = header.Type;
1960 (*metafile)->hemf = hemf;
1961 (*metafile)->preserve_hemf = !delete;
1962 list_init(&(*metafile)->containers);
1964 TRACE("<-- %p\n", *metafile);
1966 return Ok;
1969 GpStatus WINGDIPAPI GdipCreateMetafileFromWmf(HMETAFILE hwmf, BOOL delete,
1970 GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
1972 UINT read;
1973 BYTE *copy;
1974 HENHMETAFILE hemf;
1975 GpStatus retval = Ok;
1977 TRACE("(%p, %d, %p, %p)\n", hwmf, delete, placeable, metafile);
1979 if(!hwmf || !metafile)
1980 return InvalidParameter;
1982 *metafile = NULL;
1983 read = GetMetaFileBitsEx(hwmf, 0, NULL);
1984 if(!read)
1985 return GenericError;
1986 copy = heap_alloc_zero(read);
1987 GetMetaFileBitsEx(hwmf, read, copy);
1989 hemf = SetWinMetaFileBits(read, copy, NULL, NULL);
1990 heap_free(copy);
1992 /* FIXME: We should store and use hwmf instead of converting to hemf */
1993 retval = GdipCreateMetafileFromEmf(hemf, TRUE, metafile);
1995 if (retval == Ok)
1997 if (placeable)
1999 (*metafile)->image.xres = (REAL)placeable->Inch;
2000 (*metafile)->image.yres = (REAL)placeable->Inch;
2001 (*metafile)->bounds.X = ((REAL)placeable->BoundingBox.Left) / ((REAL)placeable->Inch);
2002 (*metafile)->bounds.Y = ((REAL)placeable->BoundingBox.Top) / ((REAL)placeable->Inch);
2003 (*metafile)->bounds.Width = (REAL)(placeable->BoundingBox.Right -
2004 placeable->BoundingBox.Left);
2005 (*metafile)->bounds.Height = (REAL)(placeable->BoundingBox.Bottom -
2006 placeable->BoundingBox.Top);
2007 (*metafile)->metafile_type = MetafileTypeWmfPlaceable;
2009 else
2010 (*metafile)->metafile_type = MetafileTypeWmf;
2011 (*metafile)->image.format = ImageFormatWMF;
2013 if (delete) DeleteMetaFile(hwmf);
2015 else
2016 DeleteEnhMetaFile(hemf);
2017 return retval;
2020 GpStatus WINGDIPAPI GdipCreateMetafileFromWmfFile(GDIPCONST WCHAR *file,
2021 GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
2023 HMETAFILE hmf = GetMetaFileW(file);
2025 TRACE("(%s, %p, %p)\n", debugstr_w(file), placeable, metafile);
2027 if(!hmf) return InvalidParameter;
2029 return GdipCreateMetafileFromWmf(hmf, TRUE, placeable, metafile);
2032 GpStatus WINGDIPAPI GdipCreateMetafileFromFile(GDIPCONST WCHAR *file,
2033 GpMetafile **metafile)
2035 GpStatus status;
2036 IStream *stream;
2038 TRACE("(%p, %p)\n", file, metafile);
2040 if (!file || !metafile) return InvalidParameter;
2042 *metafile = NULL;
2044 status = GdipCreateStreamOnFile(file, GENERIC_READ, &stream);
2045 if (status == Ok)
2047 status = GdipCreateMetafileFromStream(stream, metafile);
2048 IStream_Release(stream);
2050 return status;
2053 GpStatus WINGDIPAPI GdipCreateMetafileFromStream(IStream *stream,
2054 GpMetafile **metafile)
2056 GpStatus stat;
2058 TRACE("%p %p\n", stream, metafile);
2060 stat = GdipLoadImageFromStream(stream, (GpImage **)metafile);
2061 if (stat != Ok) return stat;
2063 if ((*metafile)->image.type != ImageTypeMetafile)
2065 GdipDisposeImage(&(*metafile)->image);
2066 *metafile = NULL;
2067 return GenericError;
2070 return Ok;
2073 GpStatus WINGDIPAPI GdipSetMetafileDownLevelRasterizationLimit(GpMetafile *metafile,
2074 UINT limitDpi)
2076 TRACE("(%p,%u)\n", metafile, limitDpi);
2078 return Ok;
2081 GpStatus WINGDIPAPI GdipConvertToEmfPlus(const GpGraphics* ref,
2082 GpMetafile* metafile, BOOL* succ, EmfType emfType,
2083 const WCHAR* description, GpMetafile** out_metafile)
2085 static int calls;
2087 TRACE("(%p,%p,%p,%u,%s,%p)\n", ref, metafile, succ, emfType,
2088 debugstr_w(description), out_metafile);
2090 if(!ref || !metafile || !out_metafile || emfType < EmfTypeEmfOnly || emfType > EmfTypeEmfPlusDual)
2091 return InvalidParameter;
2093 if(succ)
2094 *succ = FALSE;
2095 *out_metafile = NULL;
2097 if(!(calls++))
2098 FIXME("not implemented\n");
2100 return NotImplemented;
2103 GpStatus WINGDIPAPI GdipEmfToWmfBits(HENHMETAFILE hemf, UINT cbData16,
2104 LPBYTE pData16, INT iMapMode, INT eFlags)
2106 FIXME("(%p, %d, %p, %d, %d): stub\n", hemf, cbData16, pData16, iMapMode, eFlags);
2107 return NotImplemented;
2110 GpStatus WINGDIPAPI GdipRecordMetafileFileName(GDIPCONST WCHAR* fileName,
2111 HDC hdc, EmfType type, GDIPCONST GpRectF *pFrameRect,
2112 MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc,
2113 GpMetafile **metafile)
2115 FIXME("%s %p %d %p %d %s %p stub!\n", debugstr_w(fileName), hdc, type, pFrameRect,
2116 frameUnit, debugstr_w(desc), metafile);
2118 return NotImplemented;
2121 GpStatus WINGDIPAPI GdipRecordMetafileFileNameI(GDIPCONST WCHAR* fileName, HDC hdc, EmfType type,
2122 GDIPCONST GpRect *pFrameRect, MetafileFrameUnit frameUnit,
2123 GDIPCONST WCHAR *desc, GpMetafile **metafile)
2125 FIXME("%s %p %d %p %d %s %p stub!\n", debugstr_w(fileName), hdc, type, pFrameRect,
2126 frameUnit, debugstr_w(desc), metafile);
2128 return NotImplemented;
2131 /*****************************************************************************
2132 * GdipConvertToEmfPlusToFile [GDIPLUS.@]
2135 GpStatus WINGDIPAPI GdipConvertToEmfPlusToFile(const GpGraphics* refGraphics,
2136 GpMetafile* metafile, BOOL* conversionSuccess,
2137 const WCHAR* filename, EmfType emfType,
2138 const WCHAR* description, GpMetafile** out_metafile)
2140 FIXME("stub: %p, %p, %p, %p, %u, %p, %p\n", refGraphics, metafile, conversionSuccess, filename, emfType, description, out_metafile);
2141 return NotImplemented;