From e41ddd052ad996263bdefe892106dd053977725c Mon Sep 17 00:00:00 2001 From: Dmitry Timoshkov Date: Mon, 7 Nov 2005 16:40:20 +0000 Subject: [PATCH] First draft of ExtTextOut on an open path. --- dlls/gdi/enhmfdrv/graphics.c | 3 +- dlls/gdi/font.c | 35 +++-- dlls/gdi/gdi_private.h | 2 + dlls/gdi/path.c | 204 ++++++++++++++++++++++++++ dlls/gdi/tests/metafile.c | 270 ++++++++++++++++++++++++++++++++++- 5 files changed, 495 insertions(+), 19 deletions(-) diff --git a/dlls/gdi/enhmfdrv/graphics.c b/dlls/gdi/enhmfdrv/graphics.c index b2998ca3c13..ba6170b98ba 100644 --- a/dlls/gdi/enhmfdrv/graphics.c +++ b/dlls/gdi/enhmfdrv/graphics.c @@ -722,7 +722,8 @@ BOOL EMFDRV_ExtTextOut( PHYSDEV dev, INT x, INT y, UINT flags, 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 --git a/dlls/gdi/font.c b/dlls/gdi/font.c index 301a17ecd42..c01b1d9039a 100644 --- a/dlls/gdi/font.c +++ b/dlls/gdi/font.c @@ -1759,14 +1759,7 @@ BOOL WINAPI ExtTextOutW( HDC hdc, INT x, INT y, UINT flags, 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; @@ -1854,7 +1847,7 @@ BOOL WINAPI ExtTextOutW( HDC hdc, INT x, INT y, UINT flags, 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) @@ -1961,7 +1954,7 @@ BOOL WINAPI ExtTextOutW( HDC hdc, INT x, INT y, UINT flags, break; } - if(GetBkMode(hdc) != TRANSPARENT) + if (GetBkMode(hdc) != TRANSPARENT && !PATH_IsPathOpen(dc->path)) { if(!((flags & ETO_CLIPPED) && (flags & ETO_OPAQUE))) { @@ -2012,7 +2005,12 @@ BOOL WINAPI ExtTextOutW( HDC hdc, INT x, INT y, UINT flags, } 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; @@ -2023,7 +2021,13 @@ BOOL WINAPI ExtTextOutW( HDC hdc, INT x, INT y, UINT flags, 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); @@ -2040,7 +2044,12 @@ BOOL WINAPI ExtTextOutW( HDC hdc, INT x, INT y, UINT flags, 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 --git a/dlls/gdi/gdi_private.h b/dlls/gdi/gdi_private.h index 68790ede860..26df293d11b 100644 --- a/dlls/gdi/gdi_private.h +++ b/dlls/gdi/gdi_private.h @@ -405,6 +405,8 @@ extern BOOL PATH_AssignGdiPath(GdiPath *pPathDest, const GdiPath *pPathSrc); 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 --git a/dlls/gdi/path.c b/dlls/gdi/path.c index 216c7d05765..e7bfb0765cf 100644 --- a/dlls/gdi/path.c +++ b/dlls/gdi/path.c @@ -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 #include #include +#include #if defined(HAVE_FLOAT_H) #include #endif @@ -1212,6 +1214,208 @@ static BOOL PATH_PathToRegion(GdiPath *pPath, INT nPolyFillMode, 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 --git a/dlls/gdi/tests/metafile.c b/dlls/gdi/tests/metafile.c index 0b2aa665b53..1c024a85e01 100644 --- a/dlls/gdi/tests/metafile.c +++ b/dlls/gdi/tests/metafile.c @@ -422,6 +422,63 @@ static const unsigned char MF_PATTERN_BRUSH_BITS[] = { 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 name, const BYTE *bits, UINT bsize, const 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();