From 17a24679b491e43f8d85f0d13f95837496c6b382 Mon Sep 17 00:00:00 2001 From: Nikolay Sivov Date: Mon, 15 Dec 2014 09:39:26 +0300 Subject: [PATCH] dwrite: Initial GetGlyphRunOutline() implementation. --- dlls/dwrite/dwrite_private.h | 19 ++++ dlls/dwrite/font.c | 123 ++++++++++++++++++++++- dlls/dwrite/freetype.c | 156 +++++++++++++++++++++++++++++ dlls/dwrite/tests/font.c | 189 +++++++++++++++++++++++++++++++++++ 4 files changed, 482 insertions(+), 5 deletions(-) diff --git a/dlls/dwrite/dwrite_private.h b/dlls/dwrite/dwrite_private.h index 179dee70eac..07d894c5f4f 100644 --- a/dlls/dwrite/dwrite_private.h +++ b/dlls/dwrite/dwrite_private.h @@ -17,6 +17,7 @@ */ #include "dwrite_2.h" +#include "d2d1.h" #include "wine/debug.h" #include "wine/unicode.h" @@ -125,12 +126,30 @@ extern HRESULT opentype_get_font_strings_from_id(const void*,DWRITE_INFORMATIONA extern HRESULT bidi_computelevels(const WCHAR*,UINT32,UINT8,UINT8*,UINT8*) DECLSPEC_HIDDEN; extern WCHAR bidi_get_mirrored_char(WCHAR) DECLSPEC_HIDDEN; +enum outline_point_tag { + OUTLINE_POINT_START = 1 << 0, + OUTLINE_POINT_END = 1 << 1, + OUTLINE_POINT_BEZIER = 1 << 2, + OUTLINE_POINT_LINE = 1 << 3 +}; + +struct glyph_outline { + D2D1_POINT_2F *points; + UINT8 *tags; + UINT16 count; + FLOAT advance; +}; + +extern HRESULT new_glyph_outline(UINT32,struct glyph_outline**) DECLSPEC_HIDDEN; +extern void free_glyph_outline(struct glyph_outline*) DECLSPEC_HIDDEN; + /* FreeType integration */ extern BOOL init_freetype(void) DECLSPEC_HIDDEN; extern void release_freetype(void) DECLSPEC_HIDDEN; extern HRESULT freetype_get_design_glyph_metrics(IDWriteFontFace2*,UINT16,UINT16,DWRITE_GLYPH_METRICS*) DECLSPEC_HIDDEN; extern void freetype_notify_cacheremove(IDWriteFontFace2*) DECLSPEC_HIDDEN; extern BOOL freetype_is_monospaced(IDWriteFontFace2*) DECLSPEC_HIDDEN; +extern HRESULT freetype_get_glyph_outline(IDWriteFontFace2*,FLOAT,UINT16,struct glyph_outline**) DECLSPEC_HIDDEN; /* Glyph shaping */ enum SCRIPT_JUSTIFY diff --git a/dlls/dwrite/font.c b/dlls/dwrite/font.c index d5662d8c839..96110507c15 100644 --- a/dlls/dwrite/font.c +++ b/dlls/dwrite/font.c @@ -421,14 +421,127 @@ static void WINAPI dwritefontface_ReleaseFontTable(IDWriteFontFace2 *iface, void IDWriteFontFileStream_ReleaseFileFragment(This->streams[0], table_context); } +HRESULT new_glyph_outline(UINT32 count, struct glyph_outline **ret) +{ + struct glyph_outline *outline; + D2D1_POINT_2F *points; + UINT8 *tags; + + *ret = NULL; + + outline = heap_alloc(sizeof(*outline)); + if (!outline) + return E_OUTOFMEMORY; + + points = heap_alloc(count*sizeof(D2D1_POINT_2F)); + tags = heap_alloc(count*sizeof(UINT8)); + if (!points || !tags) { + heap_free(points); + heap_free(tags); + heap_free(outline); + return E_OUTOFMEMORY; + } + + outline->points = points; + outline->tags = tags; + outline->count = count; + outline->advance = 0.0; + + *ret = outline; + return S_OK; +} + +void free_glyph_outline(struct glyph_outline *outline) +{ + heap_free(outline->points); + heap_free(outline->tags); + heap_free(outline); +} + +static void report_glyph_outline(const struct glyph_outline *outline, IDWriteGeometrySink *sink) +{ + UINT16 p; + + for (p = 0; p < outline->count; p++) { + if (outline->tags[p] & OUTLINE_POINT_START) { + ID2D1SimplifiedGeometrySink_BeginFigure(sink, outline->points[p], D2D1_FIGURE_BEGIN_FILLED); + continue; + } + + if (outline->tags[p] & OUTLINE_POINT_LINE) + ID2D1SimplifiedGeometrySink_AddLines(sink, outline->points+p, 1); + else if (outline->tags[p] & OUTLINE_POINT_BEZIER) { + static const UINT16 segment_length = 3; + ID2D1SimplifiedGeometrySink_AddBeziers(sink, (D2D1_BEZIER_SEGMENT*)&outline->points[p], 1); + p += segment_length - 1; + } + + if (outline->tags[p] & OUTLINE_POINT_END) + ID2D1SimplifiedGeometrySink_EndFigure(sink, D2D1_FIGURE_END_CLOSED); + } +} + +static inline void translate_glyph_outline(struct glyph_outline *outline, FLOAT xoffset, FLOAT yoffset) +{ + UINT16 p; + + for (p = 0; p < outline->count; p++) { + outline->points[p].x += xoffset; + outline->points[p].y += yoffset; + } +} + static HRESULT WINAPI dwritefontface_GetGlyphRunOutline(IDWriteFontFace2 *iface, FLOAT emSize, - UINT16 const *glyph_indices, FLOAT const* glyph_advances, DWRITE_GLYPH_OFFSET const *glyph_offsets, - UINT32 glyph_count, BOOL is_sideways, BOOL is_rtl, IDWriteGeometrySink *geometrysink) + UINT16 const *glyphs, FLOAT const* advances, DWRITE_GLYPH_OFFSET const *offsets, + UINT32 count, BOOL is_sideways, BOOL is_rtl, IDWriteGeometrySink *sink) { struct dwrite_fontface *This = impl_from_IDWriteFontFace2(iface); - FIXME("(%p)->(%f %p %p %p %u %d %d %p): stub\n", This, emSize, glyph_indices, glyph_advances, glyph_offsets, - glyph_count, is_sideways, is_rtl, geometrysink); - return E_NOTIMPL; + FLOAT advance = 0.0; + HRESULT hr; + UINT32 g; + + TRACE("(%p)->(%.2f %p %p %p %u %d %d %p)\n", This, emSize, glyphs, advances, offsets, + count, is_sideways, is_rtl, sink); + + if (!glyphs || !sink) + return E_INVALIDARG; + + if (is_sideways) + FIXME("sideways mode is not supported.\n"); + + for (g = 0; g < count; g++) { + FLOAT xoffset = 0.0, yoffset = 0.0; + struct glyph_outline *outline; + + /* FIXME: cache outlines */ + + hr = freetype_get_glyph_outline(iface, emSize, glyphs[g], &outline); + if (FAILED(hr)) + return hr; + + /* glyph offsets act as current glyph adjustment */ + if (offsets) { + xoffset += is_rtl ? -offsets[g].advanceOffset : offsets[g].advanceOffset; + yoffset -= offsets[g].ascenderOffset; + } + + if (g == 0) + advance = is_rtl ? -outline->advance : 0.0; + + xoffset += advance; + translate_glyph_outline(outline, xoffset, yoffset); + + /* update advance to next glyph */ + if (advances) + advance += is_rtl ? -advances[g] : advances[g]; + else + advance += is_rtl ? -outline->advance : outline->advance; + + report_glyph_outline(outline, sink); + free_glyph_outline(outline); + } + + return S_OK; } static HRESULT WINAPI dwritefontface_GetRecommendedRenderingMode(IDWriteFontFace2 *iface, FLOAT emSize, diff --git a/dlls/dwrite/freetype.c b/dlls/dwrite/freetype.c index 7e8fd3cde2b..b4d985959f4 100644 --- a/dlls/dwrite/freetype.c +++ b/dlls/dwrite/freetype.c @@ -27,6 +27,7 @@ #include #include FT_CACHE_H #include FT_FREETYPE_H +#include FT_OUTLINE_H #endif /* HAVE_FT2BUILD_H */ #include "windef.h" @@ -65,6 +66,7 @@ MAKE_FUNCPTR(FT_Init_FreeType); MAKE_FUNCPTR(FT_Library_Version); MAKE_FUNCPTR(FT_Load_Glyph); MAKE_FUNCPTR(FT_New_Memory_Face); +MAKE_FUNCPTR(FT_Outline_Transform); MAKE_FUNCPTR(FTC_Manager_New); MAKE_FUNCPTR(FTC_Manager_Done); MAKE_FUNCPTR(FTC_Manager_LookupFace); @@ -134,6 +136,7 @@ BOOL init_freetype(void) LOAD_FUNCPTR(FT_Library_Version) LOAD_FUNCPTR(FT_Load_Glyph) LOAD_FUNCPTR(FT_New_Memory_Face) + LOAD_FUNCPTR(FT_Outline_Transform) LOAD_FUNCPTR(FTC_Manager_New) LOAD_FUNCPTR(FTC_Manager_Done) LOAD_FUNCPTR(FTC_Manager_LookupFace) @@ -225,6 +228,153 @@ BOOL freetype_is_monospaced(IDWriteFontFace2 *fontface) return is_monospaced; } +static inline void ft_vector_to_d2d_point(const FT_Vector *v, D2D1_POINT_2F *p) +{ + p->x = v->x / 64.0; + p->y = v->y / 64.0; +} + +static HRESULT get_outline_data(const FT_Outline *outline, struct glyph_outline **ret) +{ + short i, j, contour = 0; + D2D1_POINT_2F *points; + UINT16 count = 0; + UINT8 *tags; + HRESULT hr; + + /* we need all curves in cubic format */ + for (i = 0; i < outline->n_points; i++) { + /* control point */ + if (!(outline->tags[i] & FT_Curve_Tag_On)) { + if (!(outline->tags[i] & FT_Curve_Tag_Cubic)) { + count++; + } + } + count++; + } + + hr = new_glyph_outline(count, ret); + if (FAILED(hr)) + return hr; + + points = (*ret)->points; + tags = (*ret)->tags; + + ft_vector_to_d2d_point(outline->points, points); + tags[0] = OUTLINE_POINT_START; + + for (i = 1, j = 1; i < outline->n_points; i++, j++) { + /* mark start of new contour */ + if (tags[j-1] & OUTLINE_POINT_END) + tags[j] = OUTLINE_POINT_START; + else + tags[j] = 0; + + if (outline->tags[i] & FT_Curve_Tag_On) { + ft_vector_to_d2d_point(outline->points+i, points+j); + tags[j] |= OUTLINE_POINT_LINE; + } + else { + /* third order curve */ + if (outline->tags[i] & FT_Curve_Tag_Cubic) { + /* store 3 points, advance 3 points */ + + ft_vector_to_d2d_point(outline->points+i, points+j); + ft_vector_to_d2d_point(outline->points+i+1, points+j+1); + ft_vector_to_d2d_point(outline->points+i+2, points+j+2); + + i += 2; + } + else { + FT_Vector vec; + + /* Convert the quadratic Beziers to cubic Beziers. + The parametric eqn for a cubic Bezier is, from PLRM: + r(t) = at^3 + bt^2 + ct + r0 + with the control points: + r1 = r0 + c/3 + r2 = r1 + (c + b)/3 + r3 = r0 + c + b + a + + A quadratic Bezier has the form: + p(t) = (1-t)^2 p0 + 2(1-t)t p1 + t^2 p2 + + So equating powers of t leads to: + r1 = 2/3 p1 + 1/3 p0 + r2 = 2/3 p1 + 1/3 p2 + and of course r0 = p0, r3 = p2 + */ + + /* r1 */ + vec.x = 2 * outline->points[i].x + outline->points[i-1].x; + vec.y = 2 * outline->points[i].y + outline->points[i-1].y; + ft_vector_to_d2d_point(&vec, points+j); + points[j].x /= 3.0; + points[j].y /= 3.0; + + /* r2 */ + vec.x = 2 * outline->points[i].x + outline->points[i+1].x; + vec.y = 2 * outline->points[i].y + outline->points[i+1].y; + ft_vector_to_d2d_point(&vec, points+j+1); + points[j+1].x /= 3.0; + points[j+1].y /= 3.0; + + /* r3 */ + ft_vector_to_d2d_point(outline->points+i+1, points+j+2); + + i++; + } + + tags[j] = tags[j+1] = tags[j+2] = OUTLINE_POINT_BEZIER; + j += 2; + } + + /* mark end point */ + if (i < outline->n_points && outline->contours[contour] == i) { + tags[j] |= OUTLINE_POINT_END; + contour++; + } + } + + return S_OK; +} + +HRESULT freetype_get_glyph_outline(IDWriteFontFace2 *fontface, FLOAT emSize, UINT16 index, struct glyph_outline **ret) +{ + FTC_ScalerRec scaler; + HRESULT hr = S_OK; + FT_Size size; + + scaler.face_id = fontface; + scaler.width = emSize; + scaler.height = emSize; + scaler.pixel = 1; + scaler.x_res = 0; + scaler.y_res = 0; + + EnterCriticalSection(&freetype_cs); + if (pFTC_Manager_LookupSize(cache_manager, &scaler, &size) == 0) { + if (pFT_Load_Glyph(size->face, index, FT_LOAD_DEFAULT) == 0) { + FT_Outline *outline = &size->face->glyph->outline; + FT_Matrix m; + + m.xx = 1.0 * 0x10000; + m.xy = 0; + m.yx = 0; + m.yy = -1.0 * 0x10000; /* flip Y axis */ + + pFT_Outline_Transform(outline, &m); + + hr = get_outline_data(outline, ret); + if (hr == S_OK) + (*ret)->advance = size->face->glyph->metrics.horiAdvance >> 6; + } + } + LeaveCriticalSection(&freetype_cs); + + return hr; +} + #else /* HAVE_FREETYPE */ BOOL init_freetype(void) @@ -250,4 +400,10 @@ BOOL freetype_is_monospaced(IDWriteFontFace2 *fontface) return FALSE; } +HRESULT freetype_get_glyph_outline(IDWriteFontFace2 *fontface, FLOAT emSize, UINT16 index, struct glyph_outline **ret) +{ + *ret = NULL; + return E_NOTIMPL; +} + #endif /* HAVE_FREETYPE */ diff --git a/dlls/dwrite/tests/font.c b/dlls/dwrite/tests/font.c index 272b41426aa..f63ab1dd873 100644 --- a/dlls/dwrite/tests/font.c +++ b/dlls/dwrite/tests/font.c @@ -23,6 +23,8 @@ #include "windows.h" #include "dwrite_1.h" +#include "initguid.h" +#include "d2d1.h" #include "wine/test.h" @@ -378,6 +380,86 @@ static const struct IDWriteFontFileLoaderVtbl resourcefontfileloadervtbl = { static IDWriteFontFileLoader rloader = { &resourcefontfileloadervtbl }; +static D2D1_POINT_2F g_startpoints[2]; +static int g_startpoint_count; + +static HRESULT WINAPI test_geometrysink_QueryInterface(ID2D1SimplifiedGeometrySink *iface, REFIID riid, void **ret) +{ + if (IsEqualIID(riid, &IID_ID2D1SimplifiedGeometrySink) || + IsEqualIID(riid, &IID_IUnknown)) + { + *ret = iface; + ID2D1SimplifiedGeometrySink_AddRef(iface); + return S_OK; + } + + *ret = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI test_geometrysink_AddRef(ID2D1SimplifiedGeometrySink *iface) +{ + return 2; +} + +static ULONG WINAPI test_geometrysink_Release(ID2D1SimplifiedGeometrySink *iface) +{ + return 1; +} + +static void WINAPI test_geometrysink_SetFillMode(ID2D1SimplifiedGeometrySink *iface, D2D1_FILL_MODE mode) +{ + ok(mode == D2D1_FILL_MODE_WINDING, "fill mode %d\n", mode); +} + +static void WINAPI test_geometrysink_SetSegmentFlags(ID2D1SimplifiedGeometrySink *iface, D2D1_PATH_SEGMENT flags) +{ + ok(0, "unexpected SetSegmentFlags() - flags %d\n", flags); +} + +static void WINAPI test_geometrysink_BeginFigure(ID2D1SimplifiedGeometrySink *iface, + D2D1_POINT_2F startPoint, D2D1_FIGURE_BEGIN figureBegin) +{ + ok(figureBegin == D2D1_FIGURE_BEGIN_FILLED, "begin figure %d\n", figureBegin); + g_startpoints[g_startpoint_count++] = startPoint; +} + +static void WINAPI test_geometrysink_AddLines(ID2D1SimplifiedGeometrySink *iface, + const D2D1_POINT_2F *points, UINT32 count) +{ +} + +static void WINAPI test_geometrysink_AddBeziers(ID2D1SimplifiedGeometrySink *iface, + const D2D1_BEZIER_SEGMENT *beziers, UINT32 count) +{ +} + +static void WINAPI test_geometrysink_EndFigure(ID2D1SimplifiedGeometrySink *iface, D2D1_FIGURE_END figureEnd) +{ + ok(figureEnd == D2D1_FIGURE_END_CLOSED, "end figure %d\n", figureEnd); +} + +static HRESULT WINAPI test_geometrysink_Close(ID2D1SimplifiedGeometrySink *iface) +{ + ok(0, "unexpected Close()\n"); + return E_NOTIMPL; +} + +static const ID2D1SimplifiedGeometrySinkVtbl test_geometrysink_vtbl = { + test_geometrysink_QueryInterface, + test_geometrysink_AddRef, + test_geometrysink_Release, + test_geometrysink_SetFillMode, + test_geometrysink_SetSegmentFlags, + test_geometrysink_BeginFigure, + test_geometrysink_AddLines, + test_geometrysink_AddBeziers, + test_geometrysink_EndFigure, + test_geometrysink_Close +}; + +static ID2D1SimplifiedGeometrySink test_geomsink = { &test_geometrysink_vtbl }; + static void test_CreateFontFromLOGFONT(void) { static const WCHAR tahomaspW[] = {'T','a','h','o','m','a',' ',0}; @@ -2740,6 +2822,112 @@ static void test_GetDesignGlyphAdvances(void) DeleteFileW(test_fontfile); } +static void test_GetGlyphRunOutline(void) +{ + DWRITE_GLYPH_OFFSET offsets[2]; + IDWriteFactory *factory; + IDWriteFontFile *file; + IDWriteFontFace *face; + UINT32 codepoint; + FLOAT advances[2]; + UINT16 glyphs[2]; + HRESULT hr; + + create_testfontfile(test_fontfile); + factory = create_factory(); + + hr = IDWriteFactory_CreateFontFileReference(factory, test_fontfile, NULL, &file); + ok(hr == S_OK, "got 0x%08x\n",hr); + + hr = IDWriteFactory_CreateFontFace(factory, DWRITE_FONT_FACE_TYPE_TRUETYPE, 1, &file, 0, DWRITE_FONT_SIMULATIONS_NONE, &face); + ok(hr == S_OK, "got 0x%08x\n", hr); + IDWriteFontFile_Release(file); + + codepoint = 'A'; + glyphs[0] = 0; + hr = IDWriteFontFace_GetGlyphIndices(face, &codepoint, 1, glyphs); + ok(hr == S_OK, "got 0x%08x\n", hr); + ok(glyphs[0] > 0, "got %u\n", glyphs[0]); + glyphs[1] = glyphs[0]; + + hr = IDWriteFontFace_GetGlyphRunOutline(face, 2048.0, glyphs, advances, offsets, 1, FALSE, FALSE, NULL); + ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); + + hr = IDWriteFontFace_GetGlyphRunOutline(face, 2048.0, NULL, NULL, offsets, 1, FALSE, FALSE, &test_geomsink); + ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); + + advances[0] = 1.0; + advances[1] = 0.0; + + offsets[0].advanceOffset = 1.0; + offsets[0].ascenderOffset = 1.0; + offsets[1].advanceOffset = 0.0; + offsets[1].ascenderOffset = 0.0; + + /* default advances, no offsets */ + memset(g_startpoints, 0, sizeof(g_startpoints)); + g_startpoint_count = 0; + hr = IDWriteFontFace_GetGlyphRunOutline(face, 1024.0, glyphs, NULL, NULL, 2, FALSE, FALSE, &test_geomsink); + ok(hr == S_OK, "got 0x%08x\n", hr); + ok(g_startpoint_count == 2, "got %d\n", g_startpoint_count); + if (g_startpoint_count == 2) { + /* glyph advance of 500 is applied */ + ok(g_startpoints[0].x == 229.5 && g_startpoints[0].y == -629.0, "0: got (%.2f,%.2f)\n", g_startpoints[0].x, g_startpoints[0].y); + ok(g_startpoints[1].x == 729.5 && g_startpoints[1].y == -629.0, "1: got (%.2f,%.2f)\n", g_startpoints[1].x, g_startpoints[1].y); + } + + /* default advances, no offsets, RTL */ + memset(g_startpoints, 0, sizeof(g_startpoints)); + g_startpoint_count = 0; + hr = IDWriteFontFace_GetGlyphRunOutline(face, 1024.0, glyphs, NULL, NULL, 2, FALSE, TRUE, &test_geomsink); + ok(hr == S_OK, "got 0x%08x\n", hr); + ok(g_startpoint_count == 2, "got %d\n", g_startpoint_count); + if (g_startpoint_count == 2) { + /* advance is -500 now */ + ok(g_startpoints[0].x == -270.5 && g_startpoints[0].y == -629.0, "0: got (%.2f,%.2f)\n", g_startpoints[0].x, g_startpoints[0].y); + ok(g_startpoints[1].x == -770.5 && g_startpoints[1].y == -629.0, "1: got (%.2f,%.2f)\n", g_startpoints[1].x, g_startpoints[1].y); + } + + /* default advances, additional offsets */ + memset(g_startpoints, 0, sizeof(g_startpoints)); + g_startpoint_count = 0; + hr = IDWriteFontFace_GetGlyphRunOutline(face, 1024.0, glyphs, NULL, offsets, 2, FALSE, FALSE, &test_geomsink); + ok(hr == S_OK, "got 0x%08x\n", hr); + ok(g_startpoint_count == 2, "got %d\n", g_startpoint_count); + if (g_startpoint_count == 2) { + /* offsets applied to first contour */ + ok(g_startpoints[0].x == 230.5 && g_startpoints[0].y == -630.0, "0: got (%.2f,%.2f)\n", g_startpoints[0].x, g_startpoints[0].y); + ok(g_startpoints[1].x == 729.5 && g_startpoints[1].y == -629.0, "1: got (%.2f,%.2f)\n", g_startpoints[1].x, g_startpoints[1].y); + } + + /* default advances, additional offsets, RTL */ + memset(g_startpoints, 0, sizeof(g_startpoints)); + g_startpoint_count = 0; + hr = IDWriteFontFace_GetGlyphRunOutline(face, 1024.0, glyphs, NULL, offsets, 2, FALSE, TRUE, &test_geomsink); + ok(hr == S_OK, "got 0x%08x\n", hr); + ok(g_startpoint_count == 2, "got %d\n", g_startpoint_count); + if (g_startpoint_count == 2) { + ok(g_startpoints[0].x == -271.5 && g_startpoints[0].y == -630.0, "0: got (%.2f,%.2f)\n", g_startpoints[0].x, g_startpoints[0].y); + ok(g_startpoints[1].x == -770.5 && g_startpoints[1].y == -629.0, "1: got (%.2f,%.2f)\n", g_startpoints[1].x, g_startpoints[1].y); + } + + /* custom advances and offsets, offset turns total advance value to zero */ + memset(g_startpoints, 0, sizeof(g_startpoints)); + g_startpoint_count = 0; + hr = IDWriteFontFace_GetGlyphRunOutline(face, 1024.0, glyphs, advances, offsets, 2, FALSE, FALSE, &test_geomsink); + ok(hr == S_OK, "got 0x%08x\n", hr); + ok(g_startpoint_count == 2, "got %d\n", g_startpoint_count); + if (g_startpoint_count == 2) { + ok(g_startpoints[0].x == 230.5 && g_startpoints[0].y == -630.0, "0: got (%.2f,%.2f)\n", g_startpoints[0].x, g_startpoints[0].y); + ok(g_startpoints[1].x == 230.5 && g_startpoints[1].y == -629.0, "1: got (%.2f,%.2f)\n", g_startpoints[1].x, g_startpoints[1].y); + } + + IDWriteFontFace_Release(face); + IDWriteFactory_Release(factory); + + DeleteFileW(test_fontfile); +} + START_TEST(font) { IDWriteFactory *factory; @@ -2776,6 +2964,7 @@ START_TEST(font) test_GetDesignGlyphMetrics(); test_GetDesignGlyphAdvances(); test_IsMonospacedFont(); + test_GetGlyphRunOutline(); IDWriteFactory_Release(factory); }