[GDIPLUS_WINETEST]
authorAmine Khaldi <amine.khaldi@reactos.org>
Sat, 19 May 2012 10:03:49 +0000 (10:03 +0000)
committerAmine Khaldi <amine.khaldi@reactos.org>
Sat, 19 May 2012 10:03:49 +0000 (10:03 +0000)
* Sync to Wine 1.5.4.

svn path=/trunk/; revision=56614

rostests/winetests/gdiplus/CMakeLists.txt
rostests/winetests/gdiplus/brush.c
rostests/winetests/gdiplus/font.c
rostests/winetests/gdiplus/metafile.c [new file with mode: 0644]
rostests/winetests/gdiplus/testlist.c

index 28dd4c5..702d554 100644 (file)
@@ -11,6 +11,7 @@ list(APPEND SOURCE
     graphicspath.c
     image.c
     matrix.c
+    metafile.c
     pathiterator.c
     pen.c
     region.c
index 5eed607..cde7b56 100644 (file)
@@ -773,22 +773,59 @@ static void test_gradientsurroundcolorcount(void)
 {
     GpStatus status;
     GpPathGradient *grad;
-    ARGB *color;
-    INT count = 3;
+    ARGB color[3];
+    INT count;
 
     status = GdipCreatePathGradient(blendcount_ptf, 2, WrapModeClamp, &grad);
     expect(Ok, status);
 
-    color = GdipAlloc(sizeof(ARGB[3]));
+    count = 0;
+    status = GdipGetPathGradientSurroundColorCount(grad, &count);
+    expect(Ok, status);
+    expect(2, count);
+
+    color[0] = color[1] = color[2] = 0xdeadbeef;
+    count = 3;
+    status = GdipGetPathGradientSurroundColorsWithCount(grad, color, &count);
+    expect(Ok, status);
+    expect(1, count);
+    expect(0xffffffff, color[0]);
+    expect(0xffffffff, color[1]);
+    expect(0xdeadbeef, color[2]);
+
+    color[0] = color[1] = color[2] = 0xdeadbeef;
+    count = 2;
+    status = GdipGetPathGradientSurroundColorsWithCount(grad, color, &count);
+    expect(Ok, status);
+    expect(1, count);
+    expect(0xffffffff, color[0]);
+    expect(0xffffffff, color[1]);
+    expect(0xdeadbeef, color[2]);
+
+    color[0] = color[1] = color[2] = 0xdeadbeef;
+    count = 1;
+    status = GdipGetPathGradientSurroundColorsWithCount(grad, color, &count);
+    expect(InvalidParameter, status);
+    expect(1, count);
+    expect(0xdeadbeef, color[0]);
+    expect(0xdeadbeef, color[1]);
+    expect(0xdeadbeef, color[2]);
+
+    color[0] = color[1] = color[2] = 0xdeadbeef;
+    count = 0;
+    status = GdipGetPathGradientSurroundColorsWithCount(grad, color, &count);
+    expect(InvalidParameter, status);
+    expect(0, count);
+    expect(0xdeadbeef, color[0]);
+    expect(0xdeadbeef, color[1]);
+    expect(0xdeadbeef, color[2]);
 
+    count = 3;
     status = GdipSetPathGradientSurroundColorsWithCount(grad, color, &count);
     expect(InvalidParameter, status);
-    GdipFree(color);
 
     count = 2;
 
-    color = GdipAlloc(sizeof(ARGB[2]));
-
     color[0] = 0x00ff0000;
     color[1] = 0x0000ff00;
 
@@ -806,7 +843,7 @@ static void test_gradientsurroundcolorcount(void)
     }
 
     status = GdipSetPathGradientSurroundColorsWithCount(grad, color, &count);
-    todo_wine expect(Ok, status);
+    expect(Ok, status);
     expect(2, count);
 
     status = GdipGetPathGradientSurroundColorCount(NULL, &count);
@@ -817,13 +854,420 @@ static void test_gradientsurroundcolorcount(void)
 
     count = 0;
     status = GdipGetPathGradientSurroundColorCount(grad, &count);
-    todo_wine expect(Ok, status);
-    todo_wine expect(2, count);
+    expect(Ok, status);
+    expect(2, count);
+
+    color[0] = color[1] = color[2] = 0xdeadbeef;
+    count = 2;
+    status = GdipGetPathGradientSurroundColorsWithCount(grad, color, &count);
+    expect(Ok, status);
+    expect(2, count);
+    expect(0x00ff0000, color[0]);
+    expect(0x0000ff00, color[1]);
+    expect(0xdeadbeef, color[2]);
+
+    count = 1;
+    status = GdipSetPathGradientSurroundColorsWithCount(grad, color, &count);
+    expect(Ok, status);
+    expect(1, count);
+
+    count = 0;
+    status = GdipGetPathGradientSurroundColorCount(grad, &count);
+    expect(Ok, status);
+    expect(2, count);
+
+    /* If all colors are the same, count is set to 1. */
+    color[0] = color[1] = 0;
+    count = 2;
+    status = GdipSetPathGradientSurroundColorsWithCount(grad, color, &count);
+    expect(Ok, status);
+    expect(2, count);
+
+    color[0] = color[1] = color[2] = 0xdeadbeef;
+    count = 2;
+    status = GdipGetPathGradientSurroundColorsWithCount(grad, color, &count);
+    expect(Ok, status);
+    expect(1, count);
+    expect(0x00000000, color[0]);
+    expect(0x00000000, color[1]);
+    expect(0xdeadbeef, color[2]);
+
+    color[0] = color[1] = 0xff00ff00;
+    count = 2;
+    status = GdipSetPathGradientSurroundColorsWithCount(grad, color, &count);
+    expect(Ok, status);
+    expect(2, count);
+
+    color[0] = color[1] = color[2] = 0xdeadbeef;
+    count = 2;
+    status = GdipGetPathGradientSurroundColorsWithCount(grad, color, &count);
+    expect(Ok, status);
+    expect(1, count);
+    expect(0xff00ff00, color[0]);
+    expect(0xff00ff00, color[1]);
+    expect(0xdeadbeef, color[2]);
+
+    count = 0;
+    status = GdipSetPathGradientSurroundColorsWithCount(grad, color, &count);
+    expect(InvalidParameter, status);
+    expect(0, count);
+
+    GdipDeleteBrush((GpBrush*)grad);
+
+    status = GdipCreatePathGradient(getbounds_ptf, 3, WrapModeClamp, &grad);
+    expect(Ok, status);
+
+    color[0] = color[1] = color[2] = 0xdeadbeef;
+    count = 3;
+    status = GdipGetPathGradientSurroundColorsWithCount(grad, color, &count);
+    expect(Ok, status);
+    expect(1, count);
+    expect(0xffffffff, color[0]);
+    expect(0xffffffff, color[1]);
+    expect(0xffffffff, color[2]);
+
+    color[0] = color[1] = color[2] = 0xdeadbeef;
+    count = 2;
+    status = GdipGetPathGradientSurroundColorsWithCount(grad, color, &count);
+    expect(InvalidParameter, status);
+    expect(2, count);
+    expect(0xdeadbeef, color[0]);
+    expect(0xdeadbeef, color[1]);
+    expect(0xdeadbeef, color[2]);
+
+    count = 0;
+    status = GdipGetPathGradientSurroundColorCount(grad, &count);
+    expect(Ok, status);
+    expect(3, count);
 
-    GdipFree(color);
     GdipDeleteBrush((GpBrush*)grad);
 }
 
