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
22 #define NONAMELESSUNION
27 #include "wine/unicode.h"
39 #include "gdiplus_private.h"
40 #include "wine/debug.h"
41 #include "wine/list.h"
43 WINE_DEFAULT_DEBUG_CHANNEL(gdiplus
);
45 typedef struct EmfPlusRecordHeader
51 } EmfPlusRecordHeader
;
53 typedef struct EmfPlusHeader
55 EmfPlusRecordHeader Header
;
62 typedef struct EmfPlusClear
64 EmfPlusRecordHeader Header
;
68 typedef struct EmfPlusFillRects
70 EmfPlusRecordHeader Header
;
75 typedef struct EmfPlusSetClipRect
77 EmfPlusRecordHeader Header
;
81 typedef struct EmfPlusSetPageTransform
83 EmfPlusRecordHeader Header
;
85 } EmfPlusSetPageTransform
;
87 typedef struct EmfPlusRect
95 typedef struct EmfPlusSetWorldTransform
97 EmfPlusRecordHeader Header
;
99 } EmfPlusSetWorldTransform
;
101 typedef struct EmfPlusScaleWorldTransform
103 EmfPlusRecordHeader Header
;
106 } EmfPlusScaleWorldTransform
;
108 typedef struct EmfPlusMultiplyWorldTransform
110 EmfPlusRecordHeader Header
;
112 } EmfPlusMultiplyWorldTransform
;
114 typedef struct EmfPlusRotateWorldTransform
116 EmfPlusRecordHeader Header
;
118 } EmfPlusRotateWorldTransform
;
120 typedef struct EmfPlusTranslateWorldTransform
122 EmfPlusRecordHeader Header
;
125 } EmfPlusTranslateWorldTransform
;
127 typedef struct EmfPlusBeginContainer
129 EmfPlusRecordHeader Header
;
133 } EmfPlusBeginContainer
;
135 typedef struct EmfPlusContainerRecord
137 EmfPlusRecordHeader Header
;
139 } EmfPlusContainerRecord
;
147 typedef struct container
151 enum container_type type
;
152 GraphicsContainer state
;
153 GpMatrix world_transform
;
159 static GpStatus
METAFILE_AllocateRecord(GpMetafile
*metafile
, DWORD size
, void **result
)
162 EmfPlusRecordHeader
*record
;
164 if (!metafile
->comment_data_size
)
166 DWORD data_size
= max(256, size
* 2 + 4);
167 metafile
->comment_data
= heap_alloc_zero(data_size
);
169 if (!metafile
->comment_data
)
172 memcpy(metafile
->comment_data
, "EMF+", 4);
174 metafile
->comment_data_size
= data_size
;
175 metafile
->comment_data_length
= 4;
178 size_needed
= size
+ metafile
->comment_data_length
;
180 if (size_needed
> metafile
->comment_data_size
)
182 DWORD data_size
= size_needed
* 2;
183 BYTE
*new_data
= heap_alloc_zero(data_size
);
188 memcpy(new_data
, metafile
->comment_data
, metafile
->comment_data_length
);
190 metafile
->comment_data_size
= data_size
;
191 heap_free(metafile
->comment_data
);
192 metafile
->comment_data
= new_data
;
195 *result
= metafile
->comment_data
+ metafile
->comment_data_length
;
196 metafile
->comment_data_length
+= size
;
198 record
= (EmfPlusRecordHeader
*)*result
;
200 record
->DataSize
= size
- sizeof(EmfPlusRecordHeader
);
205 static void METAFILE_WriteRecords(GpMetafile
*metafile
)
207 if (metafile
->comment_data_length
> 4)
209 GdiComment(metafile
->record_dc
, metafile
->comment_data_length
, metafile
->comment_data
);
210 metafile
->comment_data_length
= 4;
214 static GpStatus
METAFILE_WriteHeader(GpMetafile
*metafile
, HDC hdc
)
218 if (metafile
->metafile_type
== MetafileTypeEmfPlusOnly
|| metafile
->metafile_type
== MetafileTypeEmfPlusDual
)
220 EmfPlusHeader
*header
;
222 stat
= METAFILE_AllocateRecord(metafile
, sizeof(EmfPlusHeader
), (void**)&header
);
226 header
->Header
.Type
= EmfPlusRecordTypeHeader
;
228 if (metafile
->metafile_type
== MetafileTypeEmfPlusDual
)
229 header
->Header
.Flags
= 1;
231 header
->Header
.Flags
= 0;
233 header
->Version
= 0xDBC01002;
235 if (GetDeviceCaps(hdc
, TECHNOLOGY
) == DT_RASDISPLAY
)
236 header
->EmfPlusFlags
= 1;
238 header
->EmfPlusFlags
= 0;
240 header
->LogicalDpiX
= GetDeviceCaps(hdc
, LOGPIXELSX
);
241 header
->LogicalDpiY
= GetDeviceCaps(hdc
, LOGPIXELSY
);
243 METAFILE_WriteRecords(metafile
);
249 static GpStatus
METAFILE_WriteEndOfFile(GpMetafile
*metafile
)
253 if (metafile
->metafile_type
== MetafileTypeEmfPlusOnly
|| metafile
->metafile_type
== MetafileTypeEmfPlusDual
)
255 EmfPlusRecordHeader
*record
;
257 stat
= METAFILE_AllocateRecord(metafile
, sizeof(EmfPlusRecordHeader
), (void**)&record
);
261 record
->Type
= EmfPlusRecordTypeEndOfFile
;
264 METAFILE_WriteRecords(metafile
);
270 GpStatus WINGDIPAPI
GdipRecordMetafile(HDC hdc
, EmfType type
, GDIPCONST GpRectF
*frameRect
,
271 MetafileFrameUnit frameUnit
, GDIPCONST WCHAR
*desc
, GpMetafile
**metafile
)
275 REAL framerect_factor_x
, framerect_factor_y
;
279 TRACE("(%p %d %p %d %p %p)\n", hdc
, type
, frameRect
, frameUnit
, desc
, metafile
);
281 if (!hdc
|| type
< EmfTypeEmfOnly
|| type
> EmfTypeEmfPlusDual
|| !metafile
)
282 return InvalidParameter
;
284 dpix
= (REAL
)GetDeviceCaps(hdc
, HORZRES
) / GetDeviceCaps(hdc
, HORZSIZE
) * 25.4;
285 dpiy
= (REAL
)GetDeviceCaps(hdc
, VERTRES
) / GetDeviceCaps(hdc
, VERTSIZE
) * 25.4;
291 case MetafileFrameUnitPixel
:
292 framerect_factor_x
= 2540.0 / dpix
;
293 framerect_factor_y
= 2540.0 / dpiy
;
295 case MetafileFrameUnitPoint
:
296 framerect_factor_x
= framerect_factor_y
= 2540.0 / 72.0;
298 case MetafileFrameUnitInch
:
299 framerect_factor_x
= framerect_factor_y
= 2540.0;
301 case MetafileFrameUnitDocument
:
302 framerect_factor_x
= framerect_factor_y
= 2540.0 / 300.0;
304 case MetafileFrameUnitMillimeter
:
305 framerect_factor_x
= framerect_factor_y
= 100.0;
307 case MetafileFrameUnitGdi
:
308 framerect_factor_x
= framerect_factor_y
= 1.0;
311 return InvalidParameter
;
314 rc
.left
= framerect_factor_x
* frameRect
->X
;
315 rc
.top
= framerect_factor_y
* frameRect
->Y
;
316 rc
.right
= rc
.left
+ framerect_factor_x
* frameRect
->Width
;
317 rc
.bottom
= rc
.top
+ framerect_factor_y
* frameRect
->Height
;
324 record_dc
= CreateEnhMetaFileW(hdc
, NULL
, lprc
, desc
);
329 *metafile
= heap_alloc_zero(sizeof(GpMetafile
));
332 DeleteEnhMetaFile(CloseEnhMetaFile(record_dc
));
336 (*metafile
)->image
.type
= ImageTypeMetafile
;
337 (*metafile
)->image
.flags
= ImageFlagsNone
;
338 (*metafile
)->image
.palette
= NULL
;
339 (*metafile
)->image
.xres
= dpix
;
340 (*metafile
)->image
.yres
= dpiy
;
341 (*metafile
)->bounds
.X
= (*metafile
)->bounds
.Y
= 0.0;
342 (*metafile
)->bounds
.Width
= (*metafile
)->bounds
.Height
= 1.0;
343 (*metafile
)->unit
= UnitPixel
;
344 (*metafile
)->metafile_type
= type
;
345 (*metafile
)->record_dc
= record_dc
;
346 (*metafile
)->comment_data
= NULL
;
347 (*metafile
)->comment_data_size
= 0;
348 (*metafile
)->comment_data_length
= 0;
349 (*metafile
)->hemf
= NULL
;
350 list_init(&(*metafile
)->containers
);
354 (*metafile
)->auto_frame
= TRUE
;
355 (*metafile
)->auto_frame_min
.X
= 0;
356 (*metafile
)->auto_frame_min
.Y
= 0;
357 (*metafile
)->auto_frame_max
.X
= -1;
358 (*metafile
)->auto_frame_max
.Y
= -1;
361 stat
= METAFILE_WriteHeader(*metafile
, hdc
);
365 DeleteEnhMetaFile(CloseEnhMetaFile(record_dc
));
366 heap_free(*metafile
);
374 /*****************************************************************************
375 * GdipRecordMetafileI [GDIPLUS.@]
377 GpStatus WINGDIPAPI
GdipRecordMetafileI(HDC hdc
, EmfType type
, GDIPCONST GpRect
*frameRect
,
378 MetafileFrameUnit frameUnit
, GDIPCONST WCHAR
*desc
, GpMetafile
**metafile
)
380 GpRectF frameRectF
, *pFrameRectF
;
382 TRACE("(%p %d %p %d %p %p)\n", hdc
, type
, frameRect
, frameUnit
, desc
, metafile
);
386 frameRectF
.X
= frameRect
->X
;
387 frameRectF
.Y
= frameRect
->Y
;
388 frameRectF
.Width
= frameRect
->Width
;
389 frameRectF
.Height
= frameRect
->Height
;
390 pFrameRectF
= &frameRectF
;
395 return GdipRecordMetafile(hdc
, type
, pFrameRectF
, frameUnit
, desc
, metafile
);
398 GpStatus WINGDIPAPI
GdipRecordMetafileStream(IStream
*stream
, HDC hdc
, EmfType type
, GDIPCONST GpRectF
*frameRect
,
399 MetafileFrameUnit frameUnit
, GDIPCONST WCHAR
*desc
, GpMetafile
**metafile
)
403 TRACE("(%p %p %d %p %d %p %p)\n", stream
, hdc
, type
, frameRect
, frameUnit
, desc
, metafile
);
406 return InvalidParameter
;
408 stat
= GdipRecordMetafile(hdc
, type
, frameRect
, frameUnit
, desc
, metafile
);
412 (*metafile
)->record_stream
= stream
;
413 IStream_AddRef(stream
);
419 static void METAFILE_AdjustFrame(GpMetafile
* metafile
, const GpPointF
*points
,
424 if (!metafile
->auto_frame
|| !num_points
)
427 if (metafile
->auto_frame_max
.X
< metafile
->auto_frame_min
.X
)
428 metafile
->auto_frame_max
= metafile
->auto_frame_min
= points
[0];
430 for (i
=0; i
<num_points
; i
++)
432 if (points
[i
].X
< metafile
->auto_frame_min
.X
)
433 metafile
->auto_frame_min
.X
= points
[i
].X
;
434 if (points
[i
].X
> metafile
->auto_frame_max
.X
)
435 metafile
->auto_frame_max
.X
= points
[i
].X
;
436 if (points
[i
].Y
< metafile
->auto_frame_min
.Y
)
437 metafile
->auto_frame_min
.Y
= points
[i
].Y
;
438 if (points
[i
].Y
> metafile
->auto_frame_max
.Y
)
439 metafile
->auto_frame_max
.Y
= points
[i
].Y
;
443 GpStatus
METAFILE_GetGraphicsContext(GpMetafile
* metafile
, GpGraphics
**result
)
447 if (!metafile
->record_dc
|| metafile
->record_graphics
)
448 return InvalidParameter
;
450 stat
= graphics_from_image((GpImage
*)metafile
, &metafile
->record_graphics
);
454 *result
= metafile
->record_graphics
;
455 metafile
->record_graphics
->xres
= 96.0;
456 metafile
->record_graphics
->yres
= 96.0;
462 GpStatus
METAFILE_GetDC(GpMetafile
* metafile
, HDC
*hdc
)
464 if (metafile
->metafile_type
== MetafileTypeEmfPlusOnly
|| metafile
->metafile_type
== MetafileTypeEmfPlusDual
)
466 EmfPlusRecordHeader
*record
;
469 stat
= METAFILE_AllocateRecord(metafile
, sizeof(EmfPlusRecordHeader
), (void**)&record
);
473 record
->Type
= EmfPlusRecordTypeGetDC
;
476 METAFILE_WriteRecords(metafile
);
479 *hdc
= metafile
->record_dc
;
484 GpStatus
METAFILE_GraphicsClear(GpMetafile
* metafile
, ARGB color
)
486 if (metafile
->metafile_type
== MetafileTypeEmfPlusOnly
|| metafile
->metafile_type
== MetafileTypeEmfPlusDual
)
488 EmfPlusClear
*record
;
491 stat
= METAFILE_AllocateRecord(metafile
, sizeof(EmfPlusClear
), (void**)&record
);
495 record
->Header
.Type
= EmfPlusRecordTypeClear
;
496 record
->Header
.Flags
= 0;
497 record
->Color
= color
;
499 METAFILE_WriteRecords(metafile
);
505 static BOOL
is_integer_rect(const GpRectF
*rect
)
507 SHORT x
, y
, width
, height
;
511 height
= rect
->Height
;
512 if (rect
->X
!= (REAL
)x
|| rect
->Y
!= (REAL
)y
||
513 rect
->Width
!= (REAL
)width
|| rect
->Height
!= (REAL
)height
)
518 GpStatus
METAFILE_FillRectangles(GpMetafile
* metafile
, GpBrush
* brush
,
519 GDIPCONST GpRectF
* rects
, INT count
)
521 if (metafile
->metafile_type
== MetafileTypeEmfPlusOnly
|| metafile
->metafile_type
== MetafileTypeEmfPlusDual
)
523 EmfPlusFillRects
*record
;
525 BOOL integer_rects
= TRUE
;
530 if (brush
->bt
== BrushTypeSolidColor
)
533 brushid
= ((GpSolidFill
*)brush
)->color
;
537 FIXME("brush serialization not implemented\n");
538 return NotImplemented
;
541 for (i
=0; i
<count
; i
++)
543 if (!is_integer_rect(&rects
[i
]))
545 integer_rects
= FALSE
;
553 stat
= METAFILE_AllocateRecord(metafile
,
554 sizeof(EmfPlusFillRects
) + count
* (integer_rects
? sizeof(EmfPlusRect
) : sizeof(GpRectF
)),
559 record
->Header
.Type
= EmfPlusRecordTypeFillRects
;
560 record
->Header
.Flags
= flags
;
561 record
->BrushID
= brushid
;
562 record
->Count
= count
;
566 EmfPlusRect
*record_rects
= (EmfPlusRect
*)(record
+1);
567 for (i
=0; i
<count
; i
++)
569 record_rects
[i
].X
= (SHORT
)rects
[i
].X
;
570 record_rects
[i
].Y
= (SHORT
)rects
[i
].Y
;
571 record_rects
[i
].Width
= (SHORT
)rects
[i
].Width
;
572 record_rects
[i
].Height
= (SHORT
)rects
[i
].Height
;
576 memcpy(record
+1, rects
, sizeof(GpRectF
) * count
);
578 METAFILE_WriteRecords(metafile
);
581 if (metafile
->auto_frame
)
586 for (i
=0; i
<count
; i
++)
588 corners
[0].X
= rects
[i
].X
;
589 corners
[0].Y
= rects
[i
].Y
;
590 corners
[1].X
= rects
[i
].X
+ rects
[i
].Width
;
591 corners
[1].Y
= rects
[i
].Y
;
592 corners
[2].X
= rects
[i
].X
;
593 corners
[2].Y
= rects
[i
].Y
+ rects
[i
].Height
;
594 corners
[3].X
= rects
[i
].X
+ rects
[i
].Width
;
595 corners
[3].Y
= rects
[i
].Y
+ rects
[i
].Height
;
597 GdipTransformPoints(metafile
->record_graphics
, CoordinateSpaceDevice
,
598 CoordinateSpaceWorld
, corners
, 4);
600 METAFILE_AdjustFrame(metafile
, corners
, 4);
607 GpStatus
METAFILE_SetClipRect(GpMetafile
* metafile
, REAL x
, REAL y
, REAL width
, REAL height
, CombineMode mode
)
609 if (metafile
->metafile_type
== MetafileTypeEmfPlusOnly
|| metafile
->metafile_type
== MetafileTypeEmfPlusDual
)
611 EmfPlusSetClipRect
*record
;
614 stat
= METAFILE_AllocateRecord(metafile
,
615 sizeof(EmfPlusSetClipRect
),
620 record
->Header
.Type
= EmfPlusRecordTypeSetClipRect
;
621 record
->Header
.Flags
= (mode
& 0xf) << 8;
622 record
->ClipRect
.X
= x
;
623 record
->ClipRect
.Y
= y
;
624 record
->ClipRect
.Width
= width
;
625 record
->ClipRect
.Height
= height
;
627 METAFILE_WriteRecords(metafile
);
633 GpStatus
METAFILE_SetPageTransform(GpMetafile
* metafile
, GpUnit unit
, REAL scale
)
635 if (metafile
->metafile_type
== MetafileTypeEmfPlusOnly
|| metafile
->metafile_type
== MetafileTypeEmfPlusDual
)
637 EmfPlusSetPageTransform
*record
;
640 stat
= METAFILE_AllocateRecord(metafile
,
641 sizeof(EmfPlusSetPageTransform
),
646 record
->Header
.Type
= EmfPlusRecordTypeSetPageTransform
;
647 record
->Header
.Flags
= unit
;
648 record
->PageScale
= scale
;
650 METAFILE_WriteRecords(metafile
);
656 GpStatus
METAFILE_SetWorldTransform(GpMetafile
* metafile
, GDIPCONST GpMatrix
* transform
)
658 if (metafile
->metafile_type
== MetafileTypeEmfPlusOnly
|| metafile
->metafile_type
== MetafileTypeEmfPlusDual
)
660 EmfPlusSetWorldTransform
*record
;
663 stat
= METAFILE_AllocateRecord(metafile
,
664 sizeof(EmfPlusSetWorldTransform
),
669 record
->Header
.Type
= EmfPlusRecordTypeSetWorldTransform
;
670 record
->Header
.Flags
= 0;
671 memcpy(record
->MatrixData
, transform
->matrix
, sizeof(record
->MatrixData
));
673 METAFILE_WriteRecords(metafile
);
679 GpStatus
METAFILE_ScaleWorldTransform(GpMetafile
* metafile
, REAL sx
, REAL sy
, MatrixOrder order
)
681 if (metafile
->metafile_type
== MetafileTypeEmfPlusOnly
|| metafile
->metafile_type
== MetafileTypeEmfPlusDual
)
683 EmfPlusScaleWorldTransform
*record
;
686 stat
= METAFILE_AllocateRecord(metafile
,
687 sizeof(EmfPlusScaleWorldTransform
),
692 record
->Header
.Type
= EmfPlusRecordTypeScaleWorldTransform
;
693 record
->Header
.Flags
= (order
== MatrixOrderAppend
? 0x2000 : 0);
697 METAFILE_WriteRecords(metafile
);
703 GpStatus
METAFILE_MultiplyWorldTransform(GpMetafile
* metafile
, GDIPCONST GpMatrix
* matrix
, MatrixOrder order
)
705 if (metafile
->metafile_type
== MetafileTypeEmfPlusOnly
|| metafile
->metafile_type
== MetafileTypeEmfPlusDual
)
707 EmfPlusMultiplyWorldTransform
*record
;
710 stat
= METAFILE_AllocateRecord(metafile
,
711 sizeof(EmfPlusMultiplyWorldTransform
),
716 record
->Header
.Type
= EmfPlusRecordTypeMultiplyWorldTransform
;
717 record
->Header
.Flags
= (order
== MatrixOrderAppend
? 0x2000 : 0);
718 memcpy(record
->MatrixData
, matrix
->matrix
, sizeof(record
->MatrixData
));
720 METAFILE_WriteRecords(metafile
);
726 GpStatus
METAFILE_RotateWorldTransform(GpMetafile
* metafile
, REAL angle
, MatrixOrder order
)
728 if (metafile
->metafile_type
== MetafileTypeEmfPlusOnly
|| metafile
->metafile_type
== MetafileTypeEmfPlusDual
)
730 EmfPlusRotateWorldTransform
*record
;
733 stat
= METAFILE_AllocateRecord(metafile
,
734 sizeof(EmfPlusRotateWorldTransform
),
739 record
->Header
.Type
= EmfPlusRecordTypeRotateWorldTransform
;
740 record
->Header
.Flags
= (order
== MatrixOrderAppend
? 0x2000 : 0);
741 record
->Angle
= angle
;
743 METAFILE_WriteRecords(metafile
);
749 GpStatus
METAFILE_TranslateWorldTransform(GpMetafile
* metafile
, REAL dx
, REAL dy
, MatrixOrder order
)
751 if (metafile
->metafile_type
== MetafileTypeEmfPlusOnly
|| metafile
->metafile_type
== MetafileTypeEmfPlusDual
)
753 EmfPlusTranslateWorldTransform
*record
;
756 stat
= METAFILE_AllocateRecord(metafile
,
757 sizeof(EmfPlusTranslateWorldTransform
),
762 record
->Header
.Type
= EmfPlusRecordTypeTranslateWorldTransform
;
763 record
->Header
.Flags
= (order
== MatrixOrderAppend
? 0x2000 : 0);
767 METAFILE_WriteRecords(metafile
);
773 GpStatus
METAFILE_ResetWorldTransform(GpMetafile
* metafile
)
775 if (metafile
->metafile_type
== MetafileTypeEmfPlusOnly
|| metafile
->metafile_type
== MetafileTypeEmfPlusDual
)
777 EmfPlusRecordHeader
*record
;
780 stat
= METAFILE_AllocateRecord(metafile
,
781 sizeof(EmfPlusRecordHeader
),
786 record
->Type
= EmfPlusRecordTypeResetWorldTransform
;
789 METAFILE_WriteRecords(metafile
);
795 GpStatus
METAFILE_BeginContainer(GpMetafile
* metafile
, GDIPCONST GpRectF
*dstrect
,
796 GDIPCONST GpRectF
*srcrect
, GpUnit unit
, DWORD StackIndex
)
798 if (metafile
->metafile_type
== MetafileTypeEmfPlusOnly
|| metafile
->metafile_type
== MetafileTypeEmfPlusDual
)
800 EmfPlusBeginContainer
*record
;
803 stat
= METAFILE_AllocateRecord(metafile
, sizeof(*record
), (void**)&record
);
807 record
->Header
.Type
= EmfPlusRecordTypeBeginContainer
;
808 record
->Header
.Flags
= unit
& 0xff;
809 record
->DestRect
= *dstrect
;
810 record
->SrcRect
= *srcrect
;
811 record
->StackIndex
= StackIndex
;
813 METAFILE_WriteRecords(metafile
);
819 GpStatus
METAFILE_BeginContainerNoParams(GpMetafile
* metafile
, DWORD StackIndex
)
821 if (metafile
->metafile_type
== MetafileTypeEmfPlusOnly
|| metafile
->metafile_type
== MetafileTypeEmfPlusDual
)
823 EmfPlusContainerRecord
*record
;
826 stat
= METAFILE_AllocateRecord(metafile
,
827 sizeof(EmfPlusContainerRecord
),
832 record
->Header
.Type
= EmfPlusRecordTypeBeginContainerNoParams
;
833 record
->Header
.Flags
= 0;
834 record
->StackIndex
= StackIndex
;
836 METAFILE_WriteRecords(metafile
);
842 GpStatus
METAFILE_EndContainer(GpMetafile
* metafile
, DWORD StackIndex
)
844 if (metafile
->metafile_type
== MetafileTypeEmfPlusOnly
|| metafile
->metafile_type
== MetafileTypeEmfPlusDual
)
846 EmfPlusContainerRecord
*record
;
849 stat
= METAFILE_AllocateRecord(metafile
,
850 sizeof(EmfPlusContainerRecord
),
855 record
->Header
.Type
= EmfPlusRecordTypeEndContainer
;
856 record
->Header
.Flags
= 0;
857 record
->StackIndex
= StackIndex
;
859 METAFILE_WriteRecords(metafile
);
865 GpStatus
METAFILE_SaveGraphics(GpMetafile
* metafile
, DWORD StackIndex
)
867 if (metafile
->metafile_type
== MetafileTypeEmfPlusOnly
|| metafile
->metafile_type
== MetafileTypeEmfPlusDual
)
869 EmfPlusContainerRecord
*record
;
872 stat
= METAFILE_AllocateRecord(metafile
,
873 sizeof(EmfPlusContainerRecord
),
878 record
->Header
.Type
= EmfPlusRecordTypeSave
;
879 record
->Header
.Flags
= 0;
880 record
->StackIndex
= StackIndex
;
882 METAFILE_WriteRecords(metafile
);
888 GpStatus
METAFILE_RestoreGraphics(GpMetafile
* metafile
, DWORD StackIndex
)
890 if (metafile
->metafile_type
== MetafileTypeEmfPlusOnly
|| metafile
->metafile_type
== MetafileTypeEmfPlusDual
)
892 EmfPlusContainerRecord
*record
;
895 stat
= METAFILE_AllocateRecord(metafile
,
896 sizeof(EmfPlusContainerRecord
),
901 record
->Header
.Type
= EmfPlusRecordTypeRestore
;
902 record
->Header
.Flags
= 0;
903 record
->StackIndex
= StackIndex
;
905 METAFILE_WriteRecords(metafile
);
911 GpStatus
METAFILE_ReleaseDC(GpMetafile
* metafile
, HDC hdc
)
913 if (hdc
!= metafile
->record_dc
)
914 return InvalidParameter
;
919 GpStatus
METAFILE_GraphicsDeleted(GpMetafile
* metafile
)
923 stat
= METAFILE_WriteEndOfFile(metafile
);
924 metafile
->record_graphics
= NULL
;
926 metafile
->hemf
= CloseEnhMetaFile(metafile
->record_dc
);
927 metafile
->record_dc
= NULL
;
929 heap_free(metafile
->comment_data
);
930 metafile
->comment_data
= NULL
;
931 metafile
->comment_data_size
= 0;
935 MetafileHeader header
;
937 stat
= GdipGetMetafileHeaderFromEmf(metafile
->hemf
, &header
);
938 if (stat
== Ok
&& metafile
->auto_frame
&&
939 metafile
->auto_frame_max
.X
>= metafile
->auto_frame_min
.X
)
941 RECTL bounds_rc
, gdi_bounds_rc
;
942 REAL x_scale
= 2540.0 / header
.DpiX
;
943 REAL y_scale
= 2540.0 / header
.DpiY
;
947 bounds_rc
.left
= floorf(metafile
->auto_frame_min
.X
* x_scale
);
948 bounds_rc
.top
= floorf(metafile
->auto_frame_min
.Y
* y_scale
);
949 bounds_rc
.right
= ceilf(metafile
->auto_frame_max
.X
* x_scale
);
950 bounds_rc
.bottom
= ceilf(metafile
->auto_frame_max
.Y
* y_scale
);
952 gdi_bounds_rc
= header
.u
.EmfHeader
.rclBounds
;
953 if (gdi_bounds_rc
.right
> gdi_bounds_rc
.left
&& gdi_bounds_rc
.bottom
> gdi_bounds_rc
.top
)
955 bounds_rc
.left
= min(bounds_rc
.left
, gdi_bounds_rc
.left
);
956 bounds_rc
.top
= min(bounds_rc
.top
, gdi_bounds_rc
.top
);
957 bounds_rc
.right
= max(bounds_rc
.right
, gdi_bounds_rc
.right
);
958 bounds_rc
.bottom
= max(bounds_rc
.bottom
, gdi_bounds_rc
.bottom
);
961 buffer_size
= GetEnhMetaFileBits(metafile
->hemf
, 0, NULL
);
962 buffer
= heap_alloc(buffer_size
);
965 HENHMETAFILE new_hemf
;
967 GetEnhMetaFileBits(metafile
->hemf
, buffer_size
, buffer
);
969 ((ENHMETAHEADER
*)buffer
)->rclFrame
= bounds_rc
;
971 new_hemf
= SetEnhMetaFileBits(buffer_size
, buffer
);
975 DeleteEnhMetaFile(metafile
->hemf
);
976 metafile
->hemf
= new_hemf
;
987 stat
= GdipGetMetafileHeaderFromEmf(metafile
->hemf
, &header
);
991 metafile
->bounds
.X
= header
.X
;
992 metafile
->bounds
.Y
= header
.Y
;
993 metafile
->bounds
.Width
= header
.Width
;
994 metafile
->bounds
.Height
= header
.Height
;
998 if (stat
== Ok
&& metafile
->record_stream
)
1003 buffer_size
= GetEnhMetaFileBits(metafile
->hemf
, 0, NULL
);
1005 buffer
= heap_alloc(buffer_size
);
1010 GetEnhMetaFileBits(metafile
->hemf
, buffer_size
, buffer
);
1012 hr
= IStream_Write(metafile
->record_stream
, buffer
, buffer_size
, NULL
);
1015 stat
= hresult_to_status(hr
);
1023 if (metafile
->record_stream
)
1025 IStream_Release(metafile
->record_stream
);
1026 metafile
->record_stream
= NULL
;
1032 GpStatus WINGDIPAPI
GdipGetHemfFromMetafile(GpMetafile
*metafile
, HENHMETAFILE
*hEmf
)
1034 TRACE("(%p,%p)\n", metafile
, hEmf
);
1036 if (!metafile
|| !hEmf
|| !metafile
->hemf
)
1037 return InvalidParameter
;
1039 *hEmf
= metafile
->hemf
;
1040 metafile
->hemf
= NULL
;
1045 static void METAFILE_GetFinalGdiTransform(const GpMetafile
*metafile
, XFORM
*result
)
1047 const GpRectF
*rect
;
1050 /* This transforms metafile device space to output points. */
1051 rect
= &metafile
->src_rect
;
1052 pt
= metafile
->playback_points
;
1053 result
->eM11
= (pt
[1].X
- pt
[0].X
) / rect
->Width
;
1054 result
->eM21
= (pt
[2].X
- pt
[0].X
) / rect
->Height
;
1055 result
->eDx
= pt
[0].X
- result
->eM11
* rect
->X
- result
->eM21
* rect
->Y
;
1056 result
->eM12
= (pt
[1].Y
- pt
[0].Y
) / rect
->Width
;
1057 result
->eM22
= (pt
[2].Y
- pt
[0].Y
) / rect
->Height
;
1058 result
->eDy
= pt
[0].Y
- result
->eM12
* rect
->X
- result
->eM22
* rect
->Y
;
1061 static GpStatus
METAFILE_PlaybackUpdateGdiTransform(GpMetafile
*metafile
)
1063 XFORM combined
, final
;
1065 METAFILE_GetFinalGdiTransform(metafile
, &final
);
1067 CombineTransform(&combined
, &metafile
->gdiworldtransform
, &final
);
1069 SetGraphicsMode(metafile
->playback_dc
, GM_ADVANCED
);
1070 SetWorldTransform(metafile
->playback_dc
, &combined
);
1075 static GpStatus
METAFILE_PlaybackGetDC(GpMetafile
*metafile
)
1079 stat
= GdipGetDC(metafile
->playback_graphics
, &metafile
->playback_dc
);
1083 static const XFORM identity
= {1, 0, 0, 1, 0, 0};
1085 metafile
->gdiworldtransform
= identity
;
1086 METAFILE_PlaybackUpdateGdiTransform(metafile
);
1092 static void METAFILE_PlaybackReleaseDC(GpMetafile
*metafile
)
1094 if (metafile
->playback_dc
)
1096 GdipReleaseDC(metafile
->playback_graphics
, metafile
->playback_dc
);
1097 metafile
->playback_dc
= NULL
;
1101 static GpStatus
METAFILE_PlaybackUpdateClip(GpMetafile
*metafile
)
1104 stat
= GdipCombineRegionRegion(metafile
->playback_graphics
->clip
, metafile
->base_clip
, CombineModeReplace
);
1106 stat
= GdipCombineRegionRegion(metafile
->playback_graphics
->clip
, metafile
->clip
, CombineModeIntersect
);
1110 static GpStatus
METAFILE_PlaybackUpdateWorldTransform(GpMetafile
*metafile
)
1112 GpMatrix
*real_transform
;
1115 stat
= GdipCreateMatrix3(&metafile
->src_rect
, metafile
->playback_points
, &real_transform
);
1119 REAL scale
= units_to_pixels(1.0, metafile
->page_unit
, 96.0);
1121 if (metafile
->page_unit
!= UnitDisplay
)
1122 scale
*= metafile
->page_scale
;
1124 stat
= GdipScaleMatrix(real_transform
, scale
, scale
, MatrixOrderPrepend
);
1127 stat
= GdipMultiplyMatrix(real_transform
, metafile
->world_transform
, MatrixOrderPrepend
);
1130 stat
= GdipSetWorldTransform(metafile
->playback_graphics
, real_transform
);
1132 GdipDeleteMatrix(real_transform
);
1138 GpStatus WINGDIPAPI
GdipPlayMetafileRecord(GDIPCONST GpMetafile
*metafile
,
1139 EmfPlusRecordType recordType
, UINT flags
, UINT dataSize
, GDIPCONST BYTE
*data
)
1142 GpMetafile
*real_metafile
= (GpMetafile
*)metafile
;
1144 TRACE("(%p,%x,%x,%d,%p)\n", metafile
, recordType
, flags
, dataSize
, data
);
1146 if (!metafile
|| (dataSize
&& !data
) || !metafile
->playback_graphics
)
1147 return InvalidParameter
;
1149 if (recordType
>= 1 && recordType
<= 0x7a)
1151 /* regular EMF record */
1152 if (metafile
->playback_dc
)
1154 ENHMETARECORD
*record
;
1158 case EMR_SETMAPMODE
:
1161 case EMR_SETWINDOWORGEX
:
1162 case EMR_SETWINDOWEXTEX
:
1163 case EMR_SETVIEWPORTORGEX
:
1164 case EMR_SETVIEWPORTEXTEX
:
1165 case EMR_SCALEVIEWPORTEXTEX
:
1166 case EMR_SCALEWINDOWEXTEX
:
1167 case EMR_MODIFYWORLDTRANSFORM
:
1168 FIXME("not implemented for record type %x\n", recordType
);
1170 case EMR_SETWORLDTRANSFORM
:
1172 const XFORM
* xform
= (void*)data
;
1173 real_metafile
->gdiworldtransform
= *xform
;
1174 METAFILE_PlaybackUpdateGdiTransform(real_metafile
);
1177 case EMR_EXTSELECTCLIPRGN
:
1179 DWORD rgndatasize
= *(DWORD
*)data
;
1180 DWORD mode
= *(DWORD
*)(data
+ 4);
1181 const RGNDATA
*rgndata
= (const RGNDATA
*)(data
+ 8);
1188 METAFILE_GetFinalGdiTransform(metafile
, &final
);
1190 hrgn
= ExtCreateRegion(&final
, rgndatasize
, rgndata
);
1193 ExtSelectClipRgn(metafile
->playback_dc
, hrgn
, mode
);
1203 record
= heap_alloc_zero(dataSize
+ 8);
1207 record
->iType
= recordType
;
1208 record
->nSize
= dataSize
+ 8;
1209 memcpy(record
->dParm
, data
, dataSize
);
1211 PlayEnhMetaFileRecord(metafile
->playback_dc
, metafile
->handle_table
,
1212 record
, metafile
->handle_count
);
1222 EmfPlusRecordHeader
*header
= (EmfPlusRecordHeader
*)(data
)-1;
1224 METAFILE_PlaybackReleaseDC((GpMetafile
*)metafile
);
1228 case EmfPlusRecordTypeHeader
:
1229 case EmfPlusRecordTypeEndOfFile
:
1231 case EmfPlusRecordTypeGetDC
:
1232 METAFILE_PlaybackGetDC((GpMetafile
*)metafile
);
1234 case EmfPlusRecordTypeClear
:
1236 EmfPlusClear
*record
= (EmfPlusClear
*)header
;
1238 return GdipGraphicsClear(metafile
->playback_graphics
, record
->Color
);
1240 case EmfPlusRecordTypeFillRects
:
1242 EmfPlusFillRects
*record
= (EmfPlusFillRects
*)header
;
1243 GpBrush
*brush
, *temp_brush
=NULL
;
1244 GpRectF
*rects
, *temp_rects
=NULL
;
1246 if (dataSize
+ sizeof(EmfPlusRecordHeader
) < sizeof(EmfPlusFillRects
))
1247 return InvalidParameter
;
1251 if (dataSize
+ sizeof(EmfPlusRecordHeader
) < sizeof(EmfPlusFillRects
) + sizeof(EmfPlusRect
) * record
->Count
)
1252 return InvalidParameter
;
1256 if (dataSize
+ sizeof(EmfPlusRecordHeader
) < sizeof(EmfPlusFillRects
) + sizeof(GpRectF
) * record
->Count
)
1257 return InvalidParameter
;
1262 stat
= GdipCreateSolidFill((ARGB
)record
->BrushID
, (GpSolidFill
**)&temp_brush
);
1267 FIXME("brush deserialization not implemented\n");
1268 return NotImplemented
;
1275 EmfPlusRect
*int_rects
= (EmfPlusRect
*)(record
+1);
1278 rects
= temp_rects
= heap_alloc_zero(sizeof(GpRectF
) * record
->Count
);
1281 for (i
=0; i
<record
->Count
; i
++)
1283 rects
[i
].X
= int_rects
[i
].X
;
1284 rects
[i
].Y
= int_rects
[i
].Y
;
1285 rects
[i
].Width
= int_rects
[i
].Width
;
1286 rects
[i
].Height
= int_rects
[i
].Height
;
1293 rects
= (GpRectF
*)(record
+1);
1298 stat
= GdipFillRectangles(metafile
->playback_graphics
, brush
, rects
, record
->Count
);
1301 GdipDeleteBrush(temp_brush
);
1302 heap_free(temp_rects
);
1306 case EmfPlusRecordTypeSetClipRect
:
1308 EmfPlusSetClipRect
*record
= (EmfPlusSetClipRect
*)header
;
1309 CombineMode mode
= (CombineMode
)((flags
>> 8) & 0xf);
1311 GpMatrix world_to_device
;
1313 if (dataSize
+ sizeof(EmfPlusRecordHeader
) < sizeof(*record
))
1314 return InvalidParameter
;
1316 stat
= GdipCreateRegionRect(&record
->ClipRect
, ®ion
);
1320 get_graphics_transform(real_metafile
->playback_graphics
,
1321 CoordinateSpaceDevice
, CoordinateSpaceWorld
, &world_to_device
);
1323 GdipTransformRegion(region
, &world_to_device
);
1325 GdipCombineRegionRegion(real_metafile
->clip
, region
, mode
);
1327 GdipDeleteRegion(region
);
1330 return METAFILE_PlaybackUpdateClip(real_metafile
);
1332 case EmfPlusRecordTypeSetPageTransform
:
1334 EmfPlusSetPageTransform
*record
= (EmfPlusSetPageTransform
*)header
;
1335 GpUnit unit
= (GpUnit
)flags
;
1337 if (dataSize
+ sizeof(EmfPlusRecordHeader
) < sizeof(EmfPlusSetPageTransform
))
1338 return InvalidParameter
;
1340 real_metafile
->page_unit
= unit
;
1341 real_metafile
->page_scale
= record
->PageScale
;
1343 return METAFILE_PlaybackUpdateWorldTransform(real_metafile
);
1345 case EmfPlusRecordTypeSetWorldTransform
:
1347 EmfPlusSetWorldTransform
*record
= (EmfPlusSetWorldTransform
*)header
;
1349 if (dataSize
+ sizeof(EmfPlusRecordHeader
) < sizeof(EmfPlusSetWorldTransform
))
1350 return InvalidParameter
;
1352 memcpy(real_metafile
->world_transform
->matrix
, record
->MatrixData
, sizeof(record
->MatrixData
));
1354 return METAFILE_PlaybackUpdateWorldTransform(real_metafile
);
1356 case EmfPlusRecordTypeScaleWorldTransform
:
1358 EmfPlusScaleWorldTransform
*record
= (EmfPlusScaleWorldTransform
*)header
;
1359 MatrixOrder order
= (flags
& 0x2000) ? MatrixOrderAppend
: MatrixOrderPrepend
;
1361 if (dataSize
+ sizeof(EmfPlusRecordHeader
) < sizeof(EmfPlusScaleWorldTransform
))
1362 return InvalidParameter
;
1364 GdipScaleMatrix(real_metafile
->world_transform
, record
->Sx
, record
->Sy
, order
);
1366 return METAFILE_PlaybackUpdateWorldTransform(real_metafile
);
1368 case EmfPlusRecordTypeMultiplyWorldTransform
:
1370 EmfPlusMultiplyWorldTransform
*record
= (EmfPlusMultiplyWorldTransform
*)header
;
1371 MatrixOrder order
= (flags
& 0x2000) ? MatrixOrderAppend
: MatrixOrderPrepend
;
1374 if (dataSize
+ sizeof(EmfPlusRecordHeader
) < sizeof(EmfPlusMultiplyWorldTransform
))
1375 return InvalidParameter
;
1377 memcpy(matrix
.matrix
, record
->MatrixData
, sizeof(matrix
.matrix
));
1379 GdipMultiplyMatrix(real_metafile
->world_transform
, &matrix
, order
);
1381 return METAFILE_PlaybackUpdateWorldTransform(real_metafile
);
1383 case EmfPlusRecordTypeRotateWorldTransform
:
1385 EmfPlusRotateWorldTransform
*record
= (EmfPlusRotateWorldTransform
*)header
;
1386 MatrixOrder order
= (flags
& 0x2000) ? MatrixOrderAppend
: MatrixOrderPrepend
;
1388 if (dataSize
+ sizeof(EmfPlusRecordHeader
) < sizeof(EmfPlusRotateWorldTransform
))
1389 return InvalidParameter
;
1391 GdipRotateMatrix(real_metafile
->world_transform
, record
->Angle
, order
);
1393 return METAFILE_PlaybackUpdateWorldTransform(real_metafile
);
1395 case EmfPlusRecordTypeTranslateWorldTransform
:
1397 EmfPlusTranslateWorldTransform
*record
= (EmfPlusTranslateWorldTransform
*)header
;
1398 MatrixOrder order
= (flags
& 0x2000) ? MatrixOrderAppend
: MatrixOrderPrepend
;
1400 if (dataSize
+ sizeof(EmfPlusRecordHeader
) < sizeof(EmfPlusTranslateWorldTransform
))
1401 return InvalidParameter
;
1403 GdipTranslateMatrix(real_metafile
->world_transform
, record
->dx
, record
->dy
, order
);
1405 return METAFILE_PlaybackUpdateWorldTransform(real_metafile
);
1407 case EmfPlusRecordTypeResetWorldTransform
:
1409 GdipSetMatrixElements(real_metafile
->world_transform
, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0);
1411 return METAFILE_PlaybackUpdateWorldTransform(real_metafile
);
1413 case EmfPlusRecordTypeBeginContainer
:
1415 EmfPlusBeginContainer
*record
= (EmfPlusBeginContainer
*)header
;
1418 REAL scale_x
, scale_y
;
1419 GpRectF scaled_srcrect
;
1422 cont
= heap_alloc_zero(sizeof(*cont
));
1426 stat
= GdipCloneRegion(metafile
->clip
, &cont
->clip
);
1433 stat
= GdipBeginContainer2(metafile
->playback_graphics
, &cont
->state
);
1437 GdipDeleteRegion(cont
->clip
);
1442 cont
->id
= record
->StackIndex
;
1443 cont
->type
= BEGIN_CONTAINER
;
1444 cont
->world_transform
= *metafile
->world_transform
;
1445 cont
->page_unit
= metafile
->page_unit
;
1446 cont
->page_scale
= metafile
->page_scale
;
1447 list_add_head(&real_metafile
->containers
, &cont
->entry
);
1449 unit
= record
->Header
.Flags
& 0xff;
1451 scale_x
= units_to_pixels(1.0, unit
, metafile
->image
.xres
);
1452 scale_y
= units_to_pixels(1.0, unit
, metafile
->image
.yres
);
1454 scaled_srcrect
.X
= scale_x
* record
->SrcRect
.X
;
1455 scaled_srcrect
.Y
= scale_y
* record
->SrcRect
.Y
;
1456 scaled_srcrect
.Width
= scale_x
* record
->SrcRect
.Width
;
1457 scaled_srcrect
.Height
= scale_y
* record
->SrcRect
.Height
;
1459 transform
.matrix
[0] = record
->DestRect
.Width
/ scaled_srcrect
.Width
;
1460 transform
.matrix
[1] = 0.0;
1461 transform
.matrix
[2] = 0.0;
1462 transform
.matrix
[3] = record
->DestRect
.Height
/ scaled_srcrect
.Height
;
1463 transform
.matrix
[4] = record
->DestRect
.X
- scaled_srcrect
.X
;
1464 transform
.matrix
[5] = record
->DestRect
.Y
- scaled_srcrect
.Y
;
1466 GdipMultiplyMatrix(real_metafile
->world_transform
, &transform
, MatrixOrderPrepend
);
1468 return METAFILE_PlaybackUpdateWorldTransform(real_metafile
);
1470 case EmfPlusRecordTypeBeginContainerNoParams
:
1471 case EmfPlusRecordTypeSave
:
1473 EmfPlusContainerRecord
*record
= (EmfPlusContainerRecord
*)header
;
1476 cont
= heap_alloc_zero(sizeof(*cont
));
1480 stat
= GdipCloneRegion(metafile
->clip
, &cont
->clip
);
1487 if (recordType
== EmfPlusRecordTypeBeginContainerNoParams
)
1488 stat
= GdipBeginContainer2(metafile
->playback_graphics
, &cont
->state
);
1490 stat
= GdipSaveGraphics(metafile
->playback_graphics
, &cont
->state
);
1494 GdipDeleteRegion(cont
->clip
);
1499 cont
->id
= record
->StackIndex
;
1500 if (recordType
== EmfPlusRecordTypeBeginContainerNoParams
)
1501 cont
->type
= BEGIN_CONTAINER
;
1503 cont
->type
= SAVE_GRAPHICS
;
1504 cont
->world_transform
= *metafile
->world_transform
;
1505 cont
->page_unit
= metafile
->page_unit
;
1506 cont
->page_scale
= metafile
->page_scale
;
1507 list_add_head(&real_metafile
->containers
, &cont
->entry
);
1511 case EmfPlusRecordTypeEndContainer
:
1512 case EmfPlusRecordTypeRestore
:
1514 EmfPlusContainerRecord
*record
= (EmfPlusContainerRecord
*)header
;
1516 enum container_type type
;
1519 if (recordType
== EmfPlusRecordTypeEndContainer
)
1520 type
= BEGIN_CONTAINER
;
1522 type
= SAVE_GRAPHICS
;
1524 LIST_FOR_EACH_ENTRY(cont
, &real_metafile
->containers
, container
, entry
)
1526 if (cont
->id
== record
->StackIndex
&& cont
->type
== type
)
1537 /* pop any newer items on the stack */
1538 while ((cont2
= LIST_ENTRY(list_head(&real_metafile
->containers
), container
, entry
)) != cont
)
1540 list_remove(&cont2
->entry
);
1541 GdipDeleteRegion(cont2
->clip
);
1545 if (type
== BEGIN_CONTAINER
)
1546 GdipEndContainer(real_metafile
->playback_graphics
, cont
->state
);
1548 GdipRestoreGraphics(real_metafile
->playback_graphics
, cont
->state
);
1550 *real_metafile
->world_transform
= cont
->world_transform
;
1551 real_metafile
->page_unit
= cont
->page_unit
;
1552 real_metafile
->page_scale
= cont
->page_scale
;
1553 GdipCombineRegionRegion(real_metafile
->clip
, cont
->clip
, CombineModeReplace
);
1555 list_remove(&cont
->entry
);
1556 GdipDeleteRegion(cont
->clip
);
1563 FIXME("Not implemented for record type %x\n", recordType
);
1564 return NotImplemented
;
1571 struct enum_metafile_data
1573 EnumerateMetafileProc callback
;
1574 void *callback_data
;
1575 GpMetafile
*metafile
;
1578 static int CALLBACK
enum_metafile_proc(HDC hDC
, HANDLETABLE
*lpHTable
, const ENHMETARECORD
*lpEMFR
,
1579 int nObj
, LPARAM lpData
)
1582 struct enum_metafile_data
*data
= (struct enum_metafile_data
*)lpData
;
1585 data
->metafile
->handle_table
= lpHTable
;
1586 data
->metafile
->handle_count
= nObj
;
1588 /* First check for an EMF+ record. */
1589 if (lpEMFR
->iType
== EMR_GDICOMMENT
)
1591 const EMRGDICOMMENT
*comment
= (const EMRGDICOMMENT
*)lpEMFR
;
1593 if (comment
->cbData
>= 4 && memcmp(comment
->Data
, "EMF+", 4) == 0)
1597 while (offset
+ sizeof(EmfPlusRecordHeader
) <= comment
->cbData
)
1599 const EmfPlusRecordHeader
*record
= (const EmfPlusRecordHeader
*)&comment
->Data
[offset
];
1601 if (record
->DataSize
)
1602 pStr
= (const BYTE
*)(record
+1);
1606 ret
= data
->callback(record
->Type
, record
->Flags
, record
->DataSize
,
1607 pStr
, data
->callback_data
);
1612 offset
+= record
->Size
;
1619 if (lpEMFR
->nSize
!= 8)
1620 pStr
= (const BYTE
*)lpEMFR
->dParm
;
1624 return data
->callback(lpEMFR
->iType
, 0, lpEMFR
->nSize
-8,
1625 pStr
, data
->callback_data
);
1628 GpStatus WINGDIPAPI
GdipEnumerateMetafileSrcRectDestPoints(GpGraphics
*graphics
,
1629 GDIPCONST GpMetafile
*metafile
, GDIPCONST GpPointF
*destPoints
, INT count
,
1630 GDIPCONST GpRectF
*srcRect
, Unit srcUnit
, EnumerateMetafileProc callback
,
1631 VOID
*callbackData
, GDIPCONST GpImageAttributes
*imageAttributes
)
1633 struct enum_metafile_data data
;
1635 GpMetafile
*real_metafile
= (GpMetafile
*)metafile
; /* whoever made this const was joking */
1636 GraphicsContainer state
;
1639 TRACE("(%p,%p,%p,%i,%p,%i,%p,%p,%p)\n", graphics
, metafile
,
1640 destPoints
, count
, srcRect
, srcUnit
, callback
, callbackData
,
1643 if (!graphics
|| !metafile
|| !destPoints
|| count
!= 3 || !srcRect
)
1644 return InvalidParameter
;
1646 if (!metafile
->hemf
)
1647 return InvalidParameter
;
1649 if (metafile
->playback_graphics
)
1652 TRACE("%s %i -> %s %s %s\n", debugstr_rectf(srcRect
), srcUnit
,
1653 debugstr_pointf(&destPoints
[0]), debugstr_pointf(&destPoints
[1]),
1654 debugstr_pointf(&destPoints
[2]));
1656 data
.callback
= callback
;
1657 data
.callback_data
= callbackData
;
1658 data
.metafile
= real_metafile
;
1660 real_metafile
->playback_graphics
= graphics
;
1661 real_metafile
->playback_dc
= NULL
;
1662 real_metafile
->src_rect
= *srcRect
;
1664 memcpy(real_metafile
->playback_points
, destPoints
, sizeof(PointF
) * 3);
1665 stat
= GdipTransformPoints(graphics
, CoordinateSpaceDevice
, CoordinateSpaceWorld
, real_metafile
->playback_points
, 3);
1668 stat
= GdipBeginContainer2(graphics
, &state
);
1672 stat
= GdipSetPageScale(graphics
, 1.0);
1675 stat
= GdipSetPageUnit(graphics
, UnitPixel
);
1678 stat
= GdipResetWorldTransform(graphics
);
1681 stat
= GdipCreateRegion(&real_metafile
->base_clip
);
1684 stat
= GdipGetClip(graphics
, real_metafile
->base_clip
);
1687 stat
= GdipCreateRegion(&real_metafile
->clip
);
1690 stat
= GdipCreatePath(FillModeAlternate
, &dst_path
);
1694 GpPointF clip_points
[4];
1696 clip_points
[0] = real_metafile
->playback_points
[0];
1697 clip_points
[1] = real_metafile
->playback_points
[1];
1698 clip_points
[2].X
= real_metafile
->playback_points
[1].X
+ real_metafile
->playback_points
[2].X
1699 - real_metafile
->playback_points
[0].X
;
1700 clip_points
[2].Y
= real_metafile
->playback_points
[1].Y
+ real_metafile
->playback_points
[2].Y
1701 - real_metafile
->playback_points
[0].Y
;
1702 clip_points
[3] = real_metafile
->playback_points
[2];
1704 stat
= GdipAddPathPolygon(dst_path
, clip_points
, 4);
1707 stat
= GdipCombineRegionPath(real_metafile
->base_clip
, dst_path
, CombineModeIntersect
);
1709 GdipDeletePath(dst_path
);
1713 stat
= GdipCreateMatrix(&real_metafile
->world_transform
);
1717 real_metafile
->page_unit
= UnitDisplay
;
1718 real_metafile
->page_scale
= 1.0;
1719 stat
= METAFILE_PlaybackUpdateWorldTransform(real_metafile
);
1724 stat
= METAFILE_PlaybackUpdateClip(real_metafile
);
1727 if (stat
== Ok
&& (metafile
->metafile_type
== MetafileTypeEmf
||
1728 metafile
->metafile_type
== MetafileTypeWmfPlaceable
||
1729 metafile
->metafile_type
== MetafileTypeWmf
))
1730 stat
= METAFILE_PlaybackGetDC(real_metafile
);
1733 EnumEnhMetaFile(0, metafile
->hemf
, enum_metafile_proc
, &data
, NULL
);
1735 METAFILE_PlaybackReleaseDC(real_metafile
);
1737 GdipDeleteMatrix(real_metafile
->world_transform
);
1738 real_metafile
->world_transform
= NULL
;
1740 GdipDeleteRegion(real_metafile
->base_clip
);
1741 real_metafile
->base_clip
= NULL
;
1743 GdipDeleteRegion(real_metafile
->clip
);
1744 real_metafile
->clip
= NULL
;
1746 while (list_head(&real_metafile
->containers
))
1748 container
* cont
= LIST_ENTRY(list_head(&real_metafile
->containers
), container
, entry
);
1749 list_remove(&cont
->entry
);
1750 GdipDeleteRegion(cont
->clip
);
1754 GdipEndContainer(graphics
, state
);
1757 real_metafile
->playback_graphics
= NULL
;
1762 GpStatus WINGDIPAPI
GdipEnumerateMetafileDestRect(GpGraphics
*graphics
,
1763 GDIPCONST GpMetafile
*metafile
, GDIPCONST GpRectF
*dest
,
1764 EnumerateMetafileProc callback
, VOID
*cb_data
, GDIPCONST GpImageAttributes
*attrs
)
1768 if (!graphics
|| !metafile
|| !dest
) return InvalidParameter
;
1770 points
[0].X
= points
[2].X
= dest
->X
;
1771 points
[0].Y
= points
[1].Y
= dest
->Y
;
1772 points
[1].X
= dest
->X
+ dest
->Width
;
1773 points
[2].Y
= dest
->Y
+ dest
->Height
;
1775 return GdipEnumerateMetafileSrcRectDestPoints(graphics
, metafile
, points
, 3,
1776 &metafile
->bounds
, metafile
->unit
, callback
, cb_data
, attrs
);
1779 GpStatus WINGDIPAPI
GdipEnumerateMetafileDestRectI(GpGraphics
*graphics
,
1780 GDIPCONST GpMetafile
*metafile
, GDIPCONST GpRect
*dest
,
1781 EnumerateMetafileProc callback
, VOID
*cb_data
, GDIPCONST GpImageAttributes
*attrs
)
1785 if (!graphics
|| !metafile
|| !dest
) return InvalidParameter
;
1789 destf
.Width
= dest
->Width
;
1790 destf
.Height
= dest
->Height
;
1792 return GdipEnumerateMetafileDestRect(graphics
, metafile
, &destf
, callback
, cb_data
, attrs
);
1795 GpStatus WINGDIPAPI
GdipEnumerateMetafileDestPoint(GpGraphics
*graphics
,
1796 GDIPCONST GpMetafile
*metafile
, GDIPCONST GpPointF
*dest
,
1797 EnumerateMetafileProc callback
, VOID
*cb_data
, GDIPCONST GpImageAttributes
*attrs
)
1801 if (!graphics
|| !metafile
|| !dest
) return InvalidParameter
;
1805 destf
.Width
= units_to_pixels(metafile
->bounds
.Width
, metafile
->unit
, metafile
->image
.xres
);
1806 destf
.Height
= units_to_pixels(metafile
->bounds
.Height
, metafile
->unit
, metafile
->image
.yres
);
1808 return GdipEnumerateMetafileDestRect(graphics
, metafile
, &destf
, callback
, cb_data
, attrs
);
1811 GpStatus WINGDIPAPI
GdipEnumerateMetafileDestPointI(GpGraphics
*graphics
,
1812 GDIPCONST GpMetafile
*metafile
, GDIPCONST GpPoint
*dest
,
1813 EnumerateMetafileProc callback
, VOID
*cb_data
, GDIPCONST GpImageAttributes
*attrs
)
1817 if (!graphics
|| !metafile
|| !dest
) return InvalidParameter
;
1822 return GdipEnumerateMetafileDestPoint(graphics
, metafile
, &ptf
, callback
, cb_data
, attrs
);
1825 GpStatus WINGDIPAPI
GdipGetMetafileHeaderFromMetafile(GpMetafile
* metafile
,
1826 MetafileHeader
* header
)
1830 TRACE("(%p, %p)\n", metafile
, header
);
1832 if(!metafile
|| !header
)
1833 return InvalidParameter
;
1837 status
= GdipGetMetafileHeaderFromEmf(metafile
->hemf
, header
);
1838 if (status
!= Ok
) return status
;
1842 memset(header
, 0, sizeof(*header
));
1843 header
->Version
= 0xdbc01002;
1846 header
->Type
= metafile
->metafile_type
;
1847 header
->DpiX
= metafile
->image
.xres
;
1848 header
->DpiY
= metafile
->image
.yres
;
1849 header
->Width
= gdip_round(metafile
->bounds
.Width
);
1850 header
->Height
= gdip_round(metafile
->bounds
.Height
);
1855 static int CALLBACK
get_emfplus_header_proc(HDC hDC
, HANDLETABLE
*lpHTable
, const ENHMETARECORD
*lpEMFR
,
1856 int nObj
, LPARAM lpData
)
1858 EmfPlusHeader
*dst_header
= (EmfPlusHeader
*)lpData
;
1860 if (lpEMFR
->iType
== EMR_GDICOMMENT
)
1862 const EMRGDICOMMENT
*comment
= (const EMRGDICOMMENT
*)lpEMFR
;
1864 if (comment
->cbData
>= 4 && memcmp(comment
->Data
, "EMF+", 4) == 0)
1866 const EmfPlusRecordHeader
*header
= (const EmfPlusRecordHeader
*)&comment
->Data
[4];
1868 if (4 + sizeof(EmfPlusHeader
) <= comment
->cbData
&&
1869 header
->Type
== EmfPlusRecordTypeHeader
)
1871 memcpy(dst_header
, header
, sizeof(*dst_header
));
1875 else if (lpEMFR
->iType
== EMR_HEADER
)
1881 GpStatus WINGDIPAPI
GdipGetMetafileHeaderFromEmf(HENHMETAFILE hemf
,
1882 MetafileHeader
*header
)
1884 ENHMETAHEADER3 emfheader
;
1885 EmfPlusHeader emfplusheader
;
1886 MetafileType metafile_type
;
1888 TRACE("(%p,%p)\n", hemf
, header
);
1890 if(!hemf
|| !header
)
1891 return InvalidParameter
;
1893 if (GetEnhMetaFileHeader(hemf
, sizeof(emfheader
), (ENHMETAHEADER
*)&emfheader
) == 0)
1894 return GenericError
;
1896 emfplusheader
.Header
.Type
= 0;
1898 EnumEnhMetaFile(NULL
, hemf
, get_emfplus_header_proc
, &emfplusheader
, NULL
);
1900 if (emfplusheader
.Header
.Type
== EmfPlusRecordTypeHeader
)
1902 if ((emfplusheader
.Header
.Flags
& 1) == 1)
1903 metafile_type
= MetafileTypeEmfPlusDual
;
1905 metafile_type
= MetafileTypeEmfPlusOnly
;
1908 metafile_type
= MetafileTypeEmf
;
1910 header
->Type
= metafile_type
;
1911 header
->Size
= emfheader
.nBytes
;
1912 header
->DpiX
= (REAL
)emfheader
.szlDevice
.cx
* 25.4 / emfheader
.szlMillimeters
.cx
;
1913 header
->DpiY
= (REAL
)emfheader
.szlDevice
.cy
* 25.4 / emfheader
.szlMillimeters
.cy
;
1914 header
->X
= gdip_round((REAL
)emfheader
.rclFrame
.left
/ 2540.0 * header
->DpiX
);
1915 header
->Y
= gdip_round((REAL
)emfheader
.rclFrame
.top
/ 2540.0 * header
->DpiY
);
1916 header
->Width
= gdip_round((REAL
)(emfheader
.rclFrame
.right
- emfheader
.rclFrame
.left
) / 2540.0 * header
->DpiX
);
1917 header
->Height
= gdip_round((REAL
)(emfheader
.rclFrame
.bottom
- emfheader
.rclFrame
.top
) / 2540.0 * header
->DpiY
);
1918 header
->u
.EmfHeader
= emfheader
;
1920 if (metafile_type
== MetafileTypeEmfPlusDual
|| metafile_type
== MetafileTypeEmfPlusOnly
)
1922 header
->Version
= emfplusheader
.Version
;
1923 header
->EmfPlusFlags
= emfplusheader
.EmfPlusFlags
;
1924 header
->EmfPlusHeaderSize
= emfplusheader
.Header
.Size
;
1925 header
->LogicalDpiX
= emfplusheader
.LogicalDpiX
;
1926 header
->LogicalDpiY
= emfplusheader
.LogicalDpiY
;
1930 header
->Version
= emfheader
.nVersion
;
1931 header
->EmfPlusFlags
= 0;
1932 header
->EmfPlusHeaderSize
= 0;
1933 header
->LogicalDpiX
= 0;
1934 header
->LogicalDpiY
= 0;
1940 GpStatus WINGDIPAPI
GdipGetMetafileHeaderFromWmf(HMETAFILE hwmf
,
1941 GDIPCONST WmfPlaceableFileHeader
*placeable
, MetafileHeader
*header
)
1944 GpMetafile
*metafile
;
1946 TRACE("(%p,%p,%p)\n", hwmf
, placeable
, header
);
1948 status
= GdipCreateMetafileFromWmf(hwmf
, FALSE
, placeable
, &metafile
);
1951 status
= GdipGetMetafileHeaderFromMetafile(metafile
, header
);
1952 GdipDisposeImage(&metafile
->image
);
1957 GpStatus WINGDIPAPI
GdipGetMetafileHeaderFromFile(GDIPCONST WCHAR
*filename
,
1958 MetafileHeader
*header
)
1961 GpMetafile
*metafile
;
1963 TRACE("(%s,%p)\n", debugstr_w(filename
), header
);
1965 if (!filename
|| !header
)
1966 return InvalidParameter
;
1968 status
= GdipCreateMetafileFromFile(filename
, &metafile
);
1971 status
= GdipGetMetafileHeaderFromMetafile(metafile
, header
);
1972 GdipDisposeImage(&metafile
->image
);
1977 GpStatus WINGDIPAPI
GdipGetMetafileHeaderFromStream(IStream
*stream
,
1978 MetafileHeader
*header
)
1981 GpMetafile
*metafile
;
1983 TRACE("(%p,%p)\n", stream
, header
);
1985 if (!stream
|| !header
)
1986 return InvalidParameter
;
1988 status
= GdipCreateMetafileFromStream(stream
, &metafile
);
1991 status
= GdipGetMetafileHeaderFromMetafile(metafile
, header
);
1992 GdipDisposeImage(&metafile
->image
);
1997 GpStatus WINGDIPAPI
GdipCreateMetafileFromEmf(HENHMETAFILE hemf
, BOOL
delete,
1998 GpMetafile
**metafile
)
2001 MetafileHeader header
;
2003 TRACE("(%p,%i,%p)\n", hemf
, delete, metafile
);
2005 if(!hemf
|| !metafile
)
2006 return InvalidParameter
;
2008 stat
= GdipGetMetafileHeaderFromEmf(hemf
, &header
);
2012 *metafile
= heap_alloc_zero(sizeof(GpMetafile
));
2016 (*metafile
)->image
.type
= ImageTypeMetafile
;
2017 (*metafile
)->image
.format
= ImageFormatEMF
;
2018 (*metafile
)->image
.frame_count
= 1;
2019 (*metafile
)->image
.xres
= header
.DpiX
;
2020 (*metafile
)->image
.yres
= header
.DpiY
;
2021 (*metafile
)->bounds
.X
= (REAL
)header
.u
.EmfHeader
.rclFrame
.left
/ 2540.0 * header
.DpiX
;
2022 (*metafile
)->bounds
.Y
= (REAL
)header
.u
.EmfHeader
.rclFrame
.top
/ 2540.0 * header
.DpiY
;
2023 (*metafile
)->bounds
.Width
= (REAL
)(header
.u
.EmfHeader
.rclFrame
.right
- header
.u
.EmfHeader
.rclFrame
.left
)
2024 / 2540.0 * header
.DpiX
;
2025 (*metafile
)->bounds
.Height
= (REAL
)(header
.u
.EmfHeader
.rclFrame
.bottom
- header
.u
.EmfHeader
.rclFrame
.top
)
2026 / 2540.0 * header
.DpiY
;
2027 (*metafile
)->unit
= UnitPixel
;
2028 (*metafile
)->metafile_type
= header
.Type
;
2029 (*metafile
)->hemf
= hemf
;
2030 (*metafile
)->preserve_hemf
= !delete;
2031 list_init(&(*metafile
)->containers
);
2033 TRACE("<-- %p\n", *metafile
);
2038 GpStatus WINGDIPAPI
GdipCreateMetafileFromWmf(HMETAFILE hwmf
, BOOL
delete,
2039 GDIPCONST WmfPlaceableFileHeader
* placeable
, GpMetafile
**metafile
)
2044 GpStatus retval
= Ok
;
2046 TRACE("(%p, %d, %p, %p)\n", hwmf
, delete, placeable
, metafile
);
2048 if(!hwmf
|| !metafile
)
2049 return InvalidParameter
;
2052 read
= GetMetaFileBitsEx(hwmf
, 0, NULL
);
2054 return GenericError
;
2055 copy
= heap_alloc_zero(read
);
2056 GetMetaFileBitsEx(hwmf
, read
, copy
);
2058 hemf
= SetWinMetaFileBits(read
, copy
, NULL
, NULL
);
2061 /* FIXME: We should store and use hwmf instead of converting to hemf */
2062 retval
= GdipCreateMetafileFromEmf(hemf
, TRUE
, metafile
);
2068 (*metafile
)->image
.xres
= (REAL
)placeable
->Inch
;
2069 (*metafile
)->image
.yres
= (REAL
)placeable
->Inch
;
2070 (*metafile
)->bounds
.X
= ((REAL
)placeable
->BoundingBox
.Left
) / ((REAL
)placeable
->Inch
);
2071 (*metafile
)->bounds
.Y
= ((REAL
)placeable
->BoundingBox
.Top
) / ((REAL
)placeable
->Inch
);
2072 (*metafile
)->bounds
.Width
= (REAL
)(placeable
->BoundingBox
.Right
-
2073 placeable
->BoundingBox
.Left
);
2074 (*metafile
)->bounds
.Height
= (REAL
)(placeable
->BoundingBox
.Bottom
-
2075 placeable
->BoundingBox
.Top
);
2076 (*metafile
)->metafile_type
= MetafileTypeWmfPlaceable
;
2079 (*metafile
)->metafile_type
= MetafileTypeWmf
;
2080 (*metafile
)->image
.format
= ImageFormatWMF
;
2082 if (delete) DeleteMetaFile(hwmf
);
2085 DeleteEnhMetaFile(hemf
);
2089 GpStatus WINGDIPAPI
GdipCreateMetafileFromWmfFile(GDIPCONST WCHAR
*file
,
2090 GDIPCONST WmfPlaceableFileHeader
* placeable
, GpMetafile
**metafile
)
2092 HMETAFILE hmf
= GetMetaFileW(file
);
2094 TRACE("(%s, %p, %p)\n", debugstr_w(file
), placeable
, metafile
);
2096 if(!hmf
) return InvalidParameter
;
2098 return GdipCreateMetafileFromWmf(hmf
, TRUE
, placeable
, metafile
);
2101 GpStatus WINGDIPAPI
GdipCreateMetafileFromFile(GDIPCONST WCHAR
*file
,
2102 GpMetafile
**metafile
)
2107 TRACE("(%p, %p)\n", file
, metafile
);
2109 if (!file
|| !metafile
) return InvalidParameter
;
2113 status
= GdipCreateStreamOnFile(file
, GENERIC_READ
, &stream
);
2116 status
= GdipCreateMetafileFromStream(stream
, metafile
);
2117 IStream_Release(stream
);
2122 GpStatus WINGDIPAPI
GdipCreateMetafileFromStream(IStream
*stream
,
2123 GpMetafile
**metafile
)
2127 TRACE("%p %p\n", stream
, metafile
);
2129 stat
= GdipLoadImageFromStream(stream
, (GpImage
**)metafile
);
2130 if (stat
!= Ok
) return stat
;
2132 if ((*metafile
)->image
.type
!= ImageTypeMetafile
)
2134 GdipDisposeImage(&(*metafile
)->image
);
2136 return GenericError
;
2142 GpStatus WINGDIPAPI
GdipSetMetafileDownLevelRasterizationLimit(GpMetafile
*metafile
,
2145 TRACE("(%p,%u)\n", metafile
, limitDpi
);
2150 GpStatus WINGDIPAPI
GdipConvertToEmfPlus(const GpGraphics
* ref
,
2151 GpMetafile
* metafile
, BOOL
* succ
, EmfType emfType
,
2152 const WCHAR
* description
, GpMetafile
** out_metafile
)
2156 TRACE("(%p,%p,%p,%u,%s,%p)\n", ref
, metafile
, succ
, emfType
,
2157 debugstr_w(description
), out_metafile
);
2159 if(!ref
|| !metafile
|| !out_metafile
|| emfType
< EmfTypeEmfOnly
|| emfType
> EmfTypeEmfPlusDual
)
2160 return InvalidParameter
;
2164 *out_metafile
= NULL
;
2167 FIXME("not implemented\n");
2169 return NotImplemented
;
2172 GpStatus WINGDIPAPI
GdipEmfToWmfBits(HENHMETAFILE hemf
, UINT cbData16
,
2173 LPBYTE pData16
, INT iMapMode
, INT eFlags
)
2175 FIXME("(%p, %d, %p, %d, %d): stub\n", hemf
, cbData16
, pData16
, iMapMode
, eFlags
);
2176 return NotImplemented
;
2179 GpStatus WINGDIPAPI
GdipRecordMetafileFileName(GDIPCONST WCHAR
* fileName
,
2180 HDC hdc
, EmfType type
, GDIPCONST GpRectF
*pFrameRect
,
2181 MetafileFrameUnit frameUnit
, GDIPCONST WCHAR
*desc
,
2182 GpMetafile
**metafile
)
2184 FIXME("%s %p %d %p %d %s %p stub!\n", debugstr_w(fileName
), hdc
, type
, pFrameRect
,
2185 frameUnit
, debugstr_w(desc
), metafile
);
2187 return NotImplemented
;
2190 GpStatus WINGDIPAPI
GdipRecordMetafileFileNameI(GDIPCONST WCHAR
* fileName
, HDC hdc
, EmfType type
,
2191 GDIPCONST GpRect
*pFrameRect
, MetafileFrameUnit frameUnit
,
2192 GDIPCONST WCHAR
*desc
, GpMetafile
**metafile
)
2194 FIXME("%s %p %d %p %d %s %p stub!\n", debugstr_w(fileName
), hdc
, type
, pFrameRect
,
2195 frameUnit
, debugstr_w(desc
), metafile
);
2197 return NotImplemented
;
2200 /*****************************************************************************
2201 * GdipConvertToEmfPlusToFile [GDIPLUS.@]
2204 GpStatus WINGDIPAPI
GdipConvertToEmfPlusToFile(const GpGraphics
* refGraphics
,
2205 GpMetafile
* metafile
, BOOL
* conversionSuccess
,
2206 const WCHAR
* filename
, EmfType emfType
,
2207 const WCHAR
* description
, GpMetafile
** out_metafile
)
2209 FIXME("stub: %p, %p, %p, %p, %u, %p, %p\n", refGraphics
, metafile
, conversionSuccess
, filename
, emfType
, description
, out_metafile
);
2210 return NotImplemented
;