First draft of ExtTextOut on an open path

Dmitry Timoshkov dmitry at baikal.ru
Sat Nov 5 05:48:55 CST 2005


Hello,

there are some things which need additional work to make it look
exactly like in windows, but most of the functionality is there.

I've added a couple of tests to see how it works on metafiles.

Changelog:
    Dmitry Timoshkov <dmitry at codeweavers.com>
    First draft of ExtTextOut on an open path.

diff -up cvs/hq/wine/dlls/gdi/enhmfdrv/graphics.c wine/dlls/gdi/enhmfdrv/graphics.c
--- cvs/hq/wine/dlls/gdi/enhmfdrv/graphics.c	2005-08-23 23:02:55.000000000 +0900
+++ wine/dlls/gdi/enhmfdrv/graphics.c	2005-11-05 19:06:51.000000000 +0800
@@ -722,7 +722,8 @@ BOOL EMFDRV_ExtTextOut( PHYSDEV dev, INT
 
     nSize = sizeof(*pemr) + ((count+1) & ~1) * sizeof(WCHAR) + count * sizeof(INT);
 
-    TRACE("%s count %d nSize = %ld\n", debugstr_wn(str, count), count, nSize);
+    TRACE("%s %s count %d nSize = %ld\n", debugstr_wn(str, count),
+           wine_dbgstr_rect(lprect), count, nSize);
     pemr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, nSize);
 
     pemr->emr.iType = EMR_EXTTEXTOUTW;