+static void test_pathgradientpath(void)
+{
+    GpStatus status;
+    GpPath *path=NULL;
+    GpPathGradient *grad=NULL;
+
+    status = GdipCreatePathGradient(blendcount_ptf, 2, WrapModeClamp, &grad);
+    expect(Ok, status);
+
+    status = GdipGetPathGradientPath(grad, NULL);
+    expect(NotImplemented, status);
+
+    status = GdipCreatePath(FillModeWinding, &path);
+    expect(Ok, status);
+
+    status = GdipGetPathGradientPath(NULL, path);
+    expect(NotImplemented, status);
+
+    status = GdipGetPathGradientPath(grad, path);
+    expect(NotImplemented, status);
+
+    status = GdipDeletePath(path);
+    expect(Ok, status);
+
+    status = GdipDeleteBrush((GpBrush*)grad);
+    expect(Ok, status);
+}
+
+static void test_pathgradientcenterpoint(void)
+{
+    static const GpPointF path_points[] = {{0,0}, {3,0}, {0,4}};
+    GpStatus status;
+    GpPathGradient *grad;
+    GpPointF point;
+
+    status = GdipCreatePathGradient(path_points+1, 2, WrapModeClamp, &grad);
+    expect(Ok, status);
+
+    status = GdipGetPathGradientCenterPoint(NULL, &point);
+    expect(InvalidParameter, status);
+
+    status = GdipGetPathGradientCenterPoint(grad, NULL);
+    expect(InvalidParameter, status);
+
+    status = GdipGetPathGradientCenterPoint(grad, &point);
+    expect(Ok, status);
+    expectf(1.5, point.X);
+    expectf(2.0, point.Y);
+
+    status = GdipSetPathGradientCenterPoint(NULL, &point);
+    expect(InvalidParameter, status);
+
+    status = GdipSetPathGradientCenterPoint(grad, NULL);
+    expect(InvalidParameter, status);
+
+    point.X = 10.0;
+    point.Y = 15.0;
+    status = GdipSetPathGradientCenterPoint(grad, &point);
+    expect(Ok, status);
+
+    point.X = point.Y = -1;
+    status = GdipGetPathGradientCenterPoint(grad, &point);
+    expect(Ok, status);
+    expectf(10.0, point.X);
+    expectf(15.0, point.Y);
+
+    status = GdipDeleteBrush((GpBrush*)grad);
+    expect(Ok, status);
+
+    status = GdipCreatePathGradient(path_points, 3, WrapModeClamp, &grad);
+    expect(Ok, status);
+
+    status = GdipGetPathGradientCenterPoint(grad, &point);
+    expect(Ok, status);
+    todo_wine expectf(1.0, point.X);
+    todo_wine expectf(4.0/3.0, point.Y);
+
+    status = GdipDeleteBrush((GpBrush*)grad);
+    expect(Ok, status);
+}
+
+static void test_pathgradientpresetblend(void)
+{
+    static const GpPointF path_points[] = {{0,0}, {3,0}, {0,4}};
+    GpStatus status;
+    GpPathGradient *grad;
+    INT count;
+    int i;
+    const REAL positions[5] = {0.0f, 0.2f, 0.5f, 0.8f, 1.0f};
+    const REAL two_positions[2] = {0.0f, 1.0f};
+    const ARGB colors[5] = {0xff0000ff, 0xff00ff00, 0xff00ffff, 0xffff0000, 0xffffffff};
+    REAL res_positions[6] = {0.3f, 0.0f, 0.0f, 0.0f, 0.0f};
+    ARGB res_colors[6] = {0xdeadbeef, 0, 0, 0, 0};
+
+    status = GdipCreatePathGradient(path_points+1, 2, WrapModeClamp, &grad);
+    expect(Ok, status);
+
+    status = GdipGetPathGradientPresetBlendCount(NULL, &count);
+    expect(InvalidParameter, status);
+
+    status = GdipGetPathGradientPresetBlendCount(grad, NULL);
+    expect(InvalidParameter, status);
+
+    status = GdipGetPathGradientPresetBlendCount(grad, &count);
+    expect(Ok, status);
+    expect(0, count);
+
+    status = GdipGetPathGradientPresetBlend(NULL, res_colors, res_positions, 1);
+    expect(InvalidParameter, status);
+
+    status = GdipGetPathGradientPresetBlend(grad, NULL, res_positions, 1);
+    expect(InvalidParameter, status);
+
+    status = GdipGetPathGradientPresetBlend(grad, res_colors, NULL, 1);
+    expect(InvalidParameter, status);
+
+    status = GdipGetPathGradientPresetBlend(grad, res_colors, res_positions, 0);
+    expect(InvalidParameter, status);
+
+    status = GdipGetPathGradientPresetBlend(grad, res_colors, res_positions, -1);
+    expect(OutOfMemory, status);
+
+    status = GdipGetPathGradientPresetBlend(grad, res_colors, res_positions, 1);
+    expect(InvalidParameter, status);
+
+    status = GdipGetPathGradientPresetBlend(grad, res_colors, res_positions, 2);
+    expect(GenericError, status);
+
+    status = GdipSetPathGradientPresetBlend(NULL, colors, positions, 5);
+    expect(InvalidParameter, status);
+
+    status = GdipSetPathGradientPresetBlend(grad, NULL, positions, 5);
+    expect(InvalidParameter, status);
+
+    if (0)
+    {
+        /* crashes on windows xp */
+        status = GdipSetPathGradientPresetBlend(grad, colors, NULL, 5);
+        expect(InvalidParameter, status);
+    }
+
+    status = GdipSetPathGradientPresetBlend(grad, colors, positions, 0);
+    expect(InvalidParameter, status);
+
+    status = GdipSetPathGradientPresetBlend(grad, colors, positions, -1);
+    expect(InvalidParameter, status);
+
+    status = GdipSetPathGradientPresetBlend(grad, colors, positions, 1);
+    expect(InvalidParameter, status);
+
+    /* leave off the 0.0 position */
+    status = GdipSetPathGradientPresetBlend(grad, &colors[1], &positions[1], 4);
+    expect(InvalidParameter, status);
+
+    /* leave off the 1.0 position */
+    status = GdipSetPathGradientPresetBlend(grad, colors, positions, 4);
+    expect(InvalidParameter, status);
+
+    status = GdipSetPathGradientPresetBlend(grad, colors, positions, 5);
+    expect(Ok, status);
+
+    status = GdipGetPathGradientPresetBlendCount(grad, &count);
+    expect(Ok, status);
+    expect(5, count);
+
+    if (0)
+    {
+        /* Native GdipGetPathGradientPresetBlend seems to copy starting from
+         * the end of each array and do no bounds checking. This is so braindead
+         * I'm not going to copy it. */
+
+        res_colors[0] = 0xdeadbeef;
+        res_positions[0] = 0.3;
+
+        status = GdipGetPathGradientPresetBlend(grad, &res_colors[1], &res_positions[1], 4);
+        expect(Ok, status);
+
+        expect(0xdeadbeef, res_colors[0]);
+        expectf(0.3, res_positions[0]);
+
+        for (i=1; i<5; i++)
+        {
+            expect(colors[i], res_colors[i]);
+            expectf(positions[i], res_positions[i]);
+        }
+
+        status = GdipGetPathGradientPresetBlend(grad, res_colors, res_positions, 6);
+        expect(Ok, status);
+
+        for (i=0; i<5; i++)
+        {
+            expect(colors[i], res_colors[i+1]);
+            expectf(positions[i], res_positions[i+1]);
+        }
+    }
+
+    status = GdipGetPathGradientPresetBlend(grad, res_colors, res_positions, 5);
+    expect(Ok, status);
+
+    for (i=0; i<5; i++)
+    {
+        expect(colors[i], res_colors[i]);
+        expectf(positions[i], res_positions[i]);
+    }
+
+    status = GdipGetPathGradientPresetBlend(grad, res_colors, res_positions, 0);
+    expect(InvalidParameter, status);
+
+    status = GdipGetPathGradientPresetBlend(grad, res_colors, res_positions, -1);
+    expect(OutOfMemory, status);
+
+    status = GdipGetPathGradientPresetBlend(grad, res_colors, res_positions, 1);
+    expect(InvalidParameter, status);
+
+    status = GdipSetPathGradientPresetBlend(grad, colors, two_positions, 2);
+    expect(Ok, status);
+
+    status = GdipDeleteBrush((GpBrush*)grad);
+    expect(Ok, status);
+}
+
+static void test_pathgradientblend(void)
+{
+    static const GpPointF path_points[] = {{0,0}, {3,0}, {0,4}};
+    GpPathGradient *brush;
+    GpStatus status;
+    INT count, i;
+    const REAL factors[5] = {0.0f, 0.1f, 0.5f, 0.9f, 1.0f};
+    const REAL positions[5] = {0.0f, 0.2f, 0.5f, 0.8f, 1.0f};
+    REAL res_factors[6] = {0.3f, 0.0f, 0.0f, 0.0f, 0.0f};
+    REAL res_positions[6] = {0.3f, 0.0f, 0.0f, 0.0f, 0.0f};
+
+    status = GdipCreatePathGradient(path_points, 3, WrapModeClamp, &brush);
+    expect(Ok, status);
+
+    status = GdipGetPathGradientBlendCount(NULL, &count);
+    expect(InvalidParameter, status);
+
+    status = GdipGetPathGradientBlendCount(brush, NULL);
+    expect(InvalidParameter, status);
+
+    status = GdipGetPathGradientBlendCount(brush, &count);
+    expect(Ok, status);
+    expect(1, count);
+
+    status = GdipGetPathGradientBlend(NULL, res_factors, res_positions, 1);
+    expect(InvalidParameter, status);
+
+    status = GdipGetPathGradientBlend(brush, NULL, res_positions, 1);
+    expect(InvalidParameter, status);
+
+    status = GdipGetPathGradientBlend(brush, res_factors, NULL, 1);
+    expect(InvalidParameter, status);
+
+    status = GdipGetPathGradientBlend(brush, res_factors, res_positions, 0);
+    expect(InvalidParameter, status);
+
+    status = GdipGetPathGradientBlend(brush, res_factors, res_positions, -1);
+    expect(InvalidParameter, status);
+
+    status = GdipGetPathGradientBlend(brush, res_factors, res_positions, 1);
+    expect(Ok, status);
+
+    status = GdipGetPathGradientBlend(brush, res_factors, res_positions, 2);
+    expect(Ok, status);
+
+    status = GdipSetPathGradientBlend(NULL, factors, positions, 5);
+    expect(InvalidParameter, status);
+
+    status = GdipSetPathGradientBlend(brush, NULL, positions, 5);
+    expect(InvalidParameter, status);
+
+    status = GdipSetPathGradientBlend(brush, factors, NULL, 5);
+    expect(InvalidParameter, status);
+
+    status = GdipSetPathGradientBlend(brush, factors, positions, 0);
+    expect(InvalidParameter, status);
+
+    status = GdipSetPathGradientBlend(brush, factors, positions, -1);
+    expect(InvalidParameter, status);
+
+    /* leave off the 0.0 position */
+    status = GdipSetPathGradientBlend(brush, &factors[1], &positions[1], 4);
+    expect(InvalidParameter, status);
+
+    /* leave off the 1.0 position */
+    status = GdipSetPathGradientBlend(brush, factors, positions, 4);
+    expect(InvalidParameter, status);
+
+    status = GdipSetPathGradientBlend(brush, factors, positions, 5);
+    expect(Ok, status);
+
+    status = GdipGetPathGradientBlendCount(brush, &count);
+    expect(Ok, status);
+    expect(5, count);
+
+    status = GdipGetPathGradientBlend(brush, res_factors, res_positions, 4);
+    expect(InsufficientBuffer, status);
+
+    status = GdipGetPathGradientBlend(brush, res_factors, res_positions, 5);
+    expect(Ok, status);
+
+    for (i=0; i<5; i++)
+    {
+        expectf(factors[i], res_factors[i]);
+        expectf(positions[i], res_positions[i]);
+    }
+
+    status = GdipGetPathGradientBlend(brush, res_factors, res_positions, 6);
+    expect(Ok, status);
+
+    status = GdipSetPathGradientBlend(brush, factors, positions, 1);
+    expect(Ok, status);
+
+    status = GdipGetPathGradientBlendCount(brush, &count);
+    expect(Ok, status);
+    expect(1, count);
+
+    status = GdipGetPathGradientBlend(brush, res_factors, res_positions, 1);
+    expect(Ok, status);
+
+    status = GdipDeleteBrush((GpBrush*)brush);
+    expect(Ok, status);
+}
+
 START_TEST(brush)
 {
     struct GdiplusStartupInput gdiplusStartupInput;
@@ -848,6 +1292,10 @@ START_TEST(brush)
     test_lineblend();
     test_linelinearblend();
     test_gradientsurroundcolorcount();
+    test_pathgradientpath();
+    test_pathgradientcenterpoint();
+    test_pathgradientpresetblend();
+    test_pathgradientblend();
 
     GdiplusShutdown(gdiplusToken);
 }
