diff --git a/dlls/gdiplus/Makefile.in b/dlls/gdiplus/Makefile.in
index fb762b1b8f2b0b34ef7b3db3aea754ae6496485d..b057eafdcb03b14c77aa6deeffdc93cbc4017cee 100644
--- a/dlls/gdiplus/Makefile.in
+++ b/dlls/gdiplus/Makefile.in
@@ -13,6 +13,7 @@ C_SRCS = \
 	image.c \
 	imageattributes.c \
 	matrix.c \
+	metafile.c \
 	pathiterator.c \
 	pen.c \
 	region.c \
diff --git a/dlls/gdiplus/gdiplus_private.h b/dlls/gdiplus/gdiplus_private.h
index 2953b45d5ed79d8d9693ae852c5ccf6e5e76774b..9cae489af6a7afd19ca409077f61cca1d875ba51 100644
--- a/dlls/gdiplus/gdiplus_private.h
+++ b/dlls/gdiplus/gdiplus_private.h
@@ -51,6 +51,9 @@ extern REAL convert_unit(REAL logpixels, GpUnit unit) DECLSPEC_HIDDEN;
 
 extern GpStatus graphics_from_image(GpImage *image, GpGraphics **graphics) DECLSPEC_HIDDEN;
 
+extern GpStatus METAFILE_GetGraphicsContext(GpMetafile* metafile, GpGraphics **result) DECLSPEC_HIDDEN;
+extern GpStatus METAFILE_GraphicsDeleted(GpMetafile* metafile) DECLSPEC_HIDDEN;
+
 extern void calc_curve_bezier(CONST GpPointF *pts, REAL tension, REAL *x1,
     REAL *y1, REAL *x2, REAL *y2) DECLSPEC_HIDDEN;
 extern void calc_curve_bezier_endp(REAL xend, REAL yend, REAL xadj, REAL yadj,
@@ -268,6 +271,13 @@ struct GpMetafile{
     GpImage image;
     GpRectF bounds;
     GpUnit unit;
+    MetafileType metafile_type;
+    HDC record_dc;
+    GpGraphics *record_graphics;
+    BYTE *comment_data;
+    DWORD comment_data_size;
+    DWORD comment_data_length;
+    HENHMETAFILE hemf;
 };
 
 struct GpBitmap{
diff --git a/dlls/gdiplus/graphics.c b/dlls/gdiplus/graphics.c
index 336929fc9b32d59cdbf2bf02dbe56469b0300dbb..ed024adeec4cbd7b61f57520224597b9c363f0a7 100644
--- a/dlls/gdiplus/graphics.c
+++ b/dlls/gdiplus/graphics.c
@@ -206,6 +206,11 @@ static GpStatus alpha_blend_pixels(GpGraphics *graphics, INT dst_x, INT dst_y,
 
         return Ok;
     }
+    else if (graphics->image && graphics->image->type == ImageTypeMetafile)
+    {
+        ERR("This should not be used for metafiles; fix caller\n");
+        return NotImplemented;
+    }
     else
     {
         HDC hdc;
@@ -1967,11 +1972,19 @@ GpStatus WINGDIPAPI GdipCreateStreamOnFile(GDIPCONST WCHAR * filename,
 GpStatus WINGDIPAPI GdipDeleteGraphics(GpGraphics *graphics)
 {
     GraphicsContainerItem *cont, *next;
+    GpStatus stat;
     TRACE("(%p)\n", graphics);
 
     if(!graphics) return InvalidParameter;
     if(graphics->busy) return ObjectBusy;
 
+    if (graphics->image && graphics->image->type == ImageTypeMetafile)
+    {
+        stat = METAFILE_GraphicsDeleted((GpMetafile*)graphics->image);
+        if (stat != Ok)
+            return stat;
+    }
+
     if(graphics->owndc)
         ReleaseDC(graphics->hwnd, graphics->hdc);
 
@@ -5873,23 +5886,6 @@ GpStatus WINGDIPAPI GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UINT16
     return stat;
 }
 
-GpStatus WINGDIPAPI GdipRecordMetafile(HDC hdc, EmfType type, GDIPCONST GpRectF *frameRect,
-                                       MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
-{
-    FIXME("(%p %d %p %d %p %p): stub\n", hdc, type, frameRect, frameUnit, desc, metafile);
-    return NotImplemented;
-}
-
-/*****************************************************************************
- * GdipRecordMetafileI [GDIPLUS.@]
- */
-GpStatus WINGDIPAPI GdipRecordMetafileI(HDC hdc, EmfType type, GDIPCONST GpRect *frameRect,
-                                        MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
-{
-    FIXME("(%p %d %p %d %p %p): stub\n", hdc, type, frameRect, frameUnit, desc, metafile);
-    return NotImplemented;
-}
-
 GpStatus WINGDIPAPI GdipRecordMetafileStream(IStream *stream, HDC hdc, EmfType type, GDIPCONST GpRect *frameRect,
                                         MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
 {
@@ -5919,15 +5915,3 @@ cleanup:
     GdipDeleteRegion(rgn);
     return stat;
 }
-
-GpStatus WINGDIPAPI GdipGetHemfFromMetafile(GpMetafile *metafile, HENHMETAFILE *hEmf)
-{
-    FIXME("(%p,%p): stub\n", metafile, hEmf);
-
-    if (!metafile || !hEmf)
-        return InvalidParameter;
-
-    *hEmf = NULL;
-
-    return NotImplemented;
-}
diff --git a/dlls/gdiplus/image.c b/dlls/gdiplus/image.c
index 9af2519b1e485c21a1c0797717ee53fda3436023..10376f5b414cc7db46edddd77b8d41650ceece09 100644
--- a/dlls/gdiplus/image.c
+++ b/dlls/gdiplus/image.c
@@ -2143,12 +2143,7 @@ GpStatus WINGDIPAPI GdipGetImageGraphicsContext(GpImage *image,
     if(!image || !graphics)
         return InvalidParameter;
 
-    if(image->type != ImageTypeBitmap){
-        FIXME("not implemented for image type %d\n", image->type);
-        return NotImplemented;
-    }
-
-    if (((GpBitmap*)image)->hbitmap)
+    if (image->type == ImageTypeBitmap && ((GpBitmap*)image)->hbitmap)
     {
         hdc = ((GpBitmap*)image)->hdc;
 
@@ -2163,6 +2158,8 @@ GpStatus WINGDIPAPI GdipGetImageGraphicsContext(GpImage *image,
         if (stat == Ok)
             (*graphics)->image = image;
     }
+    else if (image->type == ImageTypeMetafile)
+        stat = METAFILE_GetGraphicsContext((GpMetafile*)image, graphics);
     else
         stat = graphics_from_image(image, graphics);
 
diff --git a/dlls/gdiplus/metafile.c b/dlls/gdiplus/metafile.c
new file mode 100644
index 0000000000000000000000000000000000000000..99b16213a94edc59d7c71fd1d33553481e8e1c49
--- /dev/null
+++ b/dlls/gdiplus/metafile.c
@@ -0,0 +1,324 @@
+/*
+ * Copyright (C) 2011 Vincent Povirk for CodeWeavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <stdarg.h>
+#include <math.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "wine/unicode.h"
+
+#define COBJMACROS
+#include "objbase.h"
+#include "ocidl.h"
+#include "olectl.h"
+#include "ole2.h"
+
+#include "winreg.h"
+#include "shlwapi.h"
+
+#include "gdiplus.h"
+#include "gdiplus_private.h"
+#include "wine/debug.h"
+#include "wine/list.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(gdiplus);
+
+typedef struct EmfPlusRecordHeader
+{
+    WORD Type;
+    WORD Flags;
+    DWORD Size;
+    DWORD DataSize;
+} EmfPlusRecordHeader;
+
+typedef struct EmfPlusHeader
+{
+    EmfPlusRecordHeader Header;
+    DWORD Version;
+    DWORD EmfPlusFlags;
+    DWORD LogicalDpiX;
+    DWORD LogicalDpiY;
+} EmfPlusHeader;
+
+static GpStatus METAFILE_AllocateRecord(GpMetafile *metafile, DWORD size, void **result)
+{
+    DWORD size_needed;
+    EmfPlusRecordHeader *record;
+
+    if (!metafile->comment_data_size)
+    {
+        DWORD data_size = max(256, size * 2 + 4);
+        metafile->comment_data = GdipAlloc(data_size);
+
+        if (!metafile->comment_data)
+            return OutOfMemory;
+
+        memcpy(metafile->comment_data, "EMF+", 4);
+
+        metafile->comment_data_size = data_size;
+        metafile->comment_data_length = 4;
+    }
+
+    size_needed = size + metafile->comment_data_length;
+
+    if (size_needed > metafile->comment_data_size)
+    {
+        DWORD data_size = size_needed * 2;
+        BYTE *new_data = GdipAlloc(data_size);
+
+        if (!new_data)
+            return OutOfMemory;
+
+        memcpy(new_data, metafile->comment_data, metafile->comment_data_length);
+
+        metafile->comment_data_size = data_size;
+        GdipFree(metafile->comment_data);
+        metafile->comment_data = new_data;
+    }
+
+    *result = metafile->comment_data + metafile->comment_data_length;
+    metafile->comment_data_length += size;
+
+    record = (EmfPlusRecordHeader*)*result;
+    record->Size = size;
+    record->DataSize = size - sizeof(EmfPlusRecordHeader);
+
+    return Ok;
+}
+
+static void METAFILE_WriteRecords(GpMetafile *metafile)
+{
+    if (metafile->comment_data_length > 4)
+    {
+        GdiComment(metafile->record_dc, metafile->comment_data_length, metafile->comment_data);
+        metafile->comment_data_length = 4;
+    }
+}
+
+static GpStatus METAFILE_WriteHeader(GpMetafile *metafile, HDC hdc)
+{
+    GpStatus stat;
+
+    if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
+    {
+        EmfPlusHeader *header;
+
+        stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusHeader), (void**)&header);
+        if (stat != Ok)
+            return stat;
+
+        header->Header.Type = EmfPlusRecordTypeHeader;
+
+        if (metafile->metafile_type == MetafileTypeEmfPlusDual)
+            header->Header.Flags = 1;
+        else
+            header->Header.Flags = 0;
+
+        header->Version = 0xDBC01002;
+
+        if (GetDeviceCaps(hdc, TECHNOLOGY) == DT_RASDISPLAY)
+            header->EmfPlusFlags = 1;
+        else
+            header->EmfPlusFlags = 0;
+
+        header->LogicalDpiX = GetDeviceCaps(hdc, LOGPIXELSX);
+        header->LogicalDpiY = GetDeviceCaps(hdc, LOGPIXELSY);
+
+        METAFILE_WriteRecords(metafile);
+    }
+
+    return Ok;
+}
+
+static GpStatus METAFILE_WriteEndOfFile(GpMetafile *metafile)
+{
+    GpStatus stat;
+
+    if (metafile->metafile_type == MetafileTypeEmfPlusOnly || metafile->metafile_type == MetafileTypeEmfPlusDual)
+    {
+        EmfPlusRecordHeader *record;
+
+        stat = METAFILE_AllocateRecord(metafile, sizeof(EmfPlusRecordHeader), (void**)&record);
+        if (stat != Ok)
+            return stat;
+
+        record->Type = EmfPlusRecordTypeEndOfFile;
+        record->Flags = 0;
+
+        METAFILE_WriteRecords(metafile);
+    }
+
+    return Ok;
+}
+
+GpStatus WINGDIPAPI GdipRecordMetafile(HDC hdc, EmfType type, GDIPCONST GpRectF *frameRect,
+                                       MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
+{
+    HDC record_dc;
+    REAL framerect_factor_x, framerect_factor_y;
+    RECT rc;
+    GpStatus stat;
+
+    TRACE("(%p %d %p %d %p %p)\n", hdc, type, frameRect, frameUnit, desc, metafile);
+
+    if (!hdc || type < EmfTypeEmfOnly || type > EmfTypeEmfPlusDual || !metafile)
+        return InvalidParameter;
+
+    if (!frameRect)
+    {
+        FIXME("not implemented for NULL rect\n");
+        return NotImplemented;
+    }
+
+    switch (frameUnit)
+    {
+    case MetafileFrameUnitPixel:
+        framerect_factor_x = 2540.0 / GetDeviceCaps(hdc, LOGPIXELSX);
+        framerect_factor_y = 2540.0 / GetDeviceCaps(hdc, LOGPIXELSY);
+        break;
+    case MetafileFrameUnitPoint:
+        framerect_factor_x = framerect_factor_y = 2540.0 / 72.0;
+        break;
+    case MetafileFrameUnitInch:
+        framerect_factor_x = framerect_factor_y = 2540.0;
+        break;
+    case MetafileFrameUnitDocument:
+        framerect_factor_x = framerect_factor_y = 2540.0 / 300.0;
+        break;
+    case MetafileFrameUnitMillimeter:
+        framerect_factor_x = framerect_factor_y = 100.0;
+        break;
+    case MetafileFrameUnitGdi:
+        framerect_factor_x = framerect_factor_y = 1.0;
+        break;
+    default:
+        return InvalidParameter;
+    }
+
+    rc.left = framerect_factor_x * frameRect->X;
+    rc.top = framerect_factor_y * frameRect->Y;
+    rc.right = rc.left + framerect_factor_x * frameRect->Width;
+    rc.bottom = rc.top + framerect_factor_y * frameRect->Height;
+
+    record_dc = CreateEnhMetaFileW(hdc, NULL, &rc, desc);
+
+    if (!record_dc)
+        return GenericError;
+
+    *metafile = GdipAlloc(sizeof(GpMetafile));
+    if(!*metafile)
+    {
+        DeleteEnhMetaFile(CloseEnhMetaFile(record_dc));
+        return OutOfMemory;
+    }
+
+    (*metafile)->image.type = ImageTypeMetafile;
+    (*metafile)->image.picture = NULL;
+    (*metafile)->image.flags   = ImageFlagsNone;
+    (*metafile)->image.palette_flags = 0;
+    (*metafile)->image.palette_count = 0;
+    (*metafile)->image.palette_size = 0;
+    (*metafile)->image.palette_entries = NULL;
+    (*metafile)->bounds = *frameRect;
+    (*metafile)->unit = frameUnit;
+    (*metafile)->metafile_type = type;
+    (*metafile)->record_dc = record_dc;
+    (*metafile)->comment_data = NULL;
+    (*metafile)->comment_data_size = 0;
+    (*metafile)->comment_data_length = 0;
+    (*metafile)->hemf = NULL;
+
+    stat = METAFILE_WriteHeader(*metafile, hdc);
+
+    if (stat != Ok)
+    {
+        DeleteEnhMetaFile(CloseEnhMetaFile(record_dc));
+        GdipFree(*metafile);
+        *metafile = NULL;
+        return OutOfMemory;
+    }
+
+    return stat;
+}
+
+/*****************************************************************************
+ * GdipRecordMetafileI [GDIPLUS.@]
+ */
+GpStatus WINGDIPAPI GdipRecordMetafileI(HDC hdc, EmfType type, GDIPCONST GpRect *frameRect,
+                                        MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
+{
+    GpRectF frameRectF, *pFrameRectF;
+
+    TRACE("(%p %d %p %d %p %p)\n", hdc, type, frameRect, frameUnit, desc, metafile);
+
+    if (frameRect)
+    {
+        frameRectF.X = frameRect->X;
+        frameRectF.Y = frameRect->Y;
+        frameRectF.Width = frameRect->Width;
+        frameRectF.Height = frameRect->Height;
+        pFrameRectF = &frameRectF;
+    }
+    else
+        pFrameRectF = NULL;
+
+    return GdipRecordMetafile(hdc, type, pFrameRectF, frameUnit, desc, metafile);
+}
+
+GpStatus METAFILE_GetGraphicsContext(GpMetafile* metafile, GpGraphics **result)
+{
+    GpStatus stat;
+
+    if (!metafile->record_dc || metafile->record_graphics)
+        return InvalidParameter;
+
+    stat = graphics_from_image((GpImage*)metafile, &metafile->record_graphics);
+
+    if (stat == Ok)
+        *result = metafile->record_graphics;
+
+    return stat;
+}
+
+GpStatus METAFILE_GraphicsDeleted(GpMetafile* metafile)
+{
+    GpStatus stat;
+
+    stat = METAFILE_WriteEndOfFile(metafile);
+    metafile->record_graphics = NULL;
+
+    metafile->hemf = CloseEnhMetaFile(metafile->record_dc);
+    metafile->record_dc = NULL;
+
+    return stat;
+}
+
+GpStatus WINGDIPAPI GdipGetHemfFromMetafile(GpMetafile *metafile, HENHMETAFILE *hEmf)
+{
+    TRACE("(%p,%p)\n", metafile, hEmf);
+
+    if (!metafile || !hEmf || !metafile->hemf)
+        return InvalidParameter;
+
+    *hEmf = metafile->hemf;
+    metafile->hemf = NULL;
+
+    return Ok;
+}
diff --git a/dlls/gdiplus/tests/metafile.c b/dlls/gdiplus/tests/metafile.c
index b467ce3ecf31322ec9baae50a5b148e32f3b3ad4..8dcda256575dcb26c1a50f71ba0b4e93a869bac4 100644
--- a/dlls/gdiplus/tests/metafile.c
+++ b/dlls/gdiplus/tests/metafile.c
@@ -161,8 +161,26 @@ static void test_empty(void)
 
     hdc = CreateCompatibleDC(0);
 
+    stat = GdipRecordMetafile(NULL, EmfTypeEmfPlusOnly, &frame, MetafileFrameUnitPixel, description, &metafile);
+    expect(InvalidParameter, stat);
+
+    stat = GdipRecordMetafile(hdc, MetafileTypeInvalid, &frame, MetafileFrameUnitPixel, description, &metafile);
+    expect(InvalidParameter, stat);
+
+    stat = GdipRecordMetafile(hdc, MetafileTypeWmf, &frame, MetafileFrameUnitPixel, description, &metafile);
+    expect(InvalidParameter, stat);
+
+    stat = GdipRecordMetafile(hdc, MetafileTypeWmfPlaceable, &frame, MetafileFrameUnitPixel, description, &metafile);
+    expect(InvalidParameter, stat);
+
+    stat = GdipRecordMetafile(hdc, MetafileTypeEmfPlusDual+1, &frame, MetafileFrameUnitPixel, description, &metafile);
+    expect(InvalidParameter, stat);
+
+    stat = GdipRecordMetafile(hdc, EmfTypeEmfPlusOnly, &frame, MetafileFrameUnitPixel, description, NULL);
+    expect(InvalidParameter, stat);
+
     stat = GdipRecordMetafile(hdc, EmfTypeEmfPlusOnly, &frame, MetafileFrameUnitPixel, description, &metafile);
-    todo_wine expect(Ok, stat);
+    expect(Ok, stat);
 
     DeleteDC(hdc);