diff -up cvs/hq/wine/dlls/gdi/font.c wine/dlls/gdi/font.c
--- cvs/hq/wine/dlls/gdi/font.c	2005-10-31 15:27:03.000000000 +0800
+++ wine/dlls/gdi/font.c	2005-11-05 18:00:21.000000000 +0800
@@ -1757,14 +1757,7 @@ BOOL WINAPI ExtTextOutW( HDC hdc, INT x,
     if (flags & (ETO_NUMERICSLOCAL | ETO_NUMERICSLATIN | ETO_PDY))
         FIXME("flags ETO_NUMERICSLOCAL | ETO_NUMERICSLATIN | ETO_PDY unimplemented\n");
 
-    if(PATH_IsPathOpen(dc->path))
-    {
-        FIXME("called on an open path\n");
-        GDI_ReleaseObj( hdc );
-        return ret;
-    }
-
-    if(!dc->funcs->pExtTextOut)
+    if (!dc->funcs->pExtTextOut && !PATH_IsPathOpen(dc->path))
     {
         GDI_ReleaseObj( hdc );
         return ret;
@@ -1852,7 +1845,7 @@ BOOL WINAPI ExtTextOutW( HDC hdc, INT x,
         if(rc.top > rc.bottom) {INT tmp = rc.top; rc.top = rc.bottom; rc.bottom = tmp;}
     }
 
-    if(flags & ETO_OPAQUE)
+    if ((flags & ETO_OPAQUE) && !PATH_IsPathOpen(dc->path))
         dc->funcs->pExtTextOut(dc->physDev, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL);
 
     if(count == 0)
@@ -1959,7 +1952,7 @@ BOOL WINAPI ExtTextOutW( HDC hdc, INT x,
         break;
     }
 
-    if(GetBkMode(hdc) != TRANSPARENT)
+    if (GetBkMode(hdc) != TRANSPARENT && !PATH_IsPathOpen(dc->path))
     {
         if(!((flags & ETO_CLIPPED) && (flags & ETO_OPAQUE)))
         {
@@ -2010,7 +2003,12 @@ BOOL WINAPI ExtTextOutW( HDC hdc, INT x,
                 }
                 if(span)
                 {
-                    dc->funcs->pExtTextOut(dc->physDev, x + offsets[i - span] * cosEsc, y - offsets[i - span] * sinEsc,
+                    if (PATH_IsPathOpen(dc->path))
+                        ret = PATH_ExtTextOut(dc, x + offsets[i - span] * cosEsc, y - offsets[i - span] * sinEsc,
+                                              (flags & ~ETO_OPAQUE) | ETO_GLYPH_INDEX, &rc,
+                                              glyphs, span, deltas ? deltas + i - span : NULL);
+                    else
+                        dc->funcs->pExtTextOut(dc->physDev, x + offsets[i - span] * cosEsc, y - offsets[i - span] * sinEsc,
                                            (flags & ~ETO_OPAQUE) | ETO_GLYPH_INDEX, &rc,
                                            glyphs, span, deltas ? deltas + i - span : NULL);
                     span = 0;
@@ -2021,7 +2019,13 @@ BOOL WINAPI ExtTextOutW( HDC hdc, INT x,
 
             if(i == count - 1)
             {
-                ret = dc->funcs->pExtTextOut(dc->physDev, x + (offsets ? offsets[count - span] * cosEsc : 0),
+                if (PATH_IsPathOpen(dc->path))
+                    ret = PATH_ExtTextOut(dc, x + (offsets ? offsets[count - span] * cosEsc : 0),
+                                          y - (offsets ? offsets[count - span] * sinEsc : 0),
+                                          (flags & ~ETO_OPAQUE) | ETO_GLYPH_INDEX, &rc,
+                                          glyphs, span, deltas ? deltas + count - span : NULL);
+                else
+                    ret = dc->funcs->pExtTextOut(dc->physDev, x + (offsets ? offsets[count - span] * cosEsc : 0),
                                              y - (offsets ? offsets[count - span] * sinEsc : 0),
                                              (flags & ~ETO_OPAQUE) | ETO_GLYPH_INDEX, &rc,
                                              glyphs, span, deltas ? deltas + count - span : NULL);
@@ -2038,7 +2042,12 @@ BOOL WINAPI ExtTextOutW( HDC hdc, INT x,
             GetGlyphIndicesW(hdc, reordered_str, count, glyphs, 0);
             flags |= ETO_GLYPH_INDEX;
         }
-        ret = dc->funcs->pExtTextOut(dc->physDev, x, y, (flags & ~ETO_OPAQUE), &rc,
+
+        if (PATH_IsPathOpen(dc->path))
+            ret = PATH_ExtTextOut(dc, x, y, (flags & ~ETO_OPAQUE), &rc,
+                                  glyphs ? glyphs : reordered_str, count, deltas);
+        else
+            ret = dc->funcs->pExtTextOut(dc->physDev, x, y, (flags & ~ETO_OPAQUE), &rc,
                                      glyphs ? glyphs : reordered_str, count, deltas);
     }
 
diff -up cvs/hq/wine/dlls/gdi/gdi_private.h wine/dlls/gdi/gdi_private.h
--- cvs/hq/wine/dlls/gdi/gdi_private.h	2005-09-07 22:43:36.000000000 +0900
+++ wine/dlls/gdi/gdi_private.h	2005-11-05 18:01:08.000000000 +0800
@@ -405,6 +405,8 @@ extern BOOL PATH_AssignGdiPath(GdiPath *
 extern BOOL PATH_MoveTo(DC *dc);
 extern BOOL PATH_LineTo(DC *dc, INT x, INT y);
 extern BOOL PATH_Rectangle(DC *dc, INT x1, INT y1, INT x2, INT y2);
+extern BOOL PATH_ExtTextOut(DC *dc, INT x, INT y, UINT flags, const RECT *lprc,
+                            LPCWSTR str, UINT count, const INT *dx);
 extern BOOL PATH_Ellipse(DC *dc, INT x1, INT y1, INT x2, INT y2);
 extern BOOL PATH_Arc(DC *dc, INT x1, INT y1, INT x2, INT y2,
                      INT xStart, INT yStart, INT xEnd, INT yEnd, INT lines);
diff -up cvs/hq/wine/dlls/gdi/path.c wine/dlls/gdi/path.c
--- cvs/hq/wine/dlls/gdi/path.c	2005-08-23 23:02:55.000000000 +0900
+++ wine/dlls/gdi/path.c	2005-11-05 19:13:22.000000000 +0800
@@ -3,6 +3,7 @@
  *
  * Copyright 1997, 1998 Martin Boehme
  *                 1999 Huw D M Davies
+ * Copyright 2005 Dmitry Timoshkov
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -26,6 +27,7 @@
 #include <math.h>
 #include <stdarg.h>
 #include <string.h>
+#include <stdlib.h>
 #if defined(HAVE_FLOAT_H)
 #include <float.h>
 #endif
@@ -1210,6 +1212,208 @@ static BOOL PATH_PathToRegion(GdiPath *p
    return TRUE;
 }
 
+static inline INT int_from_fixed(FIXED f)
+{
+    return (f.fract >= 0x8000) ? (f.value + 1) : f.value;
+}
+
+/**********************************************************************
+ *      PATH_BezierTo
+ *
+ * internally used by PATH_add_outline
+ */
+static void PATH_BezierTo(GdiPath *pPath, POINT *lppt, INT n)
+{
+    if (n < 2) return;
+
+    if (n == 2)
+    {
+        PATH_AddEntry(pPath, &lppt[1], PT_LINETO);
+    }
+    else if (n == 3)
+    {
+        PATH_AddEntry(pPath, &lppt[0], PT_BEZIERTO);
+        PATH_AddEntry(pPath, &lppt[1], PT_BEZIERTO);
+        PATH_AddEntry(pPath, &lppt[2], PT_BEZIERTO);
+    }
+    else
+    {
+        POINT pt[3];
+        INT i = 0;
+
+        pt[2] = lppt[0];
+        n--;
+
+        while (n > 2)
+        {
+            pt[0] = pt[2];
+            pt[1] = lppt[i+1];
+            pt[2].x = (lppt[i+2].x + lppt[i+1].x) / 2;
+            pt[2].y = (lppt[i+2].y + lppt[i+1].y) / 2;
+            PATH_BezierTo(pPath, pt, 3);
+            n--;
+            i++;
+        }
+
+        pt[0] = pt[2];
+        pt[1] = lppt[i+1];
+        pt[2] = lppt[i+2];
+        PATH_BezierTo(pPath, pt, 3);
+    }
+}
+
+static BOOL PATH_add_outline(DC *dc, INT x, INT y, TTPOLYGONHEADER *header, DWORD size)
+{
+    GdiPath *pPath = &dc->path;
+    TTPOLYGONHEADER *start;
+    POINT pt;
+
+    start = header;
+
+    while ((char *)header < (char *)start + size)
+    {
+        TTPOLYCURVE *curve;
+
+        if (header->dwType != TT_POLYGON_TYPE)
+        {
+            FIXME("Unknown header type %ld\n", header->dwType);
+            return FALSE;
+        }
+
+        pt.x = x + int_from_fixed(header->pfxStart.x);
+        pt.y = y - int_from_fixed(header->pfxStart.y);
+        LPtoDP(dc->hSelf, &pt, 1);
+        PATH_AddEntry(pPath, &pt, PT_MOVETO);
+
+        curve = (TTPOLYCURVE *)(header + 1);
+
+        while ((char *)curve < (char *)header + header->cb)
+        {
+            /*TRACE("curve->wType %d\n", curve->wType);*/
+
+            switch(curve->wType)
+            {
+            case TT_PRIM_LINE:
+            {
+                WORD i;
+
+                for (i = 0; i < curve->cpfx; i++)
+                {
+                    pt.x = x + int_from_fixed(curve->apfx[i].x);
+                    pt.y = y - int_from_fixed(curve->apfx[i].y);
+                    LPtoDP(dc->hSelf, &pt, 1);
+                    PATH_AddEntry(pPath, &pt, PT_LINETO);
+                }
+                break;
+            }
+
+            case TT_PRIM_QSPLINE:
+            case TT_PRIM_CSPLINE:
+            {
+                WORD i;
+                POINTFX ptfx;
+                POINT *pts = HeapAlloc(GetProcessHeap(), 0, (curve->cpfx + 1) * sizeof(POINT));
+
+                if (!pts) return FALSE;
+
+                ptfx = *(POINTFX *)((char *)curve - sizeof(POINTFX));
+
+                pts[0].x = x + int_from_fixed(ptfx.x);
+                pts[0].y = y - int_from_fixed(ptfx.y);
+                LPtoDP(dc->hSelf, &pts[0], 1);
+
+                for(i = 0; i < curve->cpfx; i++)
+                {
+                    pts[i + 1].x = x + int_from_fixed(curve->apfx[i].x);
+                    pts[i + 1].y = y - int_from_fixed(curve->apfx[i].y);
+                    LPtoDP(dc->hSelf, &pts[i + 1], 1);
+                }
+
+                PATH_BezierTo(pPath, pts, curve->cpfx + 1);
+
+                HeapFree(GetProcessHeap(), 0, pts);
+                break;
+            }
+
+            default:
+                FIXME("Unknown curve type %04x\n", curve->wType);
+                return FALSE;
+            }
+
+            curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
+        }
+
+        header = (TTPOLYGONHEADER *)((char *)header + header->cb);
+    }
+
+    return CloseFigure(dc->hSelf);
+}
+
+/**********************************************************************
+ *      PATH_ExtTextOut
+ */
+BOOL PATH_ExtTextOut(DC *dc, INT x, INT y, UINT flags, const RECT *lprc,
+                     LPCWSTR str, UINT count, const INT *dx)
+{
+    unsigned int idx;
+    double cosEsc, sinEsc;
+    LOGFONTW lf;
+    POINT org;
+    HDC hdc = dc->hSelf;
+
+    TRACE("%p, %d, %d, %08x, %s, %s, %d, %p)\n", hdc, x, y, flags,
+	  wine_dbgstr_rect(lprc), debugstr_wn(str, count), count, dx);
+
+    if (!count) return TRUE;
+
+    GetObjectW(GetCurrentObject(hdc, OBJ_FONT), sizeof(lf), &lf);
+
+    if (lf.lfEscapement != 0)
+    {
+        cosEsc = cos(lf.lfEscapement * M_PI / 1800);
+        sinEsc = sin(lf.lfEscapement * M_PI / 1800);
+    } else
+    {
+        cosEsc = 1;
+        sinEsc = 0;
+    }
+
+    GetDCOrgEx(hdc, &org);
+
+    for (idx = 0; idx < count; idx++)
+    {
+        INT offset = 0, xoff = 0, yoff = 0;
+        GLYPHMETRICS gm;
+        DWORD dwSize;
+        void *outline;
+
+        dwSize = GetGlyphOutlineW(hdc, str[idx], GGO_GLYPH_INDEX | GGO_NATIVE, &gm, 0, NULL, NULL);
+        if (!dwSize) return FALSE;
+
+        outline = HeapAlloc(GetProcessHeap(), 0, dwSize);
+        if (!outline) return FALSE;
+
+        GetGlyphOutlineW(hdc, str[idx], GGO_GLYPH_INDEX | GGO_NATIVE, &gm, dwSize, outline, NULL);
+
+        PATH_add_outline(dc, org.x + x + xoff, org.x + y + yoff, outline, dwSize);
+
+        HeapFree(GetProcessHeap(), 0, outline);
+
+        if (dx)
+        {
+            offset += dx[idx];
+            xoff = offset * cosEsc;
+            yoff = offset * -sinEsc;
+        }
+        else
+        {
+            xoff += gm.gmCellIncX;
+            yoff += gm.gmCellIncY;
+        }
+    }
+    return TRUE;
+}
+
 /* PATH_EmptyPath
  *
  * Removes all entries from the path and sets the path state to PATH_Null.
diff -up cvs/hq/wine/dlls/gdi/tests/metafile.c wine/dlls/gdi/tests/metafile.c
--- cvs/hq/wine/dlls/gdi/tests/metafile.c	2005-11-02 20:09:12.000000000 +0800
+++ wine/dlls/gdi/tests/metafile.c	2005-11-05 19:37:44.000000000 +0800
@@ -422,6 +422,63 @@ static const unsigned char MF_PATTERN_BR
     0x00, 0x00
 };
 
+static const unsigned char MF_TEXTOUT_ON_PATH_BITS[] =
+{
+    0x01, 0x00, 0x09, 0x00, 0x00, 0x03, 0x19, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x32, 0x0a,
+    0x16, 0x00, 0x0b, 0x00, 0x04, 0x00, 0x00, 0x00,
+    0x54, 0x65, 0x73, 0x74, 0x03, 0x00, 0x05, 0x00,
+    0x08, 0x00, 0x0c, 0x00, 0x03, 0x00, 0x00, 0x00,
+    0x00, 0x00
+};
+
+static const unsigned char EMF_TEXTOUT_ON_PATH_BITS[] =
+{
+    0x01, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0xe7, 0xff, 0xff, 0xff, 0xe9, 0xff, 0xff, 0xff,
+    0x20, 0x45, 0x4d, 0x46, 0x00, 0x00, 0x01, 0x00,
+    0xf4, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+    0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
+    0x40, 0x01, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xe2, 0x04, 0x00,
+    0x80, 0xa9, 0x03, 0x00, 0x3b, 0x00, 0x00, 0x00,
+    0x08, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00,
+    0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0xc8, 0x41, 0x00, 0x80, 0xbb, 0x41,
+    0x0b, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
+    0x04, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0x54, 0x00, 0x00, 0x00,
+    0x54, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00,
+    0x03, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+    0x08, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+    0x3c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+    0x0e, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+    0x14, 0x00, 0x00, 0x00
+};
+
+/* For debugging or dumping the raw metafiles produced by
+ * new test functions.
+ */
+static INT CALLBACK mf_enum_proc(HDC hdc, HANDLETABLE *ht, METARECORD *mr,
+                                 INT nobj, LPARAM param)
+{
+    trace("hdc %p, mr->rdFunction %d, mr->rdSize %lu, param %p\n",
+           hdc, mr->rdFunction, mr->rdSize, (void *)param);
+    return TRUE;
+}
+
 /* For debugging or dumping the raw metafiles produced by
  * new test functions.
  */
@@ -521,6 +578,101 @@ static int compare_mf_disk_bits(LPCSTR n
     return diff; 
 }
 
+/* For debugging or dumping the raw EMFs produced by
+ * new test functions.
+ */
+static void dump_emf_bits(const HENHMETAFILE mf, const char *desc)
+{
+    BYTE buf[MF_BUFSIZE];
+    UINT mfsize, i;
+
+    mfsize = GetEnhMetaFileBits(mf, MF_BUFSIZE, buf);
+    ok (mfsize > 0, "%s: GetEnhMetaFileBits failed\n", desc);
+
+    printf("EMF %s has bits:\n{\n    ", desc);
+    for (i = 0; i < mfsize; i++)
+    {
+        printf ("0x%02x", buf[i]);
+        if (i == mfsize-1)
+            printf ("\n");
+        else if (i % 8 == 7)
+            printf (",\n    ");
+        else
+            printf (", ");
+    }
+    printf ("};\n");
+}
+
+static void dump_emf_records(const HENHMETAFILE mf, const char *desc)
+{
+    BYTE *emf;
+    BYTE buf[MF_BUFSIZE];
+    UINT mfsize, offset;
+
+    mfsize = GetEnhMetaFileBits(mf, MF_BUFSIZE, buf);
+    ok (mfsize > 0, "%s: GetEnhMetaFileBits failed\n", desc);
+
+    printf("EMF %s has records:\n", desc);
+
+    emf = buf;
+    offset = 0;
+    while(offset < mfsize)
+    {
+        EMR *emr = (EMR *)(emf + offset);
+        trace("emr->iType %ld, emr->nSize %lu\n", emr->iType, emr->nSize);
+        /*trace("emr->iType 0x%04lx, emr->nSize 0x%04lx\n", emr->iType, emr->nSize);*/
+        offset += emr->nSize;
+    }
+}
+
+/* Compare the EMF produced by a test function with the
+ * expected raw EMF data in "bits".
+ * Return value is 0 for a perfect match,
+ * -1 if lengths aren't equal,
+ * otherwise returns the number of non-matching bytes.
+ */
+static int compare_emf_bits(const HENHMETAFILE mf, const unsigned char *bits,
+                            UINT bsize, const char *desc, BOOL todo)
+{
+    unsigned char buf[MF_BUFSIZE];
+    UINT mfsize, i;
+    int diff;
+
+    mfsize = GetEnhMetaFileBits(mf, MF_BUFSIZE, buf);
+    ok (mfsize > 0, "%s: GetEnhMetaFileBits failed\n", desc);
+
+    if (mfsize < MF_BUFSIZE)
+        ok(mfsize == bsize, "%s: mfsize=%d, bsize=%d\n", desc, mfsize, bsize);
+    else
+        ok(bsize >= MF_BUFSIZE, "%s: mfsize > bufsize (%d bytes), bsize=%d\n",
+           desc, mfsize, bsize);
+    if (mfsize != bsize)
+        return -1;
+
+    diff = 0;
+    for (i = 0; i < bsize; i++)
+    {
+       if (buf[i] != bits[i])
+           diff++;
+    }
+    if (diff != 0 && todo)
+    {
+        todo_wine
+        {
+            ok(diff == 0, "%s: mfsize=%d, bsize=%d, diff=%d\n",
+               desc, mfsize, bsize, diff);
+        }
+        return 0;
+    }
+    else
+    {
+        ok(diff == 0, "%s: mfsize=%d, bsize=%d, diff=%d\n",
+           desc, mfsize, bsize, diff);
+
+        return diff;
+    }
+}
+
 /* Test a blank metafile.  May be used as a template for new tests. */
 
 static void test_mf_Blank(void)
@@ -548,7 +700,10 @@ static void test_mf_Blank(void)
 
     if (compare_mf_bits (hMetafile, MF_BLANK_BITS, sizeof(MF_BLANK_BITS),
         "mf_blank") != 0)
-            dump_mf_bits (hMetafile, "mf_Blank");
+    {
+        dump_mf_bits(hMetafile, "mf_Blank");
+        EnumMetaFile(0, hMetafile, mf_enum_proc, 0);
+    }
 
     ret = DeleteMetaFile(hMetafile);
     ok( ret, "DeleteMetaFile(%p) error %ld\n", hMetafile, GetLastError());
@@ -574,7 +729,10 @@ static void test_CopyMetaFile(void)
 
     if (compare_mf_bits (hMetafile, MF_BLANK_BITS, sizeof(MF_BLANK_BITS),
         "mf_blank") != 0)
-            dump_mf_bits (hMetafile, "mf_Blank");
+    {
+        dump_mf_bits(hMetafile, "mf_Blank");
+        EnumMetaFile(0, hMetafile, mf_enum_proc, 0);
+    }
 
     GetTempPathA(MAX_PATH, temp_path);
     GetTempFileNameA(temp_path, "wmf", 0, mf_name);
@@ -589,7 +747,10 @@ static void test_CopyMetaFile(void)
     ok( ret, "DeleteMetaFile(%p) error %ld\n", hMetafile, GetLastError());
 
     if (compare_mf_disk_bits(mf_name, MF_BLANK_BITS, sizeof(MF_BLANK_BITS), "mf_blank") != 0)
-        dump_mf_bits(hmf_copy, "mf_Blank");
+    {
+        dump_mf_bits(hMetafile, "mf_Blank");
+        EnumMetaFile(0, hMetafile, mf_enum_proc, 0);
+    }
 
     ret = DeleteMetaFile(hmf_copy);
     ok( ret, "DeleteMetaFile(%p) error %ld\n", hmf_copy, GetLastError());
@@ -611,7 +772,10 @@ static void test_SetMetaFileBits(void)
     ok(type == OBJ_METAFILE, "SetMetaFileBitsEx created object with type %d\n", type);
 
     if (compare_mf_bits(hmf, MF_GRAPHICS_BITS, sizeof(MF_GRAPHICS_BITS), "mf_Graphics") != 0)
+    {
         dump_mf_bits(hmf, "mf_Graphics");
+        EnumMetaFile(0, hmf, mf_enum_proc, 0);
+    }
 
     ret = DeleteMetaFile(hmf);
     ok(ret, "DeleteMetaFile(%p) error %ld\n", hmf, GetLastError());
@@ -653,7 +817,10 @@ static void test_SetMetaFileBits(void)
     ok(hmf != 0, "SetMetaFileBitsEx error %ld\n", GetLastError());
 
     if (compare_mf_bits(hmf, MF_GRAPHICS_BITS, sizeof(MF_GRAPHICS_BITS), "mf_Graphics") != 0)
+    {
         dump_mf_bits(hmf, "mf_Graphics");
+        EnumMetaFile(0, hmf, mf_enum_proc, 0);
+    }
 
     ret = DeleteMetaFile(hmf);
     ok(ret, "DeleteMetaFile(%p) error %ld\n", hmf, GetLastError());
@@ -667,7 +834,10 @@ static void test_SetMetaFileBits(void)
     ok(hmf != 0, "SetMetaFileBitsEx error %ld\n", GetLastError());
 
     if (compare_mf_bits(hmf, MF_GRAPHICS_BITS, sizeof(MF_GRAPHICS_BITS), "mf_Graphics") != 0)
+    {
         dump_mf_bits(hmf, "mf_Graphics");
+        EnumMetaFile(0, hmf, mf_enum_proc, 0);
+    }
 
     ret = DeleteMetaFile(hmf);
     ok(ret, "DeleteMetaFile(%p) error %ld\n", hmf, GetLastError());
@@ -711,7 +881,10 @@ static void test_mf_Graphics(void)
 
     if (compare_mf_bits (hMetafile, MF_GRAPHICS_BITS, sizeof(MF_GRAPHICS_BITS),
         "mf_Graphics") != 0)
-            dump_mf_bits (hMetafile, "mf_Graphics");
+    {
+        dump_mf_bits(hMetafile, "mf_Graphics");
+        EnumMetaFile(0, hMetafile, mf_enum_proc, 0);
+    }
 
     ret = DeleteMetaFile(hMetafile);
     ok( ret, "DeleteMetaFile(%p) error %ld\n",
@@ -749,7 +922,10 @@ static void test_mf_PatternBrush(void)
 
     if (compare_mf_bits (hMetafile, MF_PATTERN_BRUSH_BITS, sizeof(MF_PATTERN_BRUSH_BITS),
         "mf_Pattern_Brush") != 0)
-            dump_mf_bits (hMetafile, "mf_Pattern_Brush");
+    {
+        dump_mf_bits(hMetafile, "mf_Pattern_Brush");
+        EnumMetaFile(0, hMetafile, mf_enum_proc, 0);
+    }
 
     ret = DeleteMetaFile(hMetafile);
     ok( ret, "DeleteMetaFile error %ld\n", GetLastError());
@@ -761,6 +937,88 @@ static void test_mf_PatternBrush(void)
     HeapFree (GetProcessHeap(), 0, orig_lb);
 }
 
+static void test_mf_ExtTextOut_on_path(void)
+{
+    HDC hdcMetafile;
+    HMETAFILE hMetafile;
+    BOOL ret;
+    static const INT dx[4] = { 3, 5, 8, 12 };
+
+    hdcMetafile = CreateMetaFileA(NULL);
+    ok(hdcMetafile != 0, "CreateMetaFileA(NULL) error %ld\n", GetLastError());
+    trace("hdcMetafile %p\n", hdcMetafile);
+
+    ret = BeginPath(hdcMetafile);
+    ok(!ret, "BeginPath on metafile DC should fail\n");
+
+    ret = ExtTextOutA(hdcMetafile, 11, 22, 0, NULL, "Test", 4, dx);
+    ok(ret, "ExtTextOut error %ld\n", GetLastError());
+
+    ret = EndPath(hdcMetafile);
+    ok(!ret, "EndPath on metafile DC should fail\n");
+
+    hMetafile = CloseMetaFile(hdcMetafile);
+    ok(hMetafile != 0, "CloseMetaFile error %ld\n", GetLastError());
+
+    if (compare_mf_bits(hMetafile, MF_TEXTOUT_ON_PATH_BITS, sizeof(MF_TEXTOUT_ON_PATH_BITS),
+        "mf_TextOut_on_path") != 0)
+    {
+        dump_mf_bits(hMetafile, "mf_TextOut_on_path");
+        EnumMetaFile(0, hMetafile, mf_enum_proc, 0);
+    }
+
+    ret = DeleteMetaFile(hMetafile);
+    ok(ret, "DeleteMetaFile(%p) error %ld\n", hMetafile, GetLastError());
+}
+
+static void test_emf_ExtTextOut_on_path(void)
+{
+    HWND hwnd;
+    HDC hdcDisplay, hdcMetafile;
+    HENHMETAFILE hMetafile;
+    BOOL ret;
+    static const INT dx[4] = { 3, 5, 8, 12 };
+
+    /* Win9x doesn't play EMFs on invisible windows */
+    hwnd = CreateWindowExA(0, "static", NULL, WS_POPUP | WS_VISIBLE,
+                           0, 0, 200, 200, 0, 0, 0, NULL);
+    ok(hwnd != 0, "CreateWindowExA error %ld\n", GetLastError());
+
+    hdcDisplay = GetDC(hwnd);
+    ok(hdcDisplay != 0, "GetDC error %ld\n", GetLastError());
+
+    hdcMetafile = CreateEnhMetaFileA(hdcDisplay, NULL, NULL, NULL);
+    ok(hdcMetafile != 0, "CreateEnhMetaFileA error %ld\n", GetLastError());
+
+    ret = BeginPath(hdcMetafile);
+    ok(ret, "BeginPath error %ld\n", GetLastError());
+
+    ret = ExtTextOutA(hdcMetafile, 11, 22, 0, NULL, "Test", 4, dx);
+    ok(ret, "ExtTextOut error %ld\n", GetLastError());
+
+    ret = EndPath(hdcMetafile);
+    ok(ret, "EndPath error %ld\n", GetLastError());
+
+    hMetafile = CloseEnhMetaFile(hdcMetafile);
+    ok(hMetafile != 0, "CloseEnhMetaFile error %ld\n", GetLastError());
+
+    /* this doesn't succeed yet: EMF has correct size, all EMF records
+     * are there, but their contents don't match for different reasons.
+     */
+    if (compare_emf_bits(hMetafile, EMF_TEXTOUT_ON_PATH_BITS, sizeof(EMF_TEXTOUT_ON_PATH_BITS),
+        "emf_TextOut_on_path", TRUE) != 0)
+    {
+        dump_emf_bits(hMetafile, "emf_TextOut_on_path");
+        dump_emf_records(hMetafile, "emf_TextOut_on_path");
+    }
+
+    ret = DeleteEnhMetaFile(hMetafile);
+    ok(ret, "DeleteEnhMetaFile error %ld\n", GetLastError());
+    ret = ReleaseDC(hwnd, hdcDisplay);
+    ok(ret, "ReleaseDC error %ld\n", GetLastError());
+    DestroyWindow(hwnd);
+}
+
 static INT CALLBACK EmfEnumProc(HDC hdc, HANDLETABLE *lpHTable, const ENHMETARECORD *lpEMFR, INT nObj, LPARAM lpData)
 {
     LPMETAFILEPICT lpMFP = (LPMETAFILEPICT)lpData;
@@ -927,6 +1185,8 @@ START_TEST(metafile)
     test_mf_PatternBrush();
     test_CopyMetaFile();
     test_SetMetaFileBits();
+    test_mf_ExtTextOut_on_path();
+    test_emf_ExtTextOut_on_path();
 
     /* For metafile conversions */
     test_mf_conversions();






More information about the wine-patches mailing list