index 98c4114..982db39 100644 (file)
@@ -2,6 +2,7 @@
  * Unit test suite for fonts
  *
  * Copyright (C) 2007 Google (Evan Stade)
+ * Copyright (C) 2012 Dmitry Timoshkov
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
 #include "gdiplus.h"
 #include "wine/test.h"
 
-#define expect(expected, got) ok(got == expected, "Expected %.8x, got %.8x\n", expected, got)
-#define expectf(expected, got) ok(fabs(expected - got) < 0.0001, "Expected %.2f, got %.2f\n", expected, got)
+#define expect(expected, got) ok(got == expected, "Expected %d, got %d\n", expected, got)
+#define expectf(expected, got) ok(fabs(expected - got) < 0.0001, "Expected %f, got %f\n", expected, got)
 
-static const WCHAR arial[] = {'A','r','i','a','l','\0'};
 static const WCHAR nonexistent[] = {'T','h','i','s','F','o','n','t','s','h','o','u','l','d','N','o','t','E','x','i','s','t','\0'};
 static const WCHAR MSSansSerif[] = {'M','S',' ','S','a','n','s',' ','S','e','r','i','f','\0'};
 static const WCHAR MicrosoftSansSerif[] = {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f','\0'};
@@ -50,12 +50,7 @@ static void test_createfont(void)
     expect (FontFamilyNotFound, stat);
     stat = GdipDeleteFont(font);
     expect (InvalidParameter, stat);
-    stat = GdipCreateFontFamilyFromName(arial, NULL, &fontfamily);
-    if(stat == FontFamilyNotFound)
-    {
-        skip("Arial not installed\n");
-        return;
-    }
+    stat = GdipCreateFontFamilyFromName(Tahoma, NULL, &fontfamily);
     expect (Ok, stat);
     stat = GdipCreateFont(fontfamily, 12, FontStyleRegular, UnitPoint, &font);
     expect (Ok, stat);
@@ -67,7 +62,7 @@ static void test_createfont(void)
     expect(Ok, stat);
     stat = GdipGetFamilyName(fontfamily2, familyname, 0);
     expect(Ok, stat);
-    ok (lstrcmpiW(arial, familyname) == 0, "Expected arial, got %s\n",
+    ok (lstrcmpiW(Tahoma, familyname) == 0, "Expected Tahoma, got %s\n",
             wine_dbgstr_w(familyname));
     stat = GdipDeleteFontFamily(fontfamily2);
     expect(Ok, stat);
@@ -96,12 +91,17 @@ static void test_logfont(void)
 {
     LOGFONTA lfa, lfa2;
     GpFont *font;
+    GpFontFamily *family;
     GpStatus stat;
     GpGraphics *graphics;
     HDC hdc = GetDC(0);
     INT style;
+    REAL rval;
+    UINT16 em_height, line_spacing;
+    Unit unit;
 
     GdipCreateFromHDC(hdc, &graphics);
+
     memset(&lfa, 0, sizeof(LOGFONTA));
     memset(&lfa2, 0xff, sizeof(LOGFONTA));
 
@@ -110,14 +110,9 @@ static void test_logfont(void)
     stat = GdipCreateFontFromLogfontA(hdc, &lfa, &font);
     expect(NotTrueTypeFont, stat);
 
-    lstrcpyA(lfa.lfFaceName, "Arial");
+    lstrcpyA(lfa.lfFaceName, "Tahoma");
 
     stat = GdipCreateFontFromLogfontA(hdc, &lfa, &font);
-    if (stat == FileNotFound)
-    {
-        skip("Arial not installed.\n");
-        return;
-    }
     expect(Ok, stat);
     stat = GdipGetLogFontA(font, graphics, &lfa2);
     expect(Ok, stat);
@@ -146,7 +141,7 @@ static void test_logfont(void)
     lfa.lfItalic = lfa.lfUnderline = lfa.lfStrikeOut = TRUE;
 
     memset(&lfa2, 0xff, sizeof(LOGFONTA));
-    lstrcpyA(lfa.lfFaceName, "Arial");
+    lstrcpyA(lfa.lfFaceName, "Tahoma");
 
     stat = GdipCreateFontFromLogfontA(hdc, &lfa, &font);
     expect(Ok, stat);
@@ -173,6 +168,60 @@ static void test_logfont(void)
     ok (style == (FontStyleItalic | FontStyleUnderline | FontStyleStrikeout),
             "Expected , got %d\n", style);
 
+    stat = GdipGetFontUnit(font, &unit);
+    expect(Ok, stat);
+    expect(UnitWorld, unit);
+
+    stat = GdipGetFontHeight(font, graphics, &rval);
+    expect(Ok, stat);
+    expectf(25.347656, rval);
+    stat = GdipGetFontSize(font, &rval);
+    expect(Ok, stat);
+    expectf(21.0, rval);
+
+    stat = GdipGetFamily(font, &family);
+    expect(Ok, stat);
+    stat = GdipGetEmHeight(family, FontStyleRegular, &em_height);
+    expect(Ok, stat);
+    expect(2048, em_height);
+    stat = GdipGetLineSpacing(family, FontStyleRegular, &line_spacing);
+    expect(Ok, stat);
+    expect(2472, line_spacing);
+    GdipDeleteFontFamily(family);
+
+    GdipDeleteFont(font);
+
+    memset(&lfa, 0, sizeof(lfa));
+    lfa.lfHeight = -25;
+    lstrcpyA(lfa.lfFaceName, "Tahoma");
+    stat = GdipCreateFontFromLogfontA(hdc, &lfa, &font);
+    expect(Ok, stat);
+    memset(&lfa2, 0xff, sizeof(lfa2));
+    stat = GdipGetLogFontA(font, graphics, &lfa2);
+    expect(Ok, stat);
+    expect(lfa.lfHeight, lfa2.lfHeight);
+
+    stat = GdipGetFontUnit(font, &unit);
+    expect(Ok, stat);
+    expect(UnitWorld, unit);
+
+    stat = GdipGetFontHeight(font, graphics, &rval);
+    expect(Ok, stat);
+    expectf(30.175781, rval);
+    stat = GdipGetFontSize(font, &rval);
+    expect(Ok, stat);
+    expectf(25.0, rval);
+
+    stat = GdipGetFamily(font, &family);
+    expect(Ok, stat);
+    stat = GdipGetEmHeight(family, FontStyleRegular, &em_height);
+    expect(Ok, stat);
+    expect(2048, em_height);
+    stat = GdipGetLineSpacing(family, FontStyleRegular, &line_spacing);
+    expect(Ok, stat);
+    expect(2472, line_spacing);
+    GdipDeleteFontFamily(family);
+
     GdipDeleteFont(font);
 
     GdipDeleteGraphics(graphics);
@@ -186,7 +235,7 @@ static void test_fontfamily (void)
     GpStatus stat;
 
     /* FontFamily cannot be NULL */
-    stat = GdipCreateFontFamilyFromName (arial , NULL, NULL);
+    stat = GdipCreateFontFamilyFromName (Tahoma , NULL, NULL);
     expect (InvalidParameter, stat);
 
     /* FontFamily must be able to actually find the family.
@@ -200,17 +249,12 @@ static void test_fontfamily (void)
     expect (FontFamilyNotFound, stat);
     if(stat == Ok) GdipDeleteFontFamily(family);
 
-    stat = GdipCreateFontFamilyFromName (arial, NULL, &family);
-    if(stat == FontFamilyNotFound)
-    {
-        skip("Arial not installed\n");
-        return;
-    }
+    stat = GdipCreateFontFamilyFromName (Tahoma, NULL, &family);
     expect (Ok, stat);
 
     stat = GdipGetFamilyName (family, itsName, LANG_NEUTRAL);
     expect (Ok, stat);
-    expect (0, lstrcmpiW(itsName, arial));
+    expect (0, lstrcmpiW(itsName, Tahoma));
 
     if (0)
     {
@@ -226,7 +270,7 @@ static void test_fontfamily (void)
     GdipDeleteFontFamily(family);
     stat = GdipGetFamilyName(clonedFontFamily, itsName, LANG_NEUTRAL);
     expect(Ok, stat);
-    expect(0, lstrcmpiW(itsName, arial));
+    expect(0, lstrcmpiW(itsName, Tahoma));
 
     GdipDeleteFontFamily(clonedFontFamily);
 }
@@ -237,28 +281,25 @@ static void test_fontfamily_properties (void)
     GpStatus stat;
     UINT16 result = 0;
 
-    stat = GdipCreateFontFamilyFromName(arial, NULL, &FontFamily);
-    if(stat == FontFamilyNotFound)
-        skip("Arial not installed\n");
-    else
-    {
-        stat = GdipGetLineSpacing(FontFamily, FontStyleRegular, &result);
-        expect(Ok, stat);
-        ok (result == 2355, "Expected 2355, got %d\n", result);
-        result = 0;
-        stat = GdipGetEmHeight(FontFamily, FontStyleRegular, &result);
-        expect(Ok, stat);
-        ok(result == 2048, "Expected 2048, got %d\n", result);
-        result = 0;
-        stat = GdipGetCellAscent(FontFamily, FontStyleRegular, &result);
-        expect(Ok, stat);
-        ok(result == 1854, "Expected 1854, got %d\n", result);
-        result = 0;
-        stat = GdipGetCellDescent(FontFamily, FontStyleRegular, &result);
-        expect(Ok, stat);
-        ok(result == 434, "Expected 434, got %d\n", result);
-        GdipDeleteFontFamily(FontFamily);
-    }
+    stat = GdipCreateFontFamilyFromName(Tahoma, NULL, &FontFamily);
+    expect(Ok, stat);
+
+    stat = GdipGetLineSpacing(FontFamily, FontStyleRegular, &result);
+    expect(Ok, stat);
+    ok (result == 2472, "Expected 2472, got %d\n", result);
+    result = 0;
+    stat = GdipGetEmHeight(FontFamily, FontStyleRegular, &result);
+    expect(Ok, stat);
+    ok(result == 2048, "Expected 2048, got %d\n", result);
+    result = 0;
+    stat = GdipGetCellAscent(FontFamily, FontStyleRegular, &result);
+    expect(Ok, stat);
+    ok(result == 2049, "Expected 2049, got %d\n", result);
+    result = 0;
+    stat = GdipGetCellDescent(FontFamily, FontStyleRegular, &result);
+    expect(Ok, stat);
+    ok(result == 423, "Expected 423, got %d\n", result);
+    GdipDeleteFontFamily(FontFamily);
 
     stat = GdipCreateFontFamilyFromName(TimesNewRoman, NULL, &FontFamily);
     if(stat == FontFamilyNotFound)
@@ -362,13 +403,9 @@ static void test_heightgivendpi(void)
     GpFont* font = NULL;
     GpFontFamily* fontfamily = NULL;
     REAL height;
+    Unit unit;
 
-    stat = GdipCreateFontFamilyFromName(arial, NULL, &fontfamily);
-    if(stat == FontFamilyNotFound)
-    {
-        skip("Arial not installed\n");
-        return;
-    }
+    stat = GdipCreateFontFamilyFromName(Tahoma, NULL, &fontfamily);
     expect(Ok, stat);
 
     stat = GdipCreateFont(fontfamily, 30, FontStyleRegular, UnitPixel, &font);
@@ -382,15 +419,20 @@ static void test_heightgivendpi(void)
 
     stat = GdipGetFontHeightGivenDPI(font, 96, &height);
     expect(Ok, stat);
-    expectf((REAL)34.497070, height);
+    expectf(36.210938, height);
     GdipDeleteFont(font);
 
     height = 12345;
     stat = GdipCreateFont(fontfamily, 30, FontStyleRegular, UnitWorld, &font);
     expect(Ok, stat);
+
+    stat = GdipGetFontUnit(font, &unit);
+    expect(Ok, stat);
+    expect(UnitWorld, unit);
+
     stat = GdipGetFontHeightGivenDPI(font, 96, &height);
     expect(Ok, stat);
-    expectf((REAL)34.497070, height);
+    expectf(36.210938, height);
     GdipDeleteFont(font);
 
     height = 12345;
@@ -398,36 +440,288 @@ static void test_heightgivendpi(void)
     expect(Ok, stat);
     stat = GdipGetFontHeightGivenDPI(font, 96, &height);
     expect(Ok, stat);
-    expectf((REAL)45.996094, height);
+    expectf(48.281250, height);
     GdipDeleteFont(font);
 
     height = 12345;
     stat = GdipCreateFont(fontfamily, 30, FontStyleRegular, UnitInch, &font);
     expect(Ok, stat);
+
+    stat = GdipGetFontUnit(font, &unit);
+    expect(Ok, stat);
+    expect(UnitInch, unit);
+
     stat = GdipGetFontHeightGivenDPI(font, 96, &height);
     expect(Ok, stat);
-    expectf((REAL)3311.718750, height);
+    expectf(3476.250000, height);
     GdipDeleteFont(font);
 
     height = 12345;
     stat = GdipCreateFont(fontfamily, 30, FontStyleRegular, UnitDocument, &font);
     expect(Ok, stat);
+
+    stat = GdipGetFontUnit(font, &unit);
+    expect(Ok, stat);
+    expect(UnitDocument, unit);
+
     stat = GdipGetFontHeightGivenDPI(font, 96, &height);
     expect(Ok, stat);
-    expectf((REAL)11.039062, height);
+    expectf(11.587500, height);
     GdipDeleteFont(font);
 
     height = 12345;
     stat = GdipCreateFont(fontfamily, 30, FontStyleRegular, UnitMillimeter, &font);
     expect(Ok, stat);
+
+    stat = GdipGetFontUnit(font, &unit);
+    expect(Ok, stat);
+    expect(UnitMillimeter, unit);
+
     stat = GdipGetFontHeightGivenDPI(font, 96, &height);
     expect(Ok, stat);
-    expectf((REAL)130.382614, height);
+    expectf(136.860245, height);
     GdipDeleteFont(font);
 
     GdipDeleteFontFamily(fontfamily);
 }
 
+static int CALLBACK font_enum_proc(const LOGFONTW *lfe, const TEXTMETRICW *ntme,
+                                   DWORD type, LPARAM lparam)
+{
+    NEWTEXTMETRICW *ntm = (NEWTEXTMETRICW *)lparam;
+
+    if (type != TRUETYPE_FONTTYPE) return 1;
+
+    *ntm = *(NEWTEXTMETRICW *)ntme;
+    return 0;
+}
+
+struct font_metrics
+{
+    UINT16 em_height, line_spacing, ascent, descent;
+    REAL font_height, font_size;
+    INT lfHeight;
+};
+
+static void gdi_get_font_metrics(LOGFONTW *lf, struct font_metrics *fm)
+{
+    HDC hdc;
+    HFONT hfont;
+    NEWTEXTMETRICW ntm;
+    OUTLINETEXTMETRICW otm;
+    int ret;
+
+    hdc = CreateCompatibleDC(0);
+
+    /* it's the only way to get extended NEWTEXTMETRIC fields */
+    ret = EnumFontFamiliesExW(hdc, lf, font_enum_proc, (LPARAM)&ntm, 0);
+    ok(!ret, "EnumFontFamiliesExW failed to find %s\n", wine_dbgstr_w(lf->lfFaceName));
+
+    hfont = CreateFontIndirectW(lf);
+    SelectObject(hdc, hfont);
+
+    otm.otmSize = sizeof(otm);
+    ret = GetOutlineTextMetricsW(hdc, otm.otmSize, &otm);
+    ok(ret, "GetOutlineTextMetrics failed\n");
+
+    DeleteDC(hdc);
+    DeleteObject(hfont);
+
+    fm->lfHeight = -otm.otmTextMetrics.tmAscent;
+    fm->line_spacing = ntm.ntmCellHeight;
+    fm->font_size = (REAL)otm.otmTextMetrics.tmAscent;
+    fm->font_height = (REAL)fm->line_spacing * fm->font_size / (REAL)ntm.ntmSizeEM;
+    fm->em_height = ntm.ntmSizeEM;
+    fm->ascent = ntm.ntmSizeEM;
+    fm->descent = ntm.ntmCellHeight - ntm.ntmSizeEM;
+}
+
+static void gdip_get_font_metrics(GpFont *font, struct font_metrics *fm)
+{
+    INT style;
+    GpFontFamily *family;
+    GpStatus stat;
+
+    stat = GdipGetFontStyle(font, &style);
+    expect(Ok, stat);
+
+    stat = GdipGetFontHeight(font, NULL, &fm->font_height);
+    expect(Ok, stat);
+    stat = GdipGetFontSize(font, &fm->font_size);
+    expect(Ok, stat);
+
+    fm->lfHeight = (INT)(fm->font_size * -1.0);
+
+    stat = GdipGetFamily(font, &family);
+    expect(Ok, stat);
+
+    stat = GdipGetEmHeight(family, style, &fm->em_height);
+    expect(Ok, stat);
+    stat = GdipGetLineSpacing(family, style, &fm->line_spacing);
+    expect(Ok, stat);
+    stat = GdipGetCellAscent(family, style, &fm->ascent);
+    expect(Ok, stat);
+    stat = GdipGetCellDescent(family, style, &fm->descent);
+    expect(Ok, stat);
+
+    GdipDeleteFontFamily(family);
+}
+
+static void cmp_font_metrics(struct font_metrics *fm1, struct font_metrics *fm2, int line)
+{
+    ok_(__FILE__, line)(fm1->lfHeight == fm2->lfHeight, "lfHeight %d != %d\n", fm1->lfHeight, fm2->lfHeight);
+    ok_(__FILE__, line)(fm1->em_height == fm2->em_height, "em_height %u != %u\n", fm1->em_height, fm2->em_height);
+    ok_(__FILE__, line)(fm1->line_spacing == fm2->line_spacing, "line_spacing %u != %u\n", fm1->line_spacing, fm2->line_spacing);
+    ok_(__FILE__, line)(abs(fm1->ascent - fm2->ascent) <= 1, "ascent %u != %u\n", fm1->ascent, fm2->ascent);
+    ok_(__FILE__, line)(abs(fm1->descent - fm2->descent) <= 1, "descent %u != %u\n", fm1->descent, fm2->descent);
+    ok(fm1->font_height > 0.0, "fm1->font_height should be positive, got %f\n", fm1->font_height);
+    ok(fm2->font_height > 0.0, "fm2->font_height should be positive, got %f\n", fm2->font_height);
+    ok_(__FILE__, line)(fm1->font_height == fm2->font_height, "font_height %f != %f\n", fm1->font_height, fm2->font_height);
+    ok(fm1->font_size > 0.0, "fm1->font_size should be positive, got %f\n", fm1->font_size);
+    ok(fm2->font_size > 0.0, "fm2->font_size should be positive, got %f\n", fm2->font_size);
+    ok_(__FILE__, line)(fm1->font_size == fm2->font_size, "font_size %f != %f\n", fm1->font_size, fm2->font_size);
+}
+
+static void test_font_metrics(void)
+{
+    LOGFONTW lf;
+    GpFont *font;
+    GpFontFamily *family;
+    GpGraphics *graphics;
+    GpStatus stat;
+    Unit unit;
+    struct font_metrics fm_gdi, fm_gdip;
+    HDC hdc;
+
+    hdc = CreateCompatibleDC(0);
+    stat = GdipCreateFromHDC(hdc, &graphics);
+    expect(Ok, stat);
+
+    memset(&lf, 0, sizeof(lf));
+
+    /* Tahoma,-13 */
+    lstrcpyW(lf.lfFaceName, Tahoma);
+    lf.lfHeight = -13;
+    stat = GdipCreateFontFromLogfontW(hdc, &lf, &font);
+    expect(Ok, stat);
+
+    stat = GdipGetFontUnit(font, &unit);
+    expect(Ok, stat);
+    expect(UnitWorld, unit);
+
+    gdip_get_font_metrics(font, &fm_gdip);
+    trace("gdiplus:\n");
+    trace("%s,%d: EmHeight %u, LineSpacing %u, CellAscent %u, CellDescent %u, FontHeight %f, FontSize %f\n",
+          wine_dbgstr_w(lf.lfFaceName), lf.lfHeight,
+          fm_gdip.em_height, fm_gdip.line_spacing, fm_gdip.ascent, fm_gdip.descent,
+          fm_gdip.font_height, fm_gdip.font_size);
+
+    gdi_get_font_metrics(&lf, &fm_gdi);
+    trace("gdi:\n");
+    trace("%s,%d: EmHeight %u, LineSpacing %u, CellAscent %u, CellDescent %u, FontHeight %f, FontSize %f\n",
+          wine_dbgstr_w(lf.lfFaceName), lf.lfHeight,
+          fm_gdi.em_height, fm_gdi.line_spacing, fm_gdi.ascent, fm_gdi.descent,
+          fm_gdi.font_height, fm_gdi.font_size);
+
+    cmp_font_metrics(&fm_gdip, &fm_gdi, __LINE__);
+
+    stat = GdipGetLogFontW(font, graphics, &lf);
+    expect(Ok, stat);
+    ok(lf.lfHeight < 0, "lf.lfHeight should be negative, got %d\n", lf.lfHeight);
+    gdi_get_font_metrics(&lf, &fm_gdi);
+    trace("gdi:\n");
+    trace("%s,%d: EmHeight %u, LineSpacing %u, CellAscent %u, CellDescent %u, FontHeight %f, FontSize %f\n",
+          wine_dbgstr_w(lf.lfFaceName), lf.lfHeight,
+          fm_gdi.em_height, fm_gdi.line_spacing, fm_gdi.ascent, fm_gdi.descent,
+          fm_gdi.font_height, fm_gdi.font_size);
+    ok((REAL)lf.lfHeight * -1.0 == fm_gdi.font_size, "expected %f, got %f\n", (REAL)lf.lfHeight * -1.0, fm_gdi.font_size);
+
+    cmp_font_metrics(&fm_gdip, &fm_gdi, __LINE__);
+
+    GdipDeleteFont(font);
+
+    /* Tahoma,13 */
+    lstrcpyW(lf.lfFaceName, Tahoma);
+    lf.lfHeight = 13;
+    stat = GdipCreateFontFromLogfontW(hdc, &lf, &font);
+    expect(Ok, stat);
+
+    stat = GdipGetFontUnit(font, &unit);
+    expect(Ok, stat);
+    expect(UnitWorld, unit);
+
+    gdip_get_font_metrics(font, &fm_gdip);
+    trace("gdiplus:\n");
+    trace("%s,%d: EmHeight %u, LineSpacing %u, CellAscent %u, CellDescent %u, FontHeight %f, FontSize %f\n",
+          wine_dbgstr_w(lf.lfFaceName), lf.lfHeight,
+          fm_gdip.em_height, fm_gdip.line_spacing, fm_gdip.ascent, fm_gdip.descent,
+          fm_gdip.font_height, fm_gdip.font_size);
+
+    gdi_get_font_metrics(&lf, &fm_gdi);
+    trace("gdi:\n");
+    trace("%s,%d: EmHeight %u, LineSpacing %u, CellAscent %u, CellDescent %u, FontHeight %f, FontSize %f\n",
+          wine_dbgstr_w(lf.lfFaceName), lf.lfHeight,
+          fm_gdi.em_height, fm_gdi.line_spacing, fm_gdi.ascent, fm_gdi.descent,
+          fm_gdi.font_height, fm_gdi.font_size);
+
+    cmp_font_metrics(&fm_gdip, &fm_gdi, __LINE__);
+
+    stat = GdipGetLogFontW(font, graphics, &lf);
+    expect(Ok, stat);
+    ok(lf.lfHeight < 0, "lf.lfHeight should be negative, got %d\n", lf.lfHeight);
+    gdi_get_font_metrics(&lf, &fm_gdi);
+    trace("gdi:\n");
+    trace("%s,%d: EmHeight %u, LineSpacing %u, CellAscent %u, CellDescent %u, FontHeight %f, FontSize %f\n",
+          wine_dbgstr_w(lf.lfFaceName), lf.lfHeight,
+          fm_gdi.em_height, fm_gdi.line_spacing, fm_gdi.ascent, fm_gdi.descent,
+          fm_gdi.font_height, fm_gdi.font_size);
+    ok((REAL)lf.lfHeight * -1.0 == fm_gdi.font_size, "expected %f, got %f\n", (REAL)lf.lfHeight * -1.0, fm_gdi.font_size);
+
+    cmp_font_metrics(&fm_gdip, &fm_gdi, __LINE__);
+
+    GdipDeleteFont(font);
+
+    stat = GdipCreateFontFamilyFromName(Tahoma, NULL, &family);
+    expect(Ok, stat);
+
+    /* Tahoma,13 */
+    stat = GdipCreateFont(family, 13.0, FontStyleRegular, UnitPixel, &font);
+    expect(Ok, stat);
+
+    gdip_get_font_metrics(font, &fm_gdip);
+    trace("gdiplus:\n");
+    trace("%s,%d: EmHeight %u, LineSpacing %u, CellAscent %u, CellDescent %u, FontHeight %f, FontSize %f\n",
+          wine_dbgstr_w(lf.lfFaceName), lf.lfHeight,
+          fm_gdip.em_height, fm_gdip.line_spacing, fm_gdip.ascent, fm_gdip.descent,
+          fm_gdip.font_height, fm_gdip.font_size);
+
+    stat = GdipGetLogFontW(font, graphics, &lf);
+    expect(Ok, stat);
+    ok(lf.lfHeight < 0, "lf.lfHeight should be negative, got %d\n", lf.lfHeight);
+    gdi_get_font_metrics(&lf, &fm_gdi);
+    trace("gdi:\n");
+    trace("%s,%d: EmHeight %u, LineSpacing %u, CellAscent %u, CellDescent %u, FontHeight %f, FontSize %f\n",
+          wine_dbgstr_w(lf.lfFaceName), lf.lfHeight,
+          fm_gdi.em_height, fm_gdi.line_spacing, fm_gdi.ascent, fm_gdi.descent,
+          fm_gdi.font_height, fm_gdi.font_size);
+    ok((REAL)lf.lfHeight * -1.0 == fm_gdi.font_size, "expected %f, got %f\n", (REAL)lf.lfHeight * -1.0, fm_gdi.font_size);
+
+    cmp_font_metrics(&fm_gdip, &fm_gdi, __LINE__);
+
+    stat = GdipGetLogFontW(font, NULL, &lf);
+    expect(InvalidParameter, stat);
+
+    GdipDeleteFont(font);
+
+    stat = GdipCreateFont(family, -13.0, FontStyleRegular, UnitPixel, &font);
+    expect(InvalidParameter, stat);
+
+    GdipDeleteFontFamily(family);
+
+    GdipDeleteGraphics(graphics);
+    DeleteDC(hdc);
+}
+
 START_TEST(font)
 {
     struct GdiplusStartupInput gdiplusStartupInput;
@@ -440,6 +734,7 @@ START_TEST(font)
 
     GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
 
+    test_font_metrics();
     test_createfont();
     test_logfont();
     test_fontfamily();
diff --git a/rostests/winetests/gdiplus/metafile.c b/rostests/winetests/gdiplus/metafile.c
new file mode 100644 (file)
index 0000000..9530b0c
--- /dev/null
@@ -0,0 +1,568 @@
+/*
+ * Unit test suite for metafiles
+ *
+ * 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 "windows.h"
+#include <stdio.h>
+#include "gdiplus.h"
+#include "wine/test.h"
+
+#define expect(expected, got) ok(got == expected, "Expected %.8x, got %.8x\n", expected, got)
+
+typedef struct emfplus_record
+{
+    ULONG todo;
+    ULONG record_type;
+    ULONG playback_todo;
+} emfplus_record;
+
+typedef struct emfplus_check_state
+{
+    const char *desc;
+    int count;
+    const struct emfplus_record *expected;
+    GpMetafile *metafile;
+} emfplus_check_state;
+
+static void check_record(int count, const char *desc, const struct emfplus_record *expected, const struct emfplus_record *actual)
+{
+    if (expected->todo)
+        todo_wine ok(expected->record_type == actual->record_type,
+            "%s.%i: Expected record type 0x%x, got 0x%x\n", desc, count,
+            expected->record_type, actual->record_type);
+    else
+        ok(expected->record_type == actual->record_type,
+            "%s.%i: Expected record type 0x%x, got 0x%x\n", desc, count,
+            expected->record_type, actual->record_type);
+}
+
+typedef struct EmfPlusRecordHeader
+{
+    WORD Type;
+    WORD Flags;
+    DWORD Size;
+    DWORD DataSize;
+} EmfPlusRecordHeader;
+
+static int CALLBACK enum_emf_proc(HDC hDC, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR,
+    int nObj, LPARAM lpData)
+{
+    emfplus_check_state *state = (emfplus_check_state*)lpData;
+    emfplus_record actual;
+
+    if (lpEMFR->iType == EMR_GDICOMMENT)
+    {
+        const EMRGDICOMMENT *comment = (const EMRGDICOMMENT*)lpEMFR;
+
+        if (comment->cbData >= 4 && memcmp(comment->Data, "EMF+", 4) == 0)
+        {
+            int offset = 4;
+
+            while (offset + sizeof(EmfPlusRecordHeader) <= comment->cbData)
+            {
+                const EmfPlusRecordHeader *record = (const EmfPlusRecordHeader*)&comment->Data[offset];
+
+                ok(record->Size == record->DataSize + sizeof(EmfPlusRecordHeader),
+                    "%s: EMF+ record datasize %u and size %u mismatch\n", state->desc, record->DataSize, record->Size);
+
+                ok(offset + record->DataSize <= comment->cbData,
+                    "%s: EMF+ record truncated\n", state->desc);
+
+                if (offset + record->DataSize > comment->cbData)
+                    return 0;
+
+                if (state->expected[state->count].record_type)
+                {
+                    actual.todo = 0;
+                    actual.record_type = record->Type;
+
+                    check_record(state->count, state->desc, &state->expected[state->count], &actual);
+
+                    state->count++;
+                }
+                else
+                {
+                    ok(0, "%s: Unexpected EMF+ 0x%x record\n", state->desc, record->Type);
+                }
+
+                offset += record->Size;
+            }
+
+            ok(offset == comment->cbData, "%s: truncated EMF+ record data?\n", state->desc);
+
+            return 1;
+        }
+    }
+
+    if (state->expected[state->count].record_type)
+    {
+        actual.todo = 0;
+        actual.record_type = lpEMFR->iType;
+
+        check_record(state->count, state->desc, &state->expected[state->count], &actual);
+
+        state->count++;
+    }
+    else
+    {
+        ok(0, "%s: Unexpected EMF 0x%x record\n", state->desc, lpEMFR->iType);
+    }
+
+    return 1;
+}
+
+static void check_emfplus(HENHMETAFILE hemf, const emfplus_record *expected, const char *desc)
+{
+    emfplus_check_state state;
+
+    state.desc = desc;
+    state.count = 0;
+    state.expected = expected;
+
+    EnumEnhMetaFile(0, hemf, enum_emf_proc, &state, NULL);
+
+    if (expected[state.count].todo)
+        todo_wine ok(expected[state.count].record_type == 0, "%s: Got %i records, expecting more\n", desc, state.count);
+    else
+        ok(expected[state.count].record_type == 0, "%s: Got %i records, expecting more\n", desc, state.count);
+}
+
+static BOOL CALLBACK enum_metafile_proc(EmfPlusRecordType record_type, unsigned int flags,
+    unsigned int dataSize, const unsigned char *pStr, void *userdata)
+{
+    emfplus_check_state *state = (emfplus_check_state*)userdata;
+    emfplus_record actual;
+
+    actual.todo = 0;
+    actual.record_type = record_type;
+
+    if (dataSize == 0)
+        ok(pStr == NULL, "non-NULL pStr\n");
+
+    if (state->expected[state->count].record_type)
+    {
+        check_record(state->count, state->desc, &state->expected[state->count], &actual);
+
+        state->count++;
+    }
+    else
+    {
+        ok(0, "%s: Unexpected EMF 0x%x record\n", state->desc, record_type);
+    }
+
+    return TRUE;
+}
+
+static void check_metafile(GpMetafile *metafile, const emfplus_record *expected, const char *desc,
+    const GpPointF *dst_points, const GpRectF *src_rect, Unit src_unit)
+{
+    GpStatus stat;
+    HDC hdc;
+    GpGraphics *graphics;
+    emfplus_check_state state;
+
+    state.desc = desc;
+    state.count = 0;
+    state.expected = expected;
+    state.metafile = metafile;
+
+    hdc = CreateCompatibleDC(0);
+
+    stat = GdipCreateFromHDC(hdc, &graphics);
+    expect(Ok, stat);
+
+    stat = GdipEnumerateMetafileSrcRectDestPoints(graphics, metafile, dst_points,
+        3, src_rect, src_unit, enum_metafile_proc, &state, NULL);
+    expect(Ok, stat);
+
+    if (expected[state.count].todo)
+        todo_wine ok(expected[state.count].record_type == 0, "%s: Got %i records, expecting more\n", desc, state.count);
+    else
+        ok(expected[state.count].record_type == 0, "%s: Got %i records, expecting more\n", desc, state.count);
+
+    GdipDeleteGraphics(graphics);
+
+    DeleteDC(hdc);
+}
+
+static BOOL CALLBACK play_metafile_proc(EmfPlusRecordType record_type, unsigned int flags,
+    unsigned int dataSize, const unsigned char *pStr, void *userdata)
+{
+    emfplus_check_state *state = (emfplus_check_state*)userdata;
+    GpStatus stat;
+
+    stat = GdipPlayMetafileRecord(state->metafile, record_type, flags, dataSize, pStr);
+
+    if (state->expected[state->count].record_type)
+    {
+        if (state->expected[state->count].playback_todo)
+            todo_wine ok(stat == Ok, "%s.%i: GdipPlayMetafileRecord failed with stat %i\n", state->desc, state->count, stat);
+        else
+            ok(stat == Ok, "%s.%i: GdipPlayMetafileRecord failed with stat %i\n", state->desc, state->count, stat);
+        state->count++;
+    }
+    else
+    {
+        if (state->expected[state->count].playback_todo)
+            todo_wine ok(0, "%s: too many records\n", state->desc);
+        else
+            ok(0, "%s: too many records\n", state->desc);
+
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+static void play_metafile(GpMetafile *metafile, GpGraphics *graphics, const emfplus_record *expected,
+    const char *desc, const GpPointF *dst_points, const GpRectF *src_rect, Unit src_unit)
+{
+    GpStatus stat;
+    emfplus_check_state state;
+
+    state.desc = desc;
+    state.count = 0;
+    state.expected = expected;
+    state.metafile = metafile;
+
+    stat = GdipEnumerateMetafileSrcRectDestPoints(graphics, metafile, dst_points,
+        3, src_rect, src_unit, play_metafile_proc, &state, NULL);
+    expect(Ok, stat);
+}
+
+static const emfplus_record empty_records[] = {
+    {0, EMR_HEADER},
+    {0, EmfPlusRecordTypeHeader},
+    {0, EmfPlusRecordTypeEndOfFile},
+    {0, EMR_EOF},
+    {0}
+};
+
+static void test_empty(void)
+{
+    GpStatus stat;
+    GpMetafile *metafile;
+    GpGraphics *graphics;
+    HDC hdc;
+    HENHMETAFILE hemf, dummy;
+    BOOL ret;
+    static const GpRectF frame = {0.0, 0.0, 100.0, 100.0};
+    static const GpPointF dst_points[3] = {{0.0,0.0},{100.0,0.0},{0.0,100.0}};
+    static const WCHAR description[] = {'w','i','n','e','t','e','s','t',0};
+
+    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);
+    expect(Ok, stat);
+
+    DeleteDC(hdc);
+
+    if (stat != Ok)
+        return;
+
+    stat = GdipGetHemfFromMetafile(metafile, &hemf);
+    expect(InvalidParameter, stat);
+
+    stat = GdipGetImageGraphicsContext((GpImage*)metafile, &graphics);
+    expect(Ok, stat);
+
+    stat = GdipGetHemfFromMetafile(metafile, &hemf);
+    expect(InvalidParameter, stat);
+
+    stat = GdipDeleteGraphics(graphics);
+    expect(Ok, stat);
+
+    check_metafile(metafile, empty_records, "empty metafile", dst_points, &frame, UnitPixel);
+
+    stat = GdipGetHemfFromMetafile(metafile, &hemf);
+    expect(Ok, stat);
+
+    stat = GdipGetHemfFromMetafile(metafile, &dummy);
+    expect(InvalidParameter, stat);
+
+    stat = GdipDisposeImage((GpImage*)metafile);
+    expect(Ok, stat);
+
+    check_emfplus(hemf, empty_records, "empty emf");
+
+    ret = DeleteEnhMetaFile(hemf);
+    ok(ret != 0, "Failed to delete enhmetafile %p\n", hemf);
+}
+
+static const emfplus_record getdc_records[] = {
+    {0, EMR_HEADER},
+    {0, EmfPlusRecordTypeHeader},
+    {0, EmfPlusRecordTypeGetDC},
+    {0, EMR_CREATEBRUSHINDIRECT},
+    {0, EMR_SELECTOBJECT},
+    {0, EMR_RECTANGLE},
+    {0, EMR_SELECTOBJECT},
+    {0, EMR_DELETEOBJECT},
+    {0, EmfPlusRecordTypeEndOfFile},
+    {0, EMR_EOF},
+    {0}
+};
+
+static void test_getdc(void)
+{
+    GpStatus stat;
+    GpMetafile *metafile;
+    GpGraphics *graphics;
+    HDC hdc, metafile_dc;
+    HENHMETAFILE hemf;
+    BOOL ret;
+    static const GpRectF frame = {0.0, 0.0, 100.0, 100.0};
+    static const GpPointF dst_points[3] = {{0.0,0.0},{100.0,0.0},{0.0,100.0}};
+    static const GpPointF dst_points_half[3] = {{0.0,0.0},{50.0,0.0},{0.0,50.0}};
+    static const WCHAR description[] = {'w','i','n','e','t','e','s','t',0};
+    HBRUSH hbrush, holdbrush;
+    GpBitmap *bitmap;
+    ARGB color;
+
+    hdc = CreateCompatibleDC(0);
+
+    stat = GdipRecordMetafile(hdc, EmfTypeEmfPlusOnly, &frame, MetafileFrameUnitPixel, description, &metafile);
+    expect(Ok, stat);
+
+    DeleteDC(hdc);
+
+    if (stat != Ok)
+        return;
+
+    stat = GdipGetHemfFromMetafile(metafile, &hemf);
+    expect(InvalidParameter, stat);
+
+    stat = GdipGetImageGraphicsContext((GpImage*)metafile, &graphics);
+    expect(Ok, stat);
+
+    stat = GdipGetDC(graphics, &metafile_dc);
+    expect(Ok, stat);
+
+    if (stat != Ok)
+    {
+        GdipDeleteGraphics(graphics);
+        GdipDisposeImage((GpImage*)metafile);
+        return;
+    }
+
+    hbrush = CreateSolidBrush(0xff0000);
+
+    holdbrush = SelectObject(metafile_dc, hbrush);
+
+    Rectangle(metafile_dc, 25, 25, 75, 75);
+
+    SelectObject(metafile_dc, holdbrush);
+
+    DeleteObject(hbrush);
+
+    stat = GdipReleaseDC(graphics, metafile_dc);
+    expect(Ok, stat);
+
+    stat = GdipDeleteGraphics(graphics);
+    expect(Ok, stat);
+
+    check_metafile(metafile, getdc_records, "getdc metafile", dst_points, &frame, UnitPixel);
+
+    stat = GdipCreateBitmapFromScan0(100, 100, 0, PixelFormat32bppARGB, NULL, &bitmap);
+    expect(Ok, stat);
+
+    stat = GdipGetImageGraphicsContext((GpImage*)bitmap, &graphics);
+    expect(Ok, stat);
+
+    play_metafile(metafile, graphics, getdc_records, "getdc playback", dst_points, &frame, UnitPixel);
+
+    stat = GdipBitmapGetPixel(bitmap, 15, 15, &color);
+    expect(Ok, stat);
+    expect(0, color);
+
+    stat = GdipBitmapGetPixel(bitmap, 50, 50, &color);
+    expect(Ok, stat);
+    expect(0xff0000ff, color);
+
+    stat = GdipBitmapSetPixel(bitmap, 50, 50, 0);
+    expect(Ok, stat);
+
+    play_metafile(metafile, graphics, getdc_records, "getdc playback", dst_points_half, &frame, UnitPixel);
+
+    stat = GdipBitmapGetPixel(bitmap, 15, 15, &color);
+    expect(Ok, stat);
+    expect(0xff0000ff, color);
+
+    stat = GdipBitmapGetPixel(bitmap, 50, 50, &color);
+    expect(Ok, stat);
+    expect(0, color);
+
+    stat = GdipDeleteGraphics(graphics);
+    expect(Ok, stat);
+
+    stat = GdipDisposeImage((GpImage*)bitmap);
+    expect(Ok, stat);
+
+    stat = GdipGetHemfFromMetafile(metafile, &hemf);
+    expect(Ok, stat);
+
+    stat = GdipDisposeImage((GpImage*)metafile);
+    expect(Ok, stat);
+
+    check_emfplus(hemf, getdc_records, "getdc emf");
+
+    ret = DeleteEnhMetaFile(hemf);
+    ok(ret != 0, "Failed to delete enhmetafile %p\n", hemf);
+}
+
+static const emfplus_record emfonly_records[] = {
+    {0, EMR_HEADER},
+    {0, EMR_CREATEBRUSHINDIRECT},
+    {0, EMR_SELECTOBJECT},
+    {0, EMR_RECTANGLE},
+    {0, EMR_SELECTOBJECT},
+    {0, EMR_DELETEOBJECT},
+    {0, EMR_EOF},
+    {0}
+};
+
+static void test_emfonly(void)
+{
+    GpStatus stat;
+    GpMetafile *metafile;
+    GpGraphics *graphics;
+    HDC hdc, metafile_dc;
+    HENHMETAFILE hemf;
+    BOOL ret;
+    static const GpRectF frame = {0.0, 0.0, 100.0, 100.0};
+    static const GpPointF dst_points[3] = {{0.0,0.0},{100.0,0.0},{0.0,100.0}};
+    static const WCHAR description[] = {'w','i','n','e','t','e','s','t',0};
+    HBRUSH hbrush, holdbrush;
+    GpBitmap *bitmap;
+    ARGB color;
+
+    hdc = CreateCompatibleDC(0);
+
+    stat = GdipRecordMetafile(hdc, EmfTypeEmfOnly, &frame, MetafileFrameUnitPixel, description, &metafile);
+    expect(Ok, stat);
+
+    DeleteDC(hdc);
+
+    if (stat != Ok)
+        return;
+
+    stat = GdipGetHemfFromMetafile(metafile, &hemf);
+    expect(InvalidParameter, stat);
+
+    stat = GdipGetImageGraphicsContext((GpImage*)metafile, &graphics);
+    expect(Ok, stat);
+
+    stat = GdipGetDC(graphics, &metafile_dc);
+    expect(Ok, stat);
+
+    if (stat != Ok)
+    {
+        GdipDeleteGraphics(graphics);
+        GdipDisposeImage((GpImage*)metafile);
+        return;
+    }
+
+    hbrush = CreateSolidBrush(0xff0000);
+
+    holdbrush = SelectObject(metafile_dc, hbrush);
+
+    Rectangle(metafile_dc, 25, 25, 75, 75);
+
+    SelectObject(metafile_dc, holdbrush);
+
+    DeleteObject(hbrush);
+
+    stat = GdipReleaseDC(graphics, metafile_dc);
+    expect(Ok, stat);
+
+    stat = GdipDeleteGraphics(graphics);
+    expect(Ok, stat);
+
+    check_metafile(metafile, emfonly_records, "emfonly metafile", dst_points, &frame, UnitPixel);
+
+    stat = GdipCreateBitmapFromScan0(100, 100, 0, PixelFormat32bppARGB, NULL, &bitmap);
+    expect(Ok, stat);
+
+    stat = GdipGetImageGraphicsContext((GpImage*)bitmap, &graphics);
+    expect(Ok, stat);
+
+    play_metafile(metafile, graphics, emfonly_records, "emfonly playback", dst_points, &frame, UnitPixel);
+
+    stat = GdipBitmapGetPixel(bitmap, 15, 15, &color);
+    expect(Ok, stat);
+    expect(0, color);
+
+    stat = GdipBitmapGetPixel(bitmap, 50, 50, &color);
+    expect(Ok, stat);
+    expect(0xff0000ff, color);
+
+    stat = GdipDeleteGraphics(graphics);
+    expect(Ok, stat);
+
+    stat = GdipDisposeImage((GpImage*)bitmap);
+    expect(Ok, stat);
+
+    stat = GdipGetHemfFromMetafile(metafile, &hemf);
+    expect(Ok, stat);
+
+    stat = GdipDisposeImage((GpImage*)metafile);
+    expect(Ok, stat);
+
+    check_emfplus(hemf, emfonly_records, "emfonly emf");
+
+    ret = DeleteEnhMetaFile(hemf);
+    ok(ret != 0, "Failed to delete enhmetafile %p\n", hemf);
+}
+
+START_TEST(metafile)
+{
+    struct GdiplusStartupInput gdiplusStartupInput;
+    ULONG_PTR gdiplusToken;
+
+    gdiplusStartupInput.GdiplusVersion              = 1;
+    gdiplusStartupInput.DebugEventCallback          = NULL;
+    gdiplusStartupInput.SuppressBackgroundThread    = 0;
+    gdiplusStartupInput.SuppressExternalCodecs      = 0;
+
+    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
+
+    test_empty();
+    test_getdc();
+    test_emfonly();
+
+    GdiplusShutdown(gdiplusToken);
+}
index 05456c6..5d6e08b 100644 (file)
@@ -13,6 +13,7 @@ extern void func_graphics(void);
 extern void func_graphicspath(void);
 extern void func_image(void);
 extern void func_matrix(void);
+extern void func_metafile(void);
 extern void func_pathiterator(void);
 extern void func_pen(void);
 extern void func_region(void);
@@ -21,12 +22,13 @@ extern void func_stringformat(void);
 const struct test winetest_testlist[] =
 {
     { "brush", func_brush },
-       { "customlinecap", func_customlinecap },
+    { "customlinecap", func_customlinecap },
     { "font", func_font },
     { "graphics", func_graphics },
     { "graphicspath", func_graphicspath },
     { "image", func_image },
     { "matrix", func_matrix },
+    { "metafile", func_metafile },
     { "pathiterator", func_pathiterator },
     { "pen", func_pen },
     { "region", func_region },