| /* |
| * Text layout/format tests |
| * |
| * Copyright 2012, 2014-2017 Nikolay Sivov 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 |
| */ |
| |
| #define COBJMACROS |
| |
| #include <assert.h> |
| #include <math.h> |
| #include <limits.h> |
| |
| #include "windows.h" |
| #include "dwrite_3.h" |
| |
| #include "wine/test.h" |
| |
| static const WCHAR tahomaW[] = {'T','a','h','o','m','a',0}; |
| static const WCHAR enusW[] = {'e','n','-','u','s',0}; |
| |
| struct testanalysissink |
| { |
| IDWriteTextAnalysisSink IDWriteTextAnalysisSink_iface; |
| DWRITE_SCRIPT_ANALYSIS sa; /* last analysis, with SetScriptAnalysis() */ |
| }; |
| |
| static inline struct testanalysissink *impl_from_IDWriteTextAnalysisSink(IDWriteTextAnalysisSink *iface) |
| { |
| return CONTAINING_RECORD(iface, struct testanalysissink, IDWriteTextAnalysisSink_iface); |
| } |
| |
| /* test IDWriteTextAnalysisSink */ |
| static HRESULT WINAPI analysissink_QueryInterface(IDWriteTextAnalysisSink *iface, REFIID riid, void **obj) |
| { |
| if (IsEqualIID(riid, &IID_IDWriteTextAnalysisSink) || IsEqualIID(riid, &IID_IUnknown)) |
| { |
| *obj = iface; |
| return S_OK; |
| } |
| |
| *obj = NULL; |
| return E_NOINTERFACE; |
| } |
| |
| static ULONG WINAPI analysissink_AddRef(IDWriteTextAnalysisSink *iface) |
| { |
| return 2; |
| } |
| |
| static ULONG WINAPI analysissink_Release(IDWriteTextAnalysisSink *iface) |
| { |
| return 1; |
| } |
| |
| static HRESULT WINAPI analysissink_SetScriptAnalysis(IDWriteTextAnalysisSink *iface, |
| UINT32 position, UINT32 length, DWRITE_SCRIPT_ANALYSIS const* sa) |
| { |
| struct testanalysissink *sink = impl_from_IDWriteTextAnalysisSink(iface); |
| sink->sa = *sa; |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI analysissink_SetLineBreakpoints(IDWriteTextAnalysisSink *iface, |
| UINT32 position, UINT32 length, DWRITE_LINE_BREAKPOINT const* breakpoints) |
| { |
| ok(0, "unexpected call\n"); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI analysissink_SetBidiLevel(IDWriteTextAnalysisSink *iface, |
| UINT32 position, UINT32 length, UINT8 explicitLevel, UINT8 resolvedLevel) |
| { |
| ok(0, "unexpected\n"); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI analysissink_SetNumberSubstitution(IDWriteTextAnalysisSink *iface, |
| UINT32 position, UINT32 length, IDWriteNumberSubstitution* substitution) |
| { |
| ok(0, "unexpected\n"); |
| return E_NOTIMPL; |
| } |
| |
| static IDWriteTextAnalysisSinkVtbl analysissinkvtbl = { |
| analysissink_QueryInterface, |
| analysissink_AddRef, |
| analysissink_Release, |
| analysissink_SetScriptAnalysis, |
| analysissink_SetLineBreakpoints, |
| analysissink_SetBidiLevel, |
| analysissink_SetNumberSubstitution |
| }; |
| |
| static struct testanalysissink analysissink = { |
| { &analysissinkvtbl }, |
| { 0 } |
| }; |
| |
| /* test IDWriteTextAnalysisSource */ |
| static HRESULT WINAPI analysissource_QueryInterface(IDWriteTextAnalysisSource *iface, |
| REFIID riid, void **obj) |
| { |
| if (IsEqualIID(riid, &IID_IDWriteTextAnalysisSource) || IsEqualIID(riid, &IID_IUnknown)) { |
| *obj = iface; |
| IDWriteTextAnalysisSource_AddRef(iface); |
| return S_OK; |
| } |
| return E_NOINTERFACE; |
| } |
| |
| static ULONG WINAPI analysissource_AddRef(IDWriteTextAnalysisSource *iface) |
| { |
| return 2; |
| } |
| |
| static ULONG WINAPI analysissource_Release(IDWriteTextAnalysisSource *iface) |
| { |
| return 1; |
| } |
| |
| static const WCHAR *g_source; |
| |
| static HRESULT WINAPI analysissource_GetTextAtPosition(IDWriteTextAnalysisSource *iface, |
| UINT32 position, WCHAR const** text, UINT32* text_len) |
| { |
| if (position >= lstrlenW(g_source)) |
| { |
| *text = NULL; |
| *text_len = 0; |
| } |
| else |
| { |
| *text = &g_source[position]; |
| *text_len = lstrlenW(g_source) - position; |
| } |
| |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI analysissource_GetTextBeforePosition(IDWriteTextAnalysisSource *iface, |
| UINT32 position, WCHAR const** text, UINT32* text_len) |
| { |
| ok(0, "unexpected\n"); |
| return E_NOTIMPL; |
| } |
| |
| static DWRITE_READING_DIRECTION WINAPI analysissource_GetParagraphReadingDirection( |
| IDWriteTextAnalysisSource *iface) |
| { |
| ok(0, "unexpected\n"); |
| return DWRITE_READING_DIRECTION_RIGHT_TO_LEFT; |
| } |
| |
| static HRESULT WINAPI analysissource_GetLocaleName(IDWriteTextAnalysisSource *iface, |
| UINT32 position, UINT32* text_len, WCHAR const** locale) |
| { |
| *locale = NULL; |
| *text_len = 0; |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI analysissource_GetNumberSubstitution(IDWriteTextAnalysisSource *iface, |
| UINT32 position, UINT32* text_len, IDWriteNumberSubstitution **substitution) |
| { |
| ok(0, "unexpected\n"); |
| return E_NOTIMPL; |
| } |
| |
| static IDWriteTextAnalysisSourceVtbl analysissourcevtbl = { |
| analysissource_QueryInterface, |
| analysissource_AddRef, |
| analysissource_Release, |
| analysissource_GetTextAtPosition, |
| analysissource_GetTextBeforePosition, |
| analysissource_GetParagraphReadingDirection, |
| analysissource_GetLocaleName, |
| analysissource_GetNumberSubstitution |
| }; |
| |
| static IDWriteTextAnalysisSource analysissource = { &analysissourcevtbl }; |
| |
| static IDWriteFactory *create_factory(void) |
| { |
| IDWriteFactory *factory; |
| HRESULT hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_ISOLATED, &IID_IDWriteFactory, (IUnknown**)&factory); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| return factory; |
| } |
| |
| /* obvious limitation is that only last script data is returned, so this |
| helper is suitable for single script strings only */ |
| static void get_script_analysis(const WCHAR *str, UINT32 len, DWRITE_SCRIPT_ANALYSIS *sa) |
| { |
| IDWriteTextAnalyzer *analyzer; |
| IDWriteFactory *factory; |
| HRESULT hr; |
| |
| g_source = str; |
| |
| factory = create_factory(); |
| hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextAnalyzer_AnalyzeScript(analyzer, &analysissource, 0, len, &analysissink.IDWriteTextAnalysisSink_iface); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| *sa = analysissink.sa; |
| IDWriteFactory_Release(factory); |
| } |
| |
| static IDWriteFontFace *get_fontface_from_format(IDWriteTextFormat *format) |
| { |
| IDWriteFontCollection *collection; |
| IDWriteFontFamily *family; |
| IDWriteFontFace *fontface; |
| IDWriteFont *font; |
| WCHAR nameW[255]; |
| UINT32 index; |
| BOOL exists; |
| HRESULT hr; |
| |
| hr = IDWriteTextFormat_GetFontCollection(format, &collection); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextFormat_GetFontFamilyName(format, nameW, sizeof(nameW)/sizeof(WCHAR)); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFontCollection_FindFamilyName(collection, nameW, &index, &exists); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFontCollection_GetFontFamily(collection, index, &family); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| IDWriteFontCollection_Release(collection); |
| |
| hr = IDWriteFontFamily_GetFirstMatchingFont(family, |
| IDWriteTextFormat_GetFontWeight(format), |
| IDWriteTextFormat_GetFontStretch(format), |
| IDWriteTextFormat_GetFontStyle(format), |
| &font); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFont_CreateFontFace(font, &fontface); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| IDWriteFont_Release(font); |
| IDWriteFontFamily_Release(family); |
| |
| return fontface; |
| } |
| |
| #define EXPECT_REF(obj,ref) _expect_ref((IUnknown*)obj, ref, __LINE__) |
| static void _expect_ref(IUnknown* obj, ULONG ref, int line) |
| { |
| ULONG rc; |
| IUnknown_AddRef(obj); |
| rc = IUnknown_Release(obj); |
| ok_(__FILE__,line)(rc == ref, "expected refcount %d, got %d\n", ref, rc); |
| } |
| |
| enum drawcall_modifiers_kind { |
| DRAW_EFFECT = 0x1000 |
| }; |
| |
| enum drawcall_kind { |
| DRAW_GLYPHRUN = 0, |
| DRAW_UNDERLINE = 1, |
| DRAW_STRIKETHROUGH = 2, |
| DRAW_INLINE = 3, |
| DRAW_LAST_KIND = 4, |
| DRAW_TOTAL_KINDS = 5, |
| DRAW_KINDS_MASK = 0xff |
| }; |
| |
| static const char *get_draw_kind_name(unsigned short kind) |
| { |
| static const char *kind_names[] = { |
| "GLYPH_RUN", |
| "UNDERLINE", |
| "STRIKETHROUGH", |
| "INLINE", |
| "END_OF_SEQ", |
| |
| "GLYPH_RUN|EFFECT", |
| "UNDERLINE|EFFECT", |
| "STRIKETHROUGH|EFFECT", |
| "INLINE|EFFECT", |
| "END_OF_SEQ" |
| }; |
| if ((kind & DRAW_KINDS_MASK) > DRAW_LAST_KIND) |
| return "unknown"; |
| return (kind & DRAW_EFFECT) ? kind_names[(kind & DRAW_KINDS_MASK) + DRAW_TOTAL_KINDS] : |
| kind_names[kind]; |
| } |
| |
| struct drawcall_entry { |
| enum drawcall_kind kind; |
| WCHAR string[10]; /* only meaningful for DrawGlyphRun() */ |
| WCHAR locale[LOCALE_NAME_MAX_LENGTH]; |
| UINT32 glyphcount; /* only meaningful for DrawGlyphRun() */ |
| }; |
| |
| struct drawcall_sequence |
| { |
| int count; |
| int size; |
| struct drawcall_entry *sequence; |
| }; |
| |
| struct drawtestcontext { |
| unsigned short kind; |
| BOOL todo; |
| int *failcount; |
| const char *file; |
| int line; |
| }; |
| |
| #define NUM_CALL_SEQUENCES 1 |
| #define RENDERER_ID 0 |
| static struct drawcall_sequence *sequences[NUM_CALL_SEQUENCES]; |
| static struct drawcall_sequence *expected_seq[1]; |
| |
| static void add_call(struct drawcall_sequence **seq, int sequence_index, const struct drawcall_entry *call) |
| { |
| struct drawcall_sequence *call_seq = seq[sequence_index]; |
| |
| if (!call_seq->sequence) { |
| call_seq->size = 10; |
| call_seq->sequence = HeapAlloc(GetProcessHeap(), 0, call_seq->size * sizeof (struct drawcall_entry)); |
| } |
| |
| if (call_seq->count == call_seq->size) { |
| call_seq->size *= 2; |
| call_seq->sequence = HeapReAlloc(GetProcessHeap(), 0, |
| call_seq->sequence, |
| call_seq->size * sizeof (struct drawcall_entry)); |
| } |
| |
| assert(call_seq->sequence); |
| call_seq->sequence[call_seq->count++] = *call; |
| } |
| |
| static inline void flush_sequence(struct drawcall_sequence **seg, int sequence_index) |
| { |
| struct drawcall_sequence *call_seq = seg[sequence_index]; |
| |
| HeapFree(GetProcessHeap(), 0, call_seq->sequence); |
| call_seq->sequence = NULL; |
| call_seq->count = call_seq->size = 0; |
| } |
| |
| static void init_call_sequences(struct drawcall_sequence **seq, int n) |
| { |
| int i; |
| |
| for (i = 0; i < n; i++) |
| seq[i] = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct drawcall_sequence)); |
| } |
| |
| static void ok_sequence_(struct drawcall_sequence **seq, int sequence_index, |
| const struct drawcall_entry *expected, const char *context, BOOL todo, |
| const char *file, int line) |
| { |
| static const struct drawcall_entry end_of_sequence = { DRAW_LAST_KIND }; |
| struct drawcall_sequence *call_seq = seq[sequence_index]; |
| const struct drawcall_entry *actual, *sequence; |
| int failcount = 0; |
| |
| add_call(seq, sequence_index, &end_of_sequence); |
| |
| sequence = call_seq->sequence; |
| actual = sequence; |
| |
| while (expected->kind != DRAW_LAST_KIND && actual->kind != DRAW_LAST_KIND) { |
| if (expected->kind != actual->kind) { |
| if (todo) { |
| failcount++; |
| todo_wine |
| ok_(file, line) (0, "%s: call %s was expected, but got call %s instead\n", |
| context, get_draw_kind_name(expected->kind), get_draw_kind_name(actual->kind)); |
| |
| flush_sequence(seq, sequence_index); |
| return; |
| } |
| else |
| ok_(file, line) (0, "%s: call %s was expected, but got call %s instead\n", |
| context, get_draw_kind_name(expected->kind), get_draw_kind_name(actual->kind)); |
| } |
| else if ((expected->kind & DRAW_KINDS_MASK) == DRAW_GLYPHRUN) { |
| int cmp = lstrcmpW(expected->string, actual->string); |
| if (cmp != 0 && todo) { |
| failcount++; |
| todo_wine |
| ok_(file, line) (0, "%s: glyphrun string %s was expected, but got %s instead\n", |
| context, wine_dbgstr_w(expected->string), wine_dbgstr_w(actual->string)); |
| } |
| else |
| ok_(file, line) (cmp == 0, "%s: glyphrun string %s was expected, but got %s instead\n", |
| context, wine_dbgstr_w(expected->string), wine_dbgstr_w(actual->string)); |
| |
| cmp = lstrcmpW(expected->locale, actual->locale); |
| if (cmp != 0 && todo) { |
| failcount++; |
| todo_wine |
| ok_(file, line) (0, "%s: glyph run locale %s was expected, but got %s instead\n", |
| context, wine_dbgstr_w(expected->locale), wine_dbgstr_w(actual->locale)); |
| } |
| else |
| ok_(file, line) (cmp == 0, "%s: glyph run locale %s was expected, but got %s instead\n", |
| context, wine_dbgstr_w(expected->locale), wine_dbgstr_w(actual->locale)); |
| |
| if (expected->glyphcount != actual->glyphcount && todo) { |
| failcount++; |
| todo_wine |
| ok_(file, line) (0, "%s: wrong glyph count, %u was expected, but got %u instead\n", |
| context, expected->glyphcount, actual->glyphcount); |
| } |
| else |
| ok_(file, line) (expected->glyphcount == actual->glyphcount, |
| "%s: wrong glyph count, %u was expected, but got %u instead\n", |
| context, expected->glyphcount, actual->glyphcount); |
| } |
| else if ((expected->kind & DRAW_KINDS_MASK) == DRAW_UNDERLINE) { |
| int cmp = lstrcmpW(expected->locale, actual->locale); |
| if (cmp != 0 && todo) { |
| failcount++; |
| todo_wine |
| ok_(file, line) (0, "%s: underline locale %s was expected, but got %s instead\n", |
| context, wine_dbgstr_w(expected->locale), wine_dbgstr_w(actual->locale)); |
| } |
| else |
| ok_(file, line) (cmp == 0, "%s: underline locale %s was expected, but got %s instead\n", |
| context, wine_dbgstr_w(expected->locale), wine_dbgstr_w(actual->locale)); |
| } |
| expected++; |
| actual++; |
| } |
| |
| if (todo) { |
| todo_wine { |
| if (expected->kind != DRAW_LAST_KIND || actual->kind != DRAW_LAST_KIND) { |
| failcount++; |
| ok_(file, line) (0, "%s: the call sequence is not complete: expected %s - actual %s\n", |
| context, get_draw_kind_name(expected->kind), get_draw_kind_name(actual->kind)); |
| } |
| } |
| } |
| else if (expected->kind != DRAW_LAST_KIND || actual->kind != DRAW_LAST_KIND) |
| ok_(file, line) (0, "%s: the call sequence is not complete: expected %s - actual %s\n", |
| context, get_draw_kind_name(expected->kind), get_draw_kind_name(actual->kind)); |
| |
| if (todo && !failcount) /* succeeded yet marked todo */ |
| todo_wine |
| ok_(file, line)(1, "%s: marked \"todo_wine\" but succeeds\n", context); |
| |
| flush_sequence(seq, sequence_index); |
| } |
| |
| #define ok_sequence(seq, index, exp, contx, todo) \ |
| ok_sequence_(seq, index, (exp), (contx), (todo), __FILE__, __LINE__) |
| |
| static HRESULT WINAPI testrenderer_QI(IDWriteTextRenderer *iface, REFIID riid, void **obj) |
| { |
| if (IsEqualIID(riid, &IID_IDWriteTextRenderer) || |
| IsEqualIID(riid, &IID_IDWritePixelSnapping) || |
| IsEqualIID(riid, &IID_IUnknown) |
| ) { |
| *obj = iface; |
| return S_OK; |
| } |
| |
| *obj = NULL; |
| |
| /* IDWriteTextRenderer1 overrides drawing calls, ignore for now */ |
| if (IsEqualIID(riid, &IID_IDWriteTextRenderer1)) |
| return E_NOINTERFACE; |
| |
| ok(0, "unexpected QI %s\n", wine_dbgstr_guid(riid)); |
| return E_NOINTERFACE; |
| } |
| |
| static ULONG WINAPI testrenderer_AddRef(IDWriteTextRenderer *iface) |
| { |
| return 2; |
| } |
| |
| static ULONG WINAPI testrenderer_Release(IDWriteTextRenderer *iface) |
| { |
| return 1; |
| } |
| |
| struct renderer_context { |
| BOOL gdicompat; |
| BOOL use_gdi_natural; |
| BOOL snapping_disabled; |
| DWRITE_MATRIX m; |
| FLOAT ppdip; |
| FLOAT originX; |
| FLOAT originY; |
| IDWriteTextFormat *format; |
| const WCHAR *familyW; |
| }; |
| |
| static HRESULT WINAPI testrenderer_IsPixelSnappingDisabled(IDWriteTextRenderer *iface, |
| void *context, BOOL *disabled) |
| { |
| struct renderer_context *ctxt = (struct renderer_context*)context; |
| if (ctxt) |
| *disabled = ctxt->snapping_disabled; |
| else |
| *disabled = TRUE; |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI testrenderer_GetCurrentTransform(IDWriteTextRenderer *iface, |
| void *context, DWRITE_MATRIX *m) |
| { |
| struct renderer_context *ctxt = (struct renderer_context*)context; |
| ok(!ctxt->snapping_disabled, "expected enabled snapping\n"); |
| *m = ctxt->m; |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI testrenderer_GetPixelsPerDip(IDWriteTextRenderer *iface, |
| void *context, FLOAT *pixels_per_dip) |
| { |
| struct renderer_context *ctxt = (struct renderer_context*)context; |
| *pixels_per_dip = ctxt->ppdip; |
| return S_OK; |
| } |
| |
| #define TEST_MEASURING_MODE(ctxt, mode) test_measuring_mode(ctxt, mode, __LINE__) |
| static void test_measuring_mode(const struct renderer_context *ctxt, DWRITE_MEASURING_MODE mode, int line) |
| { |
| if (ctxt->gdicompat) { |
| if (ctxt->use_gdi_natural) |
| ok_(__FILE__, line)(mode == DWRITE_MEASURING_MODE_GDI_NATURAL, "got %d\n", mode); |
| else |
| ok_(__FILE__, line)(mode == DWRITE_MEASURING_MODE_GDI_CLASSIC, "got %d\n", mode); |
| } |
| else |
| ok_(__FILE__, line)(mode == DWRITE_MEASURING_MODE_NATURAL, "got %d\n", mode); |
| } |
| |
| static HRESULT WINAPI testrenderer_DrawGlyphRun(IDWriteTextRenderer *iface, |
| void *context, |
| FLOAT baselineOriginX, |
| FLOAT baselineOriginY, |
| DWRITE_MEASURING_MODE mode, |
| DWRITE_GLYPH_RUN const *run, |
| DWRITE_GLYPH_RUN_DESCRIPTION const *descr, |
| IUnknown *effect) |
| { |
| struct renderer_context *ctxt = (struct renderer_context*)context; |
| struct drawcall_entry entry; |
| DWRITE_SCRIPT_ANALYSIS sa; |
| |
| if (ctxt) { |
| TEST_MEASURING_MODE(ctxt, mode); |
| ctxt->originX = baselineOriginX; |
| ctxt->originY = baselineOriginY; |
| } |
| |
| ok(descr->stringLength < sizeof(entry.string)/sizeof(WCHAR), "string is too long\n"); |
| if (descr->stringLength && descr->stringLength < sizeof(entry.string)/sizeof(WCHAR)) { |
| memcpy(entry.string, descr->string, descr->stringLength*sizeof(WCHAR)); |
| entry.string[descr->stringLength] = 0; |
| } |
| else |
| entry.string[0] = 0; |
| |
| /* see what's reported for control codes runs */ |
| get_script_analysis(descr->string, descr->stringLength, &sa); |
| if (sa.shapes == DWRITE_SCRIPT_SHAPES_NO_VISUAL) { |
| UINT32 i; |
| |
| /* glyphs are not reported at all for control code runs */ |
| ok(run->glyphCount == 0, "got %u\n", run->glyphCount); |
| ok(run->glyphAdvances != NULL, "advances array %p\n", run->glyphAdvances); |
| ok(run->glyphOffsets != NULL, "offsets array %p\n", run->glyphOffsets); |
| ok(run->fontFace != NULL, "got %p\n", run->fontFace); |
| /* text positions are still valid */ |
| ok(descr->string != NULL, "got string %p\n", descr->string); |
| ok(descr->stringLength > 0, "got string length %u\n", descr->stringLength); |
| ok(descr->clusterMap != NULL, "clustermap %p\n", descr->clusterMap); |
| for (i = 0; i < descr->stringLength; i++) |
| ok(descr->clusterMap[i] == i, "got %u\n", descr->clusterMap[i]); |
| } |
| |
| entry.kind = DRAW_GLYPHRUN; |
| if (effect) |
| entry.kind |= DRAW_EFFECT; |
| ok(lstrlenW(descr->localeName) < LOCALE_NAME_MAX_LENGTH, "unexpectedly long locale name\n"); |
| lstrcpyW(entry.locale, descr->localeName); |
| entry.glyphcount = run->glyphCount; |
| add_call(sequences, RENDERER_ID, &entry); |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI testrenderer_DrawUnderline(IDWriteTextRenderer *iface, |
| void *context, |
| FLOAT baselineOriginX, |
| FLOAT baselineOriginY, |
| DWRITE_UNDERLINE const* underline, |
| IUnknown *effect) |
| { |
| struct renderer_context *ctxt = (struct renderer_context*)context; |
| struct drawcall_entry entry = { 0 }; |
| |
| if (ctxt) |
| TEST_MEASURING_MODE(ctxt, underline->measuringMode); |
| |
| ok(underline->runHeight > 0.0f, "Expected non-zero run height\n"); |
| if (ctxt && ctxt->format) { |
| DWRITE_FONT_METRICS metrics; |
| IDWriteFontFace *fontface; |
| FLOAT emsize; |
| |
| fontface = get_fontface_from_format(ctxt->format); |
| emsize = IDWriteTextFormat_GetFontSize(ctxt->format); |
| IDWriteFontFace_GetMetrics(fontface, &metrics); |
| |
| ok(emsize == metrics.designUnitsPerEm, "Unexpected font size %f\n", emsize); |
| /* Expected height is in design units, allow some absolute difference from it. Seems to only happen on Vista */ |
| ok(abs(metrics.capHeight - underline->runHeight) < 2.0f, "Expected runHeight %u, got %f, family %s\n", |
| metrics.capHeight, underline->runHeight, wine_dbgstr_w(ctxt->familyW)); |
| |
| IDWriteFontFace_Release(fontface); |
| } |
| |
| entry.kind = DRAW_UNDERLINE; |
| if (effect) |
| entry.kind |= DRAW_EFFECT; |
| lstrcpyW(entry.locale, underline->localeName); |
| add_call(sequences, RENDERER_ID, &entry); |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI testrenderer_DrawStrikethrough(IDWriteTextRenderer *iface, |
| void *context, |
| FLOAT baselineOriginX, |
| FLOAT baselineOriginY, |
| DWRITE_STRIKETHROUGH const* strikethrough, |
| IUnknown *effect) |
| { |
| struct renderer_context *ctxt = (struct renderer_context*)context; |
| struct drawcall_entry entry = { 0 }; |
| |
| if (ctxt) |
| TEST_MEASURING_MODE(ctxt, strikethrough->measuringMode); |
| |
| entry.kind = DRAW_STRIKETHROUGH; |
| if (effect) |
| entry.kind |= DRAW_EFFECT; |
| add_call(sequences, RENDERER_ID, &entry); |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI testrenderer_DrawInlineObject(IDWriteTextRenderer *iface, |
| void *context, |
| FLOAT originX, |
| FLOAT originY, |
| IDWriteInlineObject *object, |
| BOOL is_sideways, |
| BOOL is_rtl, |
| IUnknown *effect) |
| { |
| struct drawcall_entry entry = { 0 }; |
| entry.kind = DRAW_INLINE; |
| if (effect) |
| entry.kind |= DRAW_EFFECT; |
| add_call(sequences, RENDERER_ID, &entry); |
| return S_OK; |
| } |
| |
| static const IDWriteTextRendererVtbl testrenderervtbl = { |
| testrenderer_QI, |
| testrenderer_AddRef, |
| testrenderer_Release, |
| testrenderer_IsPixelSnappingDisabled, |
| testrenderer_GetCurrentTransform, |
| testrenderer_GetPixelsPerDip, |
| testrenderer_DrawGlyphRun, |
| testrenderer_DrawUnderline, |
| testrenderer_DrawStrikethrough, |
| testrenderer_DrawInlineObject |
| }; |
| |
| static IDWriteTextRenderer testrenderer = { &testrenderervtbl }; |
| |
| /* test IDWriteInlineObject */ |
| static HRESULT WINAPI testinlineobj_QI(IDWriteInlineObject *iface, REFIID riid, void **obj) |
| { |
| if (IsEqualIID(riid, &IID_IDWriteInlineObject) || IsEqualIID(riid, &IID_IUnknown)) { |
| *obj = iface; |
| IDWriteInlineObject_AddRef(iface); |
| return S_OK; |
| } |
| |
| *obj = NULL; |
| return E_NOINTERFACE; |
| } |
| |
| static ULONG WINAPI testinlineobj_AddRef(IDWriteInlineObject *iface) |
| { |
| return 2; |
| } |
| |
| static ULONG WINAPI testinlineobj_Release(IDWriteInlineObject *iface) |
| { |
| return 1; |
| } |
| |
| static HRESULT WINAPI testinlineobj_Draw(IDWriteInlineObject *iface, |
| void* client_drawingontext, IDWriteTextRenderer* renderer, |
| FLOAT originX, FLOAT originY, BOOL is_sideways, BOOL is_rtl, IUnknown *drawing_effect) |
| { |
| ok(0, "unexpected call\n"); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI testinlineobj_GetMetrics(IDWriteInlineObject *iface, DWRITE_INLINE_OBJECT_METRICS *metrics) |
| { |
| metrics->width = 123.0; |
| return 0x8faecafe; |
| } |
| |
| static HRESULT WINAPI testinlineobj_GetOverhangMetrics(IDWriteInlineObject *iface, DWRITE_OVERHANG_METRICS *overhangs) |
| { |
| ok(0, "unexpected call\n"); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI testinlineobj_GetBreakConditions(IDWriteInlineObject *iface, DWRITE_BREAK_CONDITION *before, |
| DWRITE_BREAK_CONDITION *after) |
| { |
| *before = *after = DWRITE_BREAK_CONDITION_MUST_BREAK; |
| return 0x8feacafe; |
| } |
| |
| static HRESULT WINAPI testinlineobj2_GetBreakConditions(IDWriteInlineObject *iface, DWRITE_BREAK_CONDITION *before, |
| DWRITE_BREAK_CONDITION *after) |
| { |
| *before = *after = DWRITE_BREAK_CONDITION_MAY_NOT_BREAK; |
| return S_OK; |
| } |
| |
| static IDWriteInlineObjectVtbl testinlineobjvtbl = { |
| testinlineobj_QI, |
| testinlineobj_AddRef, |
| testinlineobj_Release, |
| testinlineobj_Draw, |
| testinlineobj_GetMetrics, |
| testinlineobj_GetOverhangMetrics, |
| testinlineobj_GetBreakConditions |
| }; |
| |
| static IDWriteInlineObjectVtbl testinlineobjvtbl2 = { |
| testinlineobj_QI, |
| testinlineobj_AddRef, |
| testinlineobj_Release, |
| testinlineobj_Draw, |
| testinlineobj_GetMetrics, |
| testinlineobj_GetOverhangMetrics, |
| testinlineobj2_GetBreakConditions |
| }; |
| |
| static IDWriteInlineObject testinlineobj = { &testinlineobjvtbl }; |
| static IDWriteInlineObject testinlineobj2 = { &testinlineobjvtbl }; |
| static IDWriteInlineObject testinlineobj3 = { &testinlineobjvtbl2 }; |
| |
| struct test_inline_obj |
| { |
| IDWriteInlineObject IDWriteInlineObject_iface; |
| DWRITE_INLINE_OBJECT_METRICS metrics; |
| DWRITE_OVERHANG_METRICS overhangs; |
| }; |
| |
| static inline struct test_inline_obj *impl_from_IDWriteInlineObject(IDWriteInlineObject *iface) |
| { |
| return CONTAINING_RECORD(iface, struct test_inline_obj, IDWriteInlineObject_iface); |
| } |
| |
| static HRESULT WINAPI testinlineobj3_GetMetrics(IDWriteInlineObject *iface, DWRITE_INLINE_OBJECT_METRICS *metrics) |
| { |
| struct test_inline_obj *obj = impl_from_IDWriteInlineObject(iface); |
| *metrics = obj->metrics; |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI testinlineobj3_GetOverhangMetrics(IDWriteInlineObject *iface, DWRITE_OVERHANG_METRICS *overhangs) |
| { |
| struct test_inline_obj *obj = impl_from_IDWriteInlineObject(iface); |
| *overhangs = obj->overhangs; |
| /* Return value is ignored. */ |
| return E_NOTIMPL; |
| } |
| |
| static const IDWriteInlineObjectVtbl testinlineobjvtbl3 = { |
| testinlineobj_QI, |
| testinlineobj_AddRef, |
| testinlineobj_Release, |
| testinlineobj_Draw, |
| testinlineobj3_GetMetrics, |
| testinlineobj3_GetOverhangMetrics, |
| testinlineobj_GetBreakConditions, |
| }; |
| |
| static void test_inline_obj_init(struct test_inline_obj *obj, const DWRITE_INLINE_OBJECT_METRICS *metrics, |
| const DWRITE_OVERHANG_METRICS *overhangs) |
| { |
| obj->IDWriteInlineObject_iface.lpVtbl = &testinlineobjvtbl3; |
| obj->metrics = *metrics; |
| obj->overhangs = *overhangs; |
| } |
| |
| struct test_effect |
| { |
| IUnknown IUnknown_iface; |
| LONG ref; |
| }; |
| |
| static inline struct test_effect *test_effect_from_IUnknown(IUnknown *iface) |
| { |
| return CONTAINING_RECORD(iface, struct test_effect, IUnknown_iface); |
| } |
| |
| static HRESULT WINAPI testeffect_QI(IUnknown *iface, REFIID riid, void **obj) |
| { |
| if (IsEqualIID(riid, &IID_IUnknown)) { |
| *obj = iface; |
| IUnknown_AddRef(iface); |
| return S_OK; |
| } |
| |
| ok(0, "Unexpected riid %s.\n", wine_dbgstr_guid(riid)); |
| *obj = NULL; |
| return E_NOINTERFACE; |
| } |
| |
| static ULONG WINAPI testeffect_AddRef(IUnknown *iface) |
| { |
| struct test_effect *effect = test_effect_from_IUnknown(iface); |
| return InterlockedIncrement(&effect->ref); |
| } |
| |
| static ULONG WINAPI testeffect_Release(IUnknown *iface) |
| { |
| struct test_effect *effect = test_effect_from_IUnknown(iface); |
| LONG ref = InterlockedDecrement(&effect->ref); |
| |
| if (!ref) |
| HeapFree(GetProcessHeap(), 0, effect); |
| |
| return ref; |
| } |
| |
| static const IUnknownVtbl testeffectvtbl = { |
| testeffect_QI, |
| testeffect_AddRef, |
| testeffect_Release |
| }; |
| |
| static IUnknown *create_test_effect(void) |
| { |
| struct test_effect *effect; |
| |
| effect = HeapAlloc(GetProcessHeap(), 0, sizeof(*effect)); |
| effect->IUnknown_iface.lpVtbl = &testeffectvtbl; |
| effect->ref = 1; |
| |
| return &effect->IUnknown_iface; |
| } |
| |
| static void test_CreateTextLayout(void) |
| { |
| static const WCHAR strW[] = {'s','t','r','i','n','g',0}; |
| IDWriteTextLayout2 *layout2; |
| IDWriteTextLayout *layout; |
| IDWriteTextFormat *format; |
| IDWriteFactory *factory; |
| HRESULT hr; |
| |
| factory = create_factory(); |
| |
| layout = (void*)0xdeadbeef; |
| hr = IDWriteFactory_CreateTextLayout(factory, NULL, 0, NULL, 0.0, 0.0, &layout); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| ok(layout == NULL, "got %p\n", layout); |
| |
| layout = (void*)0xdeadbeef; |
| hr = IDWriteFactory_CreateTextLayout(factory, strW, 6, NULL, 0.0, 0.0, &layout); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| ok(layout == NULL, "got %p\n", layout); |
| |
| layout = (void*)0xdeadbeef; |
| hr = IDWriteFactory_CreateTextLayout(factory, strW, 6, NULL, 1.0, 0.0, &layout); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| ok(layout == NULL, "got %p\n", layout); |
| |
| layout = (void*)0xdeadbeef; |
| hr = IDWriteFactory_CreateTextLayout(factory, strW, 6, NULL, 0.0, 1.0, &layout); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| ok(layout == NULL, "got %p\n", layout); |
| |
| layout = (void*)0xdeadbeef; |
| hr = IDWriteFactory_CreateTextLayout(factory, strW, 6, NULL, 1000.0, 1000.0, &layout); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| ok(layout == NULL, "got %p\n", layout); |
| |
| hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, |
| DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| layout = (void*)0xdeadbeef; |
| hr = IDWriteFactory_CreateTextLayout(factory, NULL, 0, format, 100.0f, 100.0f, &layout); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| ok(layout == NULL, "got %p\n", layout); |
| |
| hr = IDWriteFactory_CreateTextLayout(factory, strW, 0, format, 0.0f, 0.0f, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| IDWriteTextLayout_Release(layout); |
| |
| EXPECT_REF(format, 1); |
| hr = IDWriteFactory_CreateTextLayout(factory, strW, 6, format, 1000.0, 1000.0, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| EXPECT_REF(format, 1); |
| |
| hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextLayout2, (void**)&layout2); |
| if (hr == S_OK) { |
| IDWriteTextLayout1 *layout1; |
| IDWriteTextFormat1 *format1; |
| IDWriteTextFormat *format; |
| |
| hr = IDWriteTextLayout2_QueryInterface(layout2, &IID_IDWriteTextLayout1, (void**)&layout1); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| IDWriteTextLayout1_Release(layout1); |
| |
| EXPECT_REF(layout2, 2); |
| hr = IDWriteTextLayout2_QueryInterface(layout2, &IID_IDWriteTextFormat1, (void**)&format1); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| EXPECT_REF(layout2, 3); |
| |
| hr = IDWriteTextLayout2_QueryInterface(layout2, &IID_IDWriteTextFormat, (void**)&format); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(format == (IDWriteTextFormat*)format1, "got %p, %p\n", format, format1); |
| ok(format != (IDWriteTextFormat*)layout2, "got %p, %p\n", format, layout2); |
| EXPECT_REF(layout2, 4); |
| |
| hr = IDWriteTextFormat_QueryInterface(format, &IID_IDWriteTextLayout1, (void**)&layout1); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| IDWriteTextLayout1_Release(layout1); |
| |
| IDWriteTextFormat1_Release(format1); |
| IDWriteTextFormat_Release(format); |
| |
| hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextFormat1, (void**)&format1); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| EXPECT_REF(layout2, 3); |
| |
| hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextFormat, (void**)&format); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(format == (IDWriteTextFormat*)format1, "got %p, %p\n", format, format1); |
| EXPECT_REF(layout2, 4); |
| |
| IDWriteTextFormat1_Release(format1); |
| IDWriteTextFormat_Release(format); |
| IDWriteTextLayout2_Release(layout2); |
| } |
| else |
| win_skip("IDWriteTextLayout2 is not supported.\n"); |
| |
| IDWriteTextLayout_Release(layout); |
| IDWriteTextFormat_Release(format); |
| IDWriteFactory_Release(factory); |
| } |
| |
| static DWRITE_MATRIX layoutcreate_transforms[] = { |
| { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }, |
| { 1.0, 0.0, 0.0, 1.0, 0.3, 0.2 }, |
| { 1.0, 0.0, 0.0, 1.0,-0.3,-0.2 }, |
| |
| { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }, |
| { 1.0, 0.0, 0.0, 0.0, 0.0, 0.0 }, |
| { 1.0, 2.0, 0.5, 1.0, 0.0, 0.0 }, |
| }; |
| |
| static void test_CreateGdiCompatibleTextLayout(void) |
| { |
| static const WCHAR strW[] = {'s','t','r','i','n','g',0}; |
| IDWriteTextLayout *layout; |
| IDWriteTextFormat *format; |
| IDWriteFactory *factory; |
| FLOAT dimension; |
| HRESULT hr; |
| int i; |
| |
| factory = create_factory(); |
| |
| layout = (void*)0xdeadbeef; |
| hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, NULL, 0, NULL, 0.0, 0.0, 0.0, NULL, FALSE, &layout); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| ok(layout == NULL, "got %p\n", layout); |
| |
| layout = (void*)0xdeadbeef; |
| hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, strW, 6, NULL, 0.0, 0.0, 0.0, NULL, FALSE, &layout); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| ok(layout == NULL, "got %p\n", layout); |
| |
| layout = (void*)0xdeadbeef; |
| hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, strW, 6, NULL, 1.0, 0.0, 0.0, NULL, FALSE, &layout); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| ok(layout == NULL, "got %p\n", layout); |
| |
| layout = (void*)0xdeadbeef; |
| hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, strW, 6, NULL, 1.0, 0.0, 1.0, NULL, FALSE, &layout); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| ok(layout == NULL, "got %p\n", layout); |
| |
| layout = (void*)0xdeadbeef; |
| hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, strW, 6, NULL, 1000.0, 1000.0, 1.0, NULL, FALSE, &layout); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| ok(layout == NULL, "got %p\n", layout); |
| |
| /* create with text format */ |
| hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, |
| DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| EXPECT_REF(format, 1); |
| |
| layout = (void*)0xdeadbeef; |
| hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, NULL, 0, format, 100.0f, 100.0f, 1.0f, NULL, FALSE, &layout); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| ok(layout == NULL, "got %p\n", layout); |
| |
| hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, strW, 6, format, 100.0, 100.0, 1.0, NULL, FALSE, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| EXPECT_REF(format, 1); |
| EXPECT_REF(layout, 1); |
| |
| IDWriteTextLayout_AddRef(layout); |
| EXPECT_REF(format, 1); |
| EXPECT_REF(layout, 2); |
| IDWriteTextLayout_Release(layout); |
| IDWriteTextLayout_Release(layout); |
| |
| /* zero length string is okay */ |
| hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, strW, 0, format, 100.0, 100.0, 1.0, NULL, FALSE, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| dimension = IDWriteTextLayout_GetMaxWidth(layout); |
| ok(dimension == 100.0, "got %f\n", dimension); |
| |
| dimension = IDWriteTextLayout_GetMaxHeight(layout); |
| ok(dimension == 100.0, "got %f\n", dimension); |
| |
| IDWriteTextLayout_Release(layout); |
| |
| /* negative, zero ppdip */ |
| hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, strW, 1, format, 100.0, 100.0, -1.0, NULL, FALSE, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| IDWriteTextLayout_Release(layout); |
| |
| hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, strW, 1, format, 100.0, 100.0, 0.0, NULL, FALSE, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| IDWriteTextLayout_Release(layout); |
| |
| /* transforms */ |
| for (i = 0; i < sizeof(layoutcreate_transforms)/sizeof(layoutcreate_transforms[0]); i++) { |
| hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, strW, 1, format, 100.0, 100.0, 1.0, |
| &layoutcreate_transforms[i], FALSE, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| IDWriteTextLayout_Release(layout); |
| } |
| |
| IDWriteTextFormat_Release(format); |
| IDWriteFactory_Release(factory); |
| } |
| |
| static void test_CreateTextFormat(void) |
| { |
| static const WCHAR emptyW[] = {0}; |
| IDWriteFontCollection *collection, *syscoll; |
| DWRITE_PARAGRAPH_ALIGNMENT paralign; |
| DWRITE_READING_DIRECTION readdir; |
| DWRITE_WORD_WRAPPING wrapping; |
| DWRITE_TEXT_ALIGNMENT align; |
| DWRITE_FLOW_DIRECTION flow; |
| DWRITE_LINE_SPACING_METHOD method; |
| DWRITE_TRIMMING trimming; |
| IDWriteTextFormat *format; |
| FLOAT spacing, baseline; |
| IDWriteInlineObject *trimmingsign; |
| IDWriteFactory *factory; |
| HRESULT hr; |
| |
| factory = create_factory(); |
| |
| /* zero/negative font size */ |
| hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, |
| DWRITE_FONT_STRETCH_NORMAL, 0.0f, enusW, &format); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, |
| DWRITE_FONT_STRETCH_NORMAL, -10.0f, enusW, &format); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| |
| /* invalid font properties */ |
| hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, 1000, DWRITE_FONT_STYLE_NORMAL, |
| DWRITE_FONT_STRETCH_NORMAL, 10.0f, enusW, &format); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_ITALIC + 1, |
| DWRITE_FONT_STRETCH_NORMAL, 10.0f, enusW, &format); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_ITALIC, |
| 10, 10.0f, enusW, &format); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| |
| /* empty family name */ |
| hr = IDWriteFactory_CreateTextFormat(factory, emptyW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, |
| DWRITE_FONT_STRETCH_NORMAL, 10.0f, enusW, &format); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| IDWriteTextFormat_Release(format); |
| |
| hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, |
| DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| if (0) /* crashes on native */ |
| hr = IDWriteTextFormat_GetFontCollection(format, NULL); |
| |
| collection = NULL; |
| hr = IDWriteTextFormat_GetFontCollection(format, &collection); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(collection != NULL, "got %p\n", collection); |
| |
| hr = IDWriteFactory_GetSystemFontCollection(factory, &syscoll, FALSE); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(collection == syscoll, "got %p, was %p\n", syscoll, collection); |
| IDWriteFontCollection_Release(syscoll); |
| IDWriteFontCollection_Release(collection); |
| |
| /* default format properties */ |
| align = IDWriteTextFormat_GetTextAlignment(format); |
| ok(align == DWRITE_TEXT_ALIGNMENT_LEADING, "got %d\n", align); |
| |
| paralign = IDWriteTextFormat_GetParagraphAlignment(format); |
| ok(paralign == DWRITE_PARAGRAPH_ALIGNMENT_NEAR, "got %d\n", paralign); |
| |
| wrapping = IDWriteTextFormat_GetWordWrapping(format); |
| ok(wrapping == DWRITE_WORD_WRAPPING_WRAP, "got %d\n", wrapping); |
| |
| readdir = IDWriteTextFormat_GetReadingDirection(format); |
| ok(readdir == DWRITE_READING_DIRECTION_LEFT_TO_RIGHT, "got %d\n", readdir); |
| |
| flow = IDWriteTextFormat_GetFlowDirection(format); |
| ok(flow == DWRITE_FLOW_DIRECTION_TOP_TO_BOTTOM, "got %d\n", flow); |
| |
| hr = IDWriteTextFormat_GetLineSpacing(format, &method, &spacing, &baseline); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(spacing == 0.0, "got %f\n", spacing); |
| ok(baseline == 0.0, "got %f\n", baseline); |
| ok(method == DWRITE_LINE_SPACING_METHOD_DEFAULT, "got %d\n", method); |
| |
| trimming.granularity = DWRITE_TRIMMING_GRANULARITY_WORD; |
| trimming.delimiter = 10; |
| trimming.delimiterCount = 10; |
| trimmingsign = (void*)0xdeadbeef; |
| hr = IDWriteTextFormat_GetTrimming(format, &trimming, &trimmingsign); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(trimming.granularity == DWRITE_TRIMMING_GRANULARITY_NONE, "got %d\n", trimming.granularity); |
| ok(trimming.delimiter == 0, "got %d\n", trimming.delimiter); |
| ok(trimming.delimiterCount == 0, "got %d\n", trimming.delimiterCount); |
| ok(trimmingsign == NULL, "got %p\n", trimmingsign); |
| |
| /* setters */ |
| hr = IDWriteTextFormat_SetTextAlignment(format, DWRITE_TEXT_ALIGNMENT_LEADING); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextFormat_SetTextAlignment(format, DWRITE_TEXT_ALIGNMENT_JUSTIFIED+1); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextFormat_SetParagraphAlignment(format, DWRITE_PARAGRAPH_ALIGNMENT_NEAR); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextFormat_SetParagraphAlignment(format, DWRITE_PARAGRAPH_ALIGNMENT_CENTER+1); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextFormat_SetWordWrapping(format, DWRITE_WORD_WRAPPING_WRAP); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextFormat_SetWordWrapping(format, DWRITE_WORD_WRAPPING_CHARACTER+1); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextFormat_SetReadingDirection(format, DWRITE_READING_DIRECTION_LEFT_TO_RIGHT); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextFormat_SetFlowDirection(format, DWRITE_FLOW_DIRECTION_TOP_TO_BOTTOM); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| |
| hr = IDWriteTextFormat_SetTrimming(format, &trimming, NULL); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| /* invalid granularity */ |
| trimming.granularity = 10; |
| trimming.delimiter = 0; |
| trimming.delimiterCount = 0; |
| hr = IDWriteTextFormat_SetTrimming(format, &trimming, NULL); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| |
| IDWriteTextFormat_Release(format); |
| IDWriteFactory_Release(factory); |
| } |
| |
| static void test_GetLocaleName(void) |
| { |
| static const WCHAR strW[] = {'s','t','r','i','n','g',0}; |
| static const WCHAR ruW[] = {'r','u',0}; |
| IDWriteTextLayout *layout; |
| IDWriteTextFormat *format, *format2; |
| IDWriteFactory *factory; |
| WCHAR buff[10]; |
| UINT32 len; |
| HRESULT hr; |
| |
| factory = create_factory(); |
| |
| hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, |
| DWRITE_FONT_STRETCH_NORMAL, 10.0, ruW, &format); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, strW, 0, format, 100.0, 100.0, 1.0, NULL, FALSE, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextFormat, (void**)&format2); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| len = IDWriteTextFormat_GetLocaleNameLength(format2); |
| ok(len == 2, "got %u\n", len); |
| len = IDWriteTextFormat_GetLocaleNameLength(format); |
| ok(len == 2, "got %u\n", len); |
| hr = IDWriteTextFormat_GetLocaleName(format2, buff, len); |
| ok(hr == E_NOT_SUFFICIENT_BUFFER, "got 0x%08x\n", hr); |
| hr = IDWriteTextFormat_GetLocaleName(format2, buff, len+1); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(!lstrcmpW(buff, ruW), "got %s\n", wine_dbgstr_w(buff)); |
| hr = IDWriteTextFormat_GetLocaleName(format, buff, len); |
| ok(hr == E_NOT_SUFFICIENT_BUFFER, "got 0x%08x\n", hr); |
| hr = IDWriteTextFormat_GetLocaleName(format, buff, len+1); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(!lstrcmpW(buff, ruW), "got %s\n", wine_dbgstr_w(buff)); |
| |
| IDWriteTextLayout_Release(layout); |
| IDWriteTextFormat_Release(format); |
| IDWriteTextFormat_Release(format2); |
| IDWriteFactory_Release(factory); |
| } |
| |
| static const struct drawcall_entry drawellipsis_seq[] = { |
| { DRAW_GLYPHRUN, {0x2026, 0}, {'e','n','-','g','b',0}, 1 }, |
| { DRAW_LAST_KIND } |
| }; |
| |
| static void test_CreateEllipsisTrimmingSign(void) |
| { |
| static const WCHAR engbW[] = {'e','n','-','G','B',0}; |
| DWRITE_INLINE_OBJECT_METRICS metrics; |
| DWRITE_BREAK_CONDITION before, after; |
| struct renderer_context ctxt; |
| IDWriteTextFormat *format; |
| IDWriteInlineObject *sign; |
| IDWriteFactory *factory; |
| IUnknown *unk, *effect; |
| HRESULT hr; |
| |
| factory = create_factory(); |
| |
| hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, |
| DWRITE_FONT_STRETCH_NORMAL, 10.0, engbW, &format); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| EXPECT_REF(format, 1); |
| hr = IDWriteFactory_CreateEllipsisTrimmingSign(factory, format, &sign); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| EXPECT_REF(format, 1); |
| |
| hr = IDWriteInlineObject_QueryInterface(sign, &IID_IDWriteTextLayout, (void**)&unk); |
| ok(hr == E_NOINTERFACE, "got 0x%08x\n", hr); |
| |
| if (0) {/* crashes on native */ |
| hr = IDWriteInlineObject_GetBreakConditions(sign, NULL, NULL); |
| hr = IDWriteInlineObject_GetMetrics(sign, NULL); |
| } |
| metrics.width = 0.0; |
| metrics.height = 123.0; |
| metrics.baseline = 123.0; |
| metrics.supportsSideways = TRUE; |
| hr = IDWriteInlineObject_GetMetrics(sign, &metrics); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(metrics.width > 0.0, "got %.2f\n", metrics.width); |
| ok(metrics.height == 0.0, "got %.2f\n", metrics.height); |
| ok(metrics.baseline == 0.0, "got %.2f\n", metrics.baseline); |
| ok(!metrics.supportsSideways, "got %d\n", metrics.supportsSideways); |
| |
| before = after = DWRITE_BREAK_CONDITION_CAN_BREAK; |
| hr = IDWriteInlineObject_GetBreakConditions(sign, &before, &after); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(before == DWRITE_BREAK_CONDITION_NEUTRAL, "got %d\n", before); |
| ok(after == DWRITE_BREAK_CONDITION_NEUTRAL, "got %d\n", after); |
| |
| /* Draw tests */ |
| flush_sequence(sequences, RENDERER_ID); |
| hr = IDWriteInlineObject_Draw(sign, NULL, &testrenderer, 0.0, 0.0, FALSE, FALSE, NULL); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok_sequence(sequences, RENDERER_ID, drawellipsis_seq, "ellipsis sign draw test", FALSE); |
| |
| effect = create_test_effect(); |
| |
| EXPECT_REF(effect, 1); |
| flush_sequence(sequences, RENDERER_ID); |
| hr = IDWriteInlineObject_Draw(sign, NULL, &testrenderer, 0.0f, 0.0f, FALSE, FALSE, effect); |
| ok(hr == S_OK, "Failed to draw trimming sign, hr %#x.\n", hr); |
| ok_sequence(sequences, RENDERER_ID, drawellipsis_seq, "ellipsis sign draw with effect test", FALSE); |
| EXPECT_REF(effect, 1); |
| |
| IUnknown_Release(effect); |
| |
| flush_sequence(sequences, RENDERER_ID); |
| hr = IDWriteInlineObject_Draw(sign, NULL, &testrenderer, 0.0f, 0.0f, FALSE, FALSE, (void *)0xdeadbeef); |
| ok(hr == S_OK, "Failed to draw trimming sign, hr %#x.\n", hr); |
| ok_sequence(sequences, RENDERER_ID, drawellipsis_seq, "ellipsis sign draw with effect test", FALSE); |
| |
| memset(&ctxt, 0, sizeof(ctxt)); |
| hr = IDWriteInlineObject_Draw(sign, &ctxt, &testrenderer, 123.0f, 456.0f, FALSE, FALSE, NULL); |
| ok(hr == S_OK, "Failed to draw trimming sign, hr %#x.\n", hr); |
| ok(ctxt.originX == 123.0f && ctxt.originY == 456.0f, "Unexpected drawing origin\n"); |
| |
| IDWriteInlineObject_Release(sign); |
| |
| /* Centered format */ |
| hr = IDWriteTextFormat_SetTextAlignment(format, DWRITE_TEXT_ALIGNMENT_CENTER); |
| ok(hr == S_OK, "Failed to set text alignment, hr %#x.\n", hr); |
| |
| hr = IDWriteFactory_CreateEllipsisTrimmingSign(factory, format, &sign); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| memset(&ctxt, 0, sizeof(ctxt)); |
| hr = IDWriteInlineObject_Draw(sign, &ctxt, &testrenderer, 123.0f, 456.0f, FALSE, FALSE, NULL); |
| ok(hr == S_OK, "Failed to draw trimming sign, hr %#x.\n", hr); |
| ok(ctxt.originX == 123.0f && ctxt.originY == 456.0f, "Unexpected drawing origin\n"); |
| |
| IDWriteInlineObject_Release(sign); |
| |
| /* non-orthogonal flow/reading combination */ |
| hr = IDWriteTextFormat_SetReadingDirection(format, DWRITE_READING_DIRECTION_LEFT_TO_RIGHT); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextFormat_SetFlowDirection(format, DWRITE_FLOW_DIRECTION_LEFT_TO_RIGHT); |
| ok(hr == S_OK || broken(hr == E_INVALIDARG) /* vista, win7 */, "got 0x%08x\n", hr); |
| if (hr == S_OK) { |
| hr = IDWriteFactory_CreateEllipsisTrimmingSign(factory, format, &sign); |
| ok(hr == DWRITE_E_FLOWDIRECTIONCONFLICTS, "got 0x%08x\n", hr); |
| } |
| |
| IDWriteTextFormat_Release(format); |
| IDWriteFactory_Release(factory); |
| } |
| |
| static void test_fontweight(void) |
| { |
| static const WCHAR strW[] = {'s','t','r','i','n','g',0}; |
| static const WCHAR ruW[] = {'r','u',0}; |
| IDWriteTextFormat *format, *fmt2; |
| IDWriteTextLayout *layout; |
| DWRITE_FONT_WEIGHT weight; |
| DWRITE_TEXT_RANGE range; |
| IDWriteFactory *factory; |
| FLOAT size; |
| HRESULT hr; |
| |
| factory = create_factory(); |
| |
| hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_BOLD, DWRITE_FONT_STYLE_NORMAL, |
| DWRITE_FONT_STRETCH_NORMAL, 10.0, ruW, &format); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, strW, 6, format, 100.0, 100.0, 1.0, NULL, FALSE, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextFormat, (void**)&fmt2); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| weight = IDWriteTextFormat_GetFontWeight(fmt2); |
| ok(weight == DWRITE_FONT_WEIGHT_BOLD, "got %u\n", weight); |
| |
| range.startPosition = range.length = 0; |
| hr = IDWriteTextLayout_GetFontWeight(layout, 0, &weight, &range); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(range.startPosition == 0 && range.length == ~0u, "got %u, %u\n", range.startPosition, range.length); |
| |
| range.startPosition = 0; |
| range.length = 6; |
| hr = IDWriteTextLayout_SetFontWeight(layout, DWRITE_FONT_WEIGHT_NORMAL, range); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| range.startPosition = range.length = 0; |
| hr = IDWriteTextLayout_GetFontWeight(layout, 0, &weight, &range); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(range.startPosition == 0 && range.length == 6, "got %u, %u\n", range.startPosition, range.length); |
| |
| /* IDWriteTextFormat methods output doesn't reflect layout changes */ |
| weight = IDWriteTextFormat_GetFontWeight(fmt2); |
| ok(weight == DWRITE_FONT_WEIGHT_BOLD, "got %u\n", weight); |
| |
| range.length = 0; |
| weight = DWRITE_FONT_WEIGHT_BOLD; |
| hr = IDWriteTextLayout_GetFontWeight(layout, 0, &weight, &range); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(weight == DWRITE_FONT_WEIGHT_NORMAL, "got %d\n", weight); |
| ok(range.length == 6, "got %d\n", range.length); |
| |
| range.startPosition = 0; |
| range.length = 6; |
| hr = IDWriteTextLayout_SetFontWeight(layout, 1000, range); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| |
| size = IDWriteTextLayout_GetMaxWidth(layout); |
| ok(size == 100.0, "got %.2f\n", size); |
| |
| hr = IDWriteTextLayout_SetMaxWidth(layout, 0.0); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| size = IDWriteTextLayout_GetMaxWidth(layout); |
| ok(size == 0.0, "got %.2f\n", size); |
| |
| hr = IDWriteTextLayout_SetMaxWidth(layout, -1.0); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| |
| size = IDWriteTextLayout_GetMaxWidth(layout); |
| ok(size == 0.0, "got %.2f\n", size); |
| |
| hr = IDWriteTextLayout_SetMaxWidth(layout, 100.0); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| size = IDWriteTextLayout_GetMaxWidth(layout); |
| ok(size == 100.0, "got %.2f\n", size); |
| |
| size = IDWriteTextLayout_GetMaxHeight(layout); |
| ok(size == 100.0, "got %.2f\n", size); |
| |
| hr = IDWriteTextLayout_SetMaxHeight(layout, 0.0); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| size = IDWriteTextLayout_GetMaxHeight(layout); |
| ok(size == 0.0, "got %.2f\n", size); |
| |
| hr = IDWriteTextLayout_SetMaxHeight(layout, -1.0); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| |
| size = IDWriteTextLayout_GetMaxHeight(layout); |
| ok(size == 0.0, "got %.2f\n", size); |
| |
| hr = IDWriteTextLayout_SetMaxHeight(layout, 100.0); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| size = IDWriteTextLayout_GetMaxHeight(layout); |
| ok(size == 100.0, "got %.2f\n", size); |
| |
| IDWriteTextLayout_Release(layout); |
| IDWriteTextFormat_Release(fmt2); |
| IDWriteTextFormat_Release(format); |
| IDWriteFactory_Release(factory); |
| } |
| |
| static void test_SetInlineObject(void) |
| { |
| static const WCHAR strW[] = {'s','t','r','i','n','g',0}; |
| static const WCHAR ruW[] = {'r','u',0}; |
| |
| IDWriteInlineObject *inlineobj, *inlineobj2, *inlinetest; |
| IDWriteTextFormat *format; |
| IDWriteTextLayout *layout; |
| DWRITE_TEXT_RANGE range, r2; |
| IDWriteFactory *factory; |
| HRESULT hr; |
| |
| factory = create_factory(); |
| |
| hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_BOLD, DWRITE_FONT_STYLE_NORMAL, |
| DWRITE_FONT_STRETCH_NORMAL, 10.0, ruW, &format); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, strW, 6, format, 100.0, 100.0, 1.0, NULL, FALSE, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFactory_CreateEllipsisTrimmingSign(factory, format, &inlineobj); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFactory_CreateEllipsisTrimmingSign(factory, format, &inlineobj2); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| EXPECT_REF(inlineobj, 1); |
| EXPECT_REF(inlineobj2, 1); |
| |
| inlinetest = (void*)0x1; |
| hr = IDWriteTextLayout_GetInlineObject(layout, 0, &inlinetest, NULL); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(inlinetest == NULL, "got %p\n", inlinetest); |
| |
| range.startPosition = 0; |
| range.length = 2; |
| hr = IDWriteTextLayout_SetInlineObject(layout, inlineobj, range); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| EXPECT_REF(inlineobj, 2); |
| |
| inlinetest = (void*)0x1; |
| hr = IDWriteTextLayout_GetInlineObject(layout, 2, &inlinetest, NULL); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(inlinetest == NULL, "got %p\n", inlinetest); |
| |
| inlinetest = NULL; |
| r2.startPosition = r2.length = 100; |
| hr = IDWriteTextLayout_GetInlineObject(layout, 0, &inlinetest, &r2); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(inlinetest == inlineobj, "got %p\n", inlinetest); |
| ok(r2.startPosition == 0 && r2.length == 2, "got %d, %d\n", r2.startPosition, r2.length); |
| IDWriteInlineObject_Release(inlinetest); |
| |
| EXPECT_REF(inlineobj, 2); |
| |
| /* get from somewhere inside a range */ |
| inlinetest = NULL; |
| r2.startPosition = r2.length = 100; |
| hr = IDWriteTextLayout_GetInlineObject(layout, 1, &inlinetest, &r2); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(inlinetest == inlineobj, "got %p\n", inlinetest); |
| ok(r2.startPosition == 0 && r2.length == 2, "got %d, %d\n", r2.startPosition, r2.length); |
| IDWriteInlineObject_Release(inlinetest); |
| |
| EXPECT_REF(inlineobj, 2); |
| |
| range.startPosition = 1; |
| range.length = 1; |
| hr = IDWriteTextLayout_SetInlineObject(layout, inlineobj2, range); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| inlinetest = NULL; |
| r2.startPosition = r2.length = 100; |
| hr = IDWriteTextLayout_GetInlineObject(layout, 1, &inlinetest, &r2); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(inlinetest == inlineobj2, "got %p\n", inlinetest); |
| ok(r2.startPosition == 1 && r2.length == 1, "got %d, %d\n", r2.startPosition, r2.length); |
| IDWriteInlineObject_Release(inlinetest); |
| |
| EXPECT_REF(inlineobj, 2); |
| EXPECT_REF(inlineobj2, 2); |
| |
| inlinetest = NULL; |
| r2.startPosition = r2.length = 100; |
| hr = IDWriteTextLayout_GetInlineObject(layout, 0, &inlinetest, &r2); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(inlinetest == inlineobj, "got %p\n", inlinetest); |
| ok(r2.startPosition == 0 && r2.length == 1, "got %d, %d\n", r2.startPosition, r2.length); |
| IDWriteInlineObject_Release(inlinetest); |
| |
| EXPECT_REF(inlineobj, 2); |
| |
| range.startPosition = 1; |
| range.length = 1; |
| hr = IDWriteTextLayout_SetInlineObject(layout, inlineobj, range); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| r2.startPosition = r2.length = 100; |
| hr = IDWriteTextLayout_GetInlineObject(layout, 0, &inlinetest, &r2); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(inlinetest == inlineobj, "got %p\n", inlinetest); |
| ok(r2.startPosition == 0 && r2.length == 2, "got %d, %d\n", r2.startPosition, r2.length); |
| IDWriteInlineObject_Release(inlinetest); |
| |
| EXPECT_REF(inlineobj, 2); |
| |
| range.startPosition = 1; |
| range.length = 2; |
| hr = IDWriteTextLayout_SetInlineObject(layout, inlineobj, range); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| EXPECT_REF(inlineobj, 2); |
| |
| r2.startPosition = r2.length = 100; |
| hr = IDWriteTextLayout_GetInlineObject(layout, 0, &inlinetest, &r2); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(inlinetest == inlineobj, "got %p\n", inlinetest); |
| ok(r2.startPosition == 0 && r2.length == 3, "got %d, %d\n", r2.startPosition, r2.length); |
| IDWriteInlineObject_Release(inlinetest); |
| |
| EXPECT_REF(inlineobj, 2); |
| EXPECT_REF(inlineobj2, 1); |
| |
| IDWriteTextLayout_Release(layout); |
| |
| EXPECT_REF(inlineobj, 1); |
| |
| IDWriteInlineObject_Release(inlineobj); |
| IDWriteInlineObject_Release(inlineobj2); |
| IDWriteTextFormat_Release(format); |
| IDWriteFactory_Release(factory); |
| } |
| |
| /* drawing calls sequence doesn't depend on run order, instead all runs are |
| drawn first, inline objects next and then underline/strikes */ |
| static const struct drawcall_entry draw_seq[] = { |
| { DRAW_GLYPHRUN, {'s',0}, {'r','u',0}, 1 }, |
| { DRAW_GLYPHRUN, {'r','i',0}, {'r','u',0}, 2 }, |
| { DRAW_GLYPHRUN|DRAW_EFFECT, {'n',0}, {'r','u',0}, 1 }, |
| { DRAW_GLYPHRUN, {'g',0}, {'r','u',0}, 1 }, |
| { DRAW_INLINE }, |
| { DRAW_UNDERLINE, {0}, {'r','u',0} }, |
| { DRAW_STRIKETHROUGH }, |
| { DRAW_LAST_KIND } |
| }; |
| |
| static const struct drawcall_entry draw_seq2[] = { |
| { DRAW_GLYPHRUN, {'s',0}, {'r','u',0}, 1 }, |
| { DRAW_GLYPHRUN, {'t',0}, {'r','u',0}, 1 }, |
| { DRAW_GLYPHRUN, {'r',0}, {'r','u',0}, 1 }, |
| { DRAW_GLYPHRUN, {'i',0}, {'r','u',0}, 1 }, |
| { DRAW_GLYPHRUN, {'n',0}, {'r','u',0}, 1 }, |
| { DRAW_GLYPHRUN, {'g',0}, {'r','u',0}, 1 }, |
| { DRAW_LAST_KIND } |
| }; |
| |
| static const struct drawcall_entry draw_seq3[] = { |
| { DRAW_GLYPHRUN, {0x202a,0x202c,0}, {'r','u',0}, 0 }, |
| { DRAW_GLYPHRUN, {'a','b',0}, {'r','u',0}, 2 }, |
| { DRAW_LAST_KIND } |
| }; |
| |
| static const struct drawcall_entry draw_seq4[] = { |
| { DRAW_GLYPHRUN, {'s','t','r',0}, {'r','u',0}, 3 }, |
| { DRAW_GLYPHRUN, {'i','n','g',0}, {'r','u',0}, 3 }, |
| { DRAW_STRIKETHROUGH }, |
| { DRAW_LAST_KIND } |
| }; |
| |
| static const struct drawcall_entry draw_seq5[] = { |
| { DRAW_GLYPHRUN, {'s','t',0}, {'r','u',0}, 2 }, |
| { DRAW_GLYPHRUN, {'r','i',0}, {'r','u',0}, 2 }, |
| { DRAW_GLYPHRUN, {'n','g',0}, {'r','u',0}, 2 }, |
| { DRAW_STRIKETHROUGH }, |
| { DRAW_LAST_KIND } |
| }; |
| |
| static const struct drawcall_entry empty_seq[] = { |
| { DRAW_LAST_KIND } |
| }; |
| |
| static const struct drawcall_entry draw_single_run_seq[] = { |
| { DRAW_GLYPHRUN, {'s','t','r','i','n','g',0}, {'r','u',0}, 6 }, |
| { DRAW_LAST_KIND } |
| }; |
| |
| static const struct drawcall_entry draw_reordered_run_seq[] = { |
| { DRAW_GLYPHRUN, {'1','2','3','-','5','2',0}, {'r','u',0}, 6 }, |
| { DRAW_GLYPHRUN, {0x64a,0x64f,0x633,0x627,0x648,0x650,0x64a,0}, {'r','u',0}, 7 }, |
| { DRAW_GLYPHRUN, {'7','1',0}, {'r','u',0}, 2 }, |
| { DRAW_GLYPHRUN, {'.',0}, {'r','u',0}, 1 }, |
| { DRAW_LAST_KIND } |
| }; |
| |
| static void test_Draw(void) |
| { |
| static const WCHAR str3W[] = {'1','2','3','-','5','2',0x64a,0x64f,0x633,0x627,0x648,0x650, |
| 0x64a,'7','1','.',0}; |
| static const WCHAR strW[] = {'s','t','r','i','n','g',0}; |
| static const WCHAR str2W[] = {0x202a,0x202c,'a','b',0}; |
| static const WCHAR ruW[] = {'r','u',0}; |
| IDWriteInlineObject *inlineobj; |
| struct renderer_context ctxt; |
| IDWriteTextFormat *format; |
| IDWriteTextLayout *layout; |
| DWRITE_TEXT_RANGE range; |
| IDWriteFactory *factory; |
| DWRITE_TEXT_METRICS tm; |
| DWRITE_MATRIX m; |
| HRESULT hr; |
| |
| factory = create_factory(); |
| |
| memset(&ctxt, 0, sizeof(ctxt)); |
| ctxt.snapping_disabled = TRUE; |
| |
| hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_BOLD, DWRITE_FONT_STYLE_NORMAL, |
| DWRITE_FONT_STRETCH_NORMAL, 10.0, ruW, &format); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFactory_CreateTextLayout(factory, strW, 6, format, 100.0, 100.0, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFactory_CreateEllipsisTrimmingSign(factory, format, &inlineobj); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| range.startPosition = 5; |
| range.length = 1; |
| hr = IDWriteTextLayout_SetStrikethrough(layout, TRUE, range); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| range.startPosition = 1; |
| range.length = 1; |
| hr = IDWriteTextLayout_SetInlineObject(layout, inlineobj, range); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| range.startPosition = 4; |
| range.length = 1; |
| hr = IDWriteTextLayout_SetDrawingEffect(layout, (IUnknown*)inlineobj, range); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| range.startPosition = 0; |
| range.length = 1; |
| hr = IDWriteTextLayout_SetUnderline(layout, TRUE, range); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| flush_sequence(sequences, RENDERER_ID); |
| hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0, 0.0); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok_sequence(sequences, RENDERER_ID, draw_seq, "draw test", FALSE); |
| IDWriteTextLayout_Release(layout); |
| |
| /* with reduced width DrawGlyphRun() is called for every line */ |
| hr = IDWriteFactory_CreateTextLayout(factory, strW, 6, format, 5.0, 100.0, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| flush_sequence(sequences, RENDERER_ID); |
| hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0, 0.0); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok_sequence(sequences, RENDERER_ID, draw_seq2, "draw test 2", TRUE); |
| hr = IDWriteTextLayout_GetMetrics(layout, &tm); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| todo_wine |
| ok(tm.lineCount == 6, "got %u\n", tm.lineCount); |
| IDWriteTextLayout_Release(layout); |
| |
| /* string with control characters */ |
| hr = IDWriteFactory_CreateTextLayout(factory, str2W, 4, format, 500.0, 100.0, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| flush_sequence(sequences, RENDERER_ID); |
| hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0, 0.0); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok_sequence(sequences, RENDERER_ID, draw_seq3, "draw test 3", FALSE); |
| IDWriteTextLayout_Release(layout); |
| |
| /* strikethrough splits ranges from renderer point of view, but doesn't break |
| shaping */ |
| hr = IDWriteFactory_CreateTextLayout(factory, strW, 6, format, 500.0, 100.0, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| flush_sequence(sequences, RENDERER_ID); |
| |
| range.startPosition = 0; |
| range.length = 3; |
| hr = IDWriteTextLayout_SetStrikethrough(layout, TRUE, range); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0, 0.0); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok_sequence(sequences, RENDERER_ID, draw_seq4, "draw test 4", FALSE); |
| IDWriteTextLayout_Release(layout); |
| |
| /* strikethrough somewhere in the middle */ |
| hr = IDWriteFactory_CreateTextLayout(factory, strW, 6, format, 500.0, 100.0, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| flush_sequence(sequences, RENDERER_ID); |
| |
| range.startPosition = 2; |
| range.length = 2; |
| hr = IDWriteTextLayout_SetStrikethrough(layout, TRUE, range); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0, 0.0); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok_sequence(sequences, RENDERER_ID, draw_seq5, "draw test 5", FALSE); |
| IDWriteTextLayout_Release(layout); |
| |
| /* empty string */ |
| hr = IDWriteFactory_CreateTextLayout(factory, strW, 0, format, 500.0, 100.0, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| flush_sequence(sequences, RENDERER_ID); |
| hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0, 0.0); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok_sequence(sequences, RENDERER_ID, empty_seq, "draw test 6", FALSE); |
| IDWriteTextLayout_Release(layout); |
| |
| ctxt.gdicompat = TRUE; |
| ctxt.use_gdi_natural = TRUE; |
| |
| /* different parameter combinations with gdi-compatible layout */ |
| hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, strW, 6, format, 100.0, 100.0, 1.0, NULL, TRUE, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| flush_sequence(sequences, RENDERER_ID); |
| hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0, 0.0); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok_sequence(sequences, RENDERER_ID, draw_single_run_seq, "draw test 7", FALSE); |
| |
| /* text alignment keeps pixel-aligned origin */ |
| hr = IDWriteTextLayout_GetMetrics(layout, &tm); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(tm.width == floorf(tm.width), "got %f\n", tm.width); |
| |
| hr = IDWriteTextLayout_SetMaxWidth(layout, tm.width + 3.0); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| hr = IDWriteTextLayout_SetTextAlignment(layout, DWRITE_TEXT_ALIGNMENT_CENTER); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| ctxt.originX = ctxt.originY = 0.0; |
| flush_sequence(sequences, RENDERER_ID); |
| hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0, 0.0); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok_sequence(sequences, RENDERER_ID, draw_single_run_seq, "draw test 7", FALSE); |
| ok(ctxt.originX != 0.0 && ctxt.originX == floorf(ctxt.originX), "got %f\n", ctxt.originX); |
| |
| IDWriteTextLayout_Release(layout); |
| |
| ctxt.gdicompat = TRUE; |
| ctxt.use_gdi_natural = FALSE; |
| |
| hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, strW, 6, format, 100.0, 100.0, 1.0, NULL, FALSE, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| flush_sequence(sequences, RENDERER_ID); |
| hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0, 0.0); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok_sequence(sequences, RENDERER_ID, draw_single_run_seq, "draw test 8", FALSE); |
| IDWriteTextLayout_Release(layout); |
| |
| ctxt.gdicompat = TRUE; |
| ctxt.use_gdi_natural = TRUE; |
| |
| m.m11 = m.m22 = 2.0; |
| m.m12 = m.m21 = m.dx = m.dy = 0.0; |
| hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, strW, 6, format, 100.0, 100.0, 1.0, &m, TRUE, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| flush_sequence(sequences, RENDERER_ID); |
| hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0, 0.0); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok_sequence(sequences, RENDERER_ID, draw_single_run_seq, "draw test 9", FALSE); |
| IDWriteTextLayout_Release(layout); |
| |
| ctxt.gdicompat = TRUE; |
| ctxt.use_gdi_natural = FALSE; |
| |
| m.m11 = m.m22 = 2.0; |
| m.m12 = m.m21 = m.dx = m.dy = 0.0; |
| hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, strW, 6, format, 100.0, 100.0, 1.0, &m, FALSE, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| flush_sequence(sequences, RENDERER_ID); |
| hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0, 0.0); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok_sequence(sequences, RENDERER_ID, draw_single_run_seq, "draw test 10", FALSE); |
| IDWriteTextLayout_Release(layout); |
| |
| IDWriteInlineObject_Release(inlineobj); |
| |
| /* text that triggers bidi run reordering */ |
| hr = IDWriteFactory_CreateTextLayout(factory, str3W, lstrlenW(str3W), format, 1000.0f, 100.0f, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| ctxt.gdicompat = FALSE; |
| ctxt.use_gdi_natural = FALSE; |
| ctxt.snapping_disabled = TRUE; |
| |
| flush_sequence(sequences, RENDERER_ID); |
| hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0f, 0.0f); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok_sequence(sequences, RENDERER_ID, draw_reordered_run_seq, "draw test 11", FALSE); |
| |
| IDWriteTextLayout_Release(layout); |
| |
| IDWriteTextFormat_Release(format); |
| IDWriteFactory_Release(factory); |
| } |
| |
| static void test_typography(void) |
| { |
| DWRITE_FONT_FEATURE feature; |
| IDWriteTypography *typography; |
| IDWriteFactory *factory; |
| UINT32 count; |
| HRESULT hr; |
| |
| factory = create_factory(); |
| |
| hr = IDWriteFactory_CreateTypography(factory, &typography); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| feature.nameTag = DWRITE_FONT_FEATURE_TAG_KERNING; |
| feature.parameter = 1; |
| hr = IDWriteTypography_AddFontFeature(typography, feature); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| count = IDWriteTypography_GetFontFeatureCount(typography); |
| ok(count == 1, "got %u\n", count); |
| |
| /* duplicated features work just fine */ |
| feature.nameTag = DWRITE_FONT_FEATURE_TAG_KERNING; |
| feature.parameter = 0; |
| hr = IDWriteTypography_AddFontFeature(typography, feature); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| count = IDWriteTypography_GetFontFeatureCount(typography); |
| ok(count == 2, "got %u\n", count); |
| |
| memset(&feature, 0xcc, sizeof(feature)); |
| hr = IDWriteTypography_GetFontFeature(typography, 0, &feature); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(feature.nameTag == DWRITE_FONT_FEATURE_TAG_KERNING, "got tag %x\n", feature.nameTag); |
| ok(feature.parameter == 1, "got %u\n", feature.parameter); |
| |
| memset(&feature, 0xcc, sizeof(feature)); |
| hr = IDWriteTypography_GetFontFeature(typography, 1, &feature); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(feature.nameTag == DWRITE_FONT_FEATURE_TAG_KERNING, "got tag %x\n", feature.nameTag); |
| ok(feature.parameter == 0, "got %u\n", feature.parameter); |
| |
| hr = IDWriteTypography_GetFontFeature(typography, 2, &feature); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| |
| /* duplicated with same parameter value */ |
| feature.nameTag = DWRITE_FONT_FEATURE_TAG_KERNING; |
| feature.parameter = 0; |
| hr = IDWriteTypography_AddFontFeature(typography, feature); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| count = IDWriteTypography_GetFontFeatureCount(typography); |
| ok(count == 3, "got %u\n", count); |
| |
| memset(&feature, 0xcc, sizeof(feature)); |
| hr = IDWriteTypography_GetFontFeature(typography, 2, &feature); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(feature.nameTag == DWRITE_FONT_FEATURE_TAG_KERNING, "got tag %x\n", feature.nameTag); |
| ok(feature.parameter == 0, "got %u\n", feature.parameter); |
| |
| IDWriteTypography_Release(typography); |
| IDWriteFactory_Release(factory); |
| } |
| |
| static void test_GetClusterMetrics(void) |
| { |
| static const WCHAR str_white_spaceW[] = { |
| /* BK - FORM FEED, LINE TABULATION, LINE SEP, PARA SEP */ 0xc, 0xb, 0x2028, 0x2029, |
| /* ZW - ZERO WIDTH SPACE */ 0x200b, |
| /* SP - SPACE */ 0x20 |
| }; |
| static const WCHAR str5W[] = {'a','\r','b','\n','c','\n','\r','d','\r','\n','e',0xb,'f',0xc, |
| 'g',0x0085,'h',0x2028,'i',0x2029,0xad,0xa,0}; |
| static const WCHAR str3W[] = {0x2066,')',')',0x661,'(',0x627,')',0}; |
| static const WCHAR str2W[] = {0x202a,0x202c,'a',0}; |
| static const WCHAR strW[] = {'a','b','c','d',0}; |
| static const WCHAR str4W[] = {'a',' ',0}; |
| static const WCHAR str6W[] = {'a',' ','b',0}; |
| DWRITE_INLINE_OBJECT_METRICS inline_metrics; |
| DWRITE_CLUSTER_METRICS metrics[22]; |
| DWRITE_TEXT_METRICS text_metrics; |
| DWRITE_TRIMMING trimming_options; |
| IDWriteTextLayout1 *layout1; |
| IDWriteInlineObject *trimm; |
| IDWriteTextFormat *format; |
| IDWriteTextLayout *layout; |
| DWRITE_LINE_METRICS line; |
| DWRITE_TEXT_RANGE range; |
| IDWriteFactory *factory; |
| UINT32 count, i; |
| FLOAT width; |
| HRESULT hr; |
| |
| factory = create_factory(); |
| |
| hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, |
| DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFactory_CreateTextLayout(factory, str3W, 7, format, 1000.0, 1000.0, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| hr = IDWriteTextLayout_GetClusterMetrics(layout, NULL, 0, &count); |
| ok(hr == E_NOT_SUFFICIENT_BUFFER, "got 0x%08x\n", hr); |
| ok(count == 7, "got %u\n", count); |
| IDWriteTextLayout_Release(layout); |
| |
| hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 1000.0, 1000.0, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| count = 0; |
| hr = IDWriteTextLayout_GetClusterMetrics(layout, NULL, 0, &count); |
| ok(hr == E_NOT_SUFFICIENT_BUFFER, "got 0x%08x\n", hr); |
| ok(count == 4, "got %u\n", count); |
| |
| /* check every cluster width */ |
| count = 0; |
| hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, sizeof(metrics)/sizeof(metrics[0]), &count); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(count == 4, "got %u\n", count); |
| for (i = 0; i < count; i++) { |
| ok(metrics[i].width > 0.0, "%u: got width %.2f\n", i, metrics[i].width); |
| ok(metrics[i].length == 1, "%u: got length %u\n", i, metrics[i].length); |
| } |
| |
| /* apply spacing and check widths again */ |
| if (IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextLayout1, (void**)&layout1) == S_OK) { |
| DWRITE_CLUSTER_METRICS metrics2[4]; |
| FLOAT leading, trailing, min_advance; |
| DWRITE_TEXT_RANGE r; |
| |
| leading = trailing = min_advance = 2.0; |
| hr = IDWriteTextLayout1_GetCharacterSpacing(layout1, 500, &leading, &trailing, |
| &min_advance, &r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(leading == 0.0 && trailing == 0.0 && min_advance == 0.0, |
| "got %.2f, %.2f, %.2f\n", leading, trailing, min_advance); |
| ok(r.startPosition == 0 && r.length == ~0u, "got %u, %u\n", r.startPosition, r.length); |
| |
| leading = trailing = min_advance = 2.0; |
| hr = IDWriteTextLayout1_GetCharacterSpacing(layout1, 0, &leading, &trailing, |
| &min_advance, NULL); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(leading == 0.0 && trailing == 0.0 && min_advance == 0.0, |
| "got %.2f, %.2f, %.2f\n", leading, trailing, min_advance); |
| |
| r.startPosition = 0; |
| r.length = 4; |
| hr = IDWriteTextLayout1_SetCharacterSpacing(layout1, 10.0, 15.0, 0.0, r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| count = 0; |
| hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics2, sizeof(metrics2)/sizeof(metrics2[0]), &count); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(count == 4, "got %u\n", count); |
| for (i = 0; i < count; i++) { |
| todo_wine |
| ok(metrics2[i].width > metrics[i].width, "%u: got width %.2f, was %.2f\n", i, metrics2[i].width, |
| metrics[i].width); |
| ok(metrics2[i].length == 1, "%u: got length %u\n", i, metrics2[i].length); |
| } |
| |
| /* back to defaults */ |
| r.startPosition = 0; |
| r.length = 4; |
| hr = IDWriteTextLayout1_SetCharacterSpacing(layout1, 0.0, 0.0, 0.0, r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| /* negative advance limit */ |
| r.startPosition = 0; |
| r.length = 4; |
| hr = IDWriteTextLayout1_SetCharacterSpacing(layout1, 0.0, 0.0, -10.0, r); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| |
| IDWriteTextLayout1_Release(layout1); |
| } |
| else |
| win_skip("IDWriteTextLayout1 is not supported, cluster spacing test skipped.\n"); |
| |
| hr = IDWriteFactory_CreateEllipsisTrimmingSign(factory, format, &trimm); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| range.startPosition = 0; |
| range.length = 2; |
| hr = IDWriteTextLayout_SetInlineObject(layout, trimm, range); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| /* inline object takes a separate cluster, replaced codepoints number doesn't matter */ |
| count = 0; |
| hr = IDWriteTextLayout_GetClusterMetrics(layout, NULL, 0, &count); |
| ok(hr == E_NOT_SUFFICIENT_BUFFER, "got 0x%08x\n", hr); |
| ok(count == 3, "got %u\n", count); |
| |
| count = 0; |
| memset(&metrics, 0, sizeof(metrics)); |
| hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 1, &count); |
| ok(hr == E_NOT_SUFFICIENT_BUFFER, "got 0x%08x\n", hr); |
| ok(count == 3, "got %u\n", count); |
| ok(metrics[0].length == 2, "got %u\n", metrics[0].length); |
| |
| hr = IDWriteInlineObject_GetMetrics(trimm, &inline_metrics); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(inline_metrics.width > 0.0 && inline_metrics.width == metrics[0].width, "got %.2f, expected %.2f\n", |
| inline_metrics.width, metrics[0].width); |
| |
| IDWriteTextLayout_Release(layout); |
| |
| /* text with non-visual control codes */ |
| hr = IDWriteFactory_CreateTextLayout(factory, str2W, 3, format, 1000.0, 1000.0, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| /* bidi control codes take a separate cluster */ |
| count = 0; |
| memset(metrics, 0, sizeof(metrics)); |
| hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 3, &count); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(count == 3, "got %u\n", count); |
| |
| ok(metrics[0].width == 0.0, "got %.2f\n", metrics[0].width); |
| ok(metrics[0].length == 1, "got %d\n", metrics[0].length); |
| ok(metrics[0].canWrapLineAfter == 0, "got %d\n", metrics[0].canWrapLineAfter); |
| ok(metrics[0].isWhitespace == 0, "got %d\n", metrics[0].isWhitespace); |
| ok(metrics[0].isNewline == 0, "got %d\n", metrics[0].isNewline); |
| ok(metrics[0].isSoftHyphen == 0, "got %d\n", metrics[0].isSoftHyphen); |
| ok(metrics[0].isRightToLeft == 0, "got %d\n", metrics[0].isRightToLeft); |
| |
| ok(metrics[1].width == 0.0, "got %.2f\n", metrics[1].width); |
| ok(metrics[1].length == 1, "got %d\n", metrics[1].length); |
| ok(metrics[1].canWrapLineAfter == 0, "got %d\n", metrics[1].canWrapLineAfter); |
| ok(metrics[1].isWhitespace == 0, "got %d\n", metrics[1].isWhitespace); |
| ok(metrics[1].isNewline == 0, "got %d\n", metrics[1].isNewline); |
| ok(metrics[1].isSoftHyphen == 0, "got %d\n", metrics[1].isSoftHyphen); |
| ok(metrics[1].isRightToLeft == 0, "got %d\n", metrics[1].isRightToLeft); |
| |
| ok(metrics[2].width > 0.0, "got %.2f\n", metrics[2].width); |
| ok(metrics[2].length == 1, "got %d\n", metrics[2].length); |
| ok(metrics[2].canWrapLineAfter == 1, "got %d\n", metrics[2].canWrapLineAfter); |
| ok(metrics[2].isWhitespace == 0, "got %d\n", metrics[2].isWhitespace); |
| ok(metrics[2].isNewline == 0, "got %d\n", metrics[2].isNewline); |
| ok(metrics[2].isSoftHyphen == 0, "got %d\n", metrics[2].isSoftHyphen); |
| ok(metrics[2].isRightToLeft == 0, "got %d\n", metrics[2].isRightToLeft); |
| |
| IDWriteTextLayout_Release(layout); |
| |
| /* single inline object that fails to report its metrics */ |
| hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 1000.0, 1000.0, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| range.startPosition = 0; |
| range.length = 4; |
| hr = IDWriteTextLayout_SetInlineObject(layout, &testinlineobj, range); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| count = 0; |
| memset(metrics, 0, sizeof(metrics)); |
| hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 3, &count); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(count == 1, "got %u\n", count); |
| |
| /* object sets a width to 123.0, but returns failure from GetMetrics() */ |
| ok(metrics[0].width == 0.0, "got %.2f\n", metrics[0].width); |
| ok(metrics[0].length == 4, "got %d\n", metrics[0].length); |
| ok(metrics[0].canWrapLineAfter == 1, "got %d\n", metrics[0].canWrapLineAfter); |
| ok(metrics[0].isWhitespace == 0, "got %d\n", metrics[0].isWhitespace); |
| ok(metrics[0].isNewline == 0, "got %d\n", metrics[0].isNewline); |
| ok(metrics[0].isSoftHyphen == 0, "got %d\n", metrics[0].isSoftHyphen); |
| ok(metrics[0].isRightToLeft == 0, "got %d\n", metrics[0].isRightToLeft); |
| |
| /* now set two inline object for [0,1] and [2,3], both fail to report break conditions */ |
| range.startPosition = 2; |
| range.length = 2; |
| hr = IDWriteTextLayout_SetInlineObject(layout, &testinlineobj2, range); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| count = 0; |
| memset(metrics, 0, sizeof(metrics)); |
| hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 3, &count); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(count == 2, "got %u\n", count); |
| |
| ok(metrics[0].width == 0.0, "got %.2f\n", metrics[0].width); |
| ok(metrics[0].length == 2, "got %d\n", metrics[0].length); |
| ok(metrics[0].canWrapLineAfter == 0, "got %d\n", metrics[0].canWrapLineAfter); |
| ok(metrics[0].isWhitespace == 0, "got %d\n", metrics[0].isWhitespace); |
| ok(metrics[0].isNewline == 0, "got %d\n", metrics[0].isNewline); |
| ok(metrics[0].isSoftHyphen == 0, "got %d\n", metrics[0].isSoftHyphen); |
| ok(metrics[0].isRightToLeft == 0, "got %d\n", metrics[0].isRightToLeft); |
| |
| ok(metrics[1].width == 0.0, "got %.2f\n", metrics[1].width); |
| ok(metrics[1].length == 2, "got %d\n", metrics[1].length); |
| ok(metrics[1].canWrapLineAfter == 1, "got %d\n", metrics[1].canWrapLineAfter); |
| ok(metrics[1].isWhitespace == 0, "got %d\n", metrics[1].isWhitespace); |
| ok(metrics[1].isNewline == 0, "got %d\n", metrics[1].isNewline); |
| ok(metrics[1].isSoftHyphen == 0, "got %d\n", metrics[1].isSoftHyphen); |
| ok(metrics[1].isRightToLeft == 0, "got %d\n", metrics[1].isRightToLeft); |
| |
| IDWriteTextLayout_Release(layout); |
| |
| /* zero length string */ |
| hr = IDWriteFactory_CreateTextLayout(factory, strW, 0, format, 1000.0, 1000.0, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| count = 1; |
| memset(metrics, 0, sizeof(metrics)); |
| hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 3, &count); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(count == 0, "got %u\n", count); |
| IDWriteTextLayout_Release(layout); |
| |
| /* whitespace */ |
| hr = IDWriteFactory_CreateTextLayout(factory, str4W, 2, format, 1000.0, 1000.0, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| count = 0; |
| memset(metrics, 0, sizeof(metrics)); |
| hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 2, &count); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(count == 2, "got %u\n", count); |
| ok(metrics[0].isWhitespace == 0, "got %d\n", metrics[0].isWhitespace); |
| ok(metrics[0].canWrapLineAfter == 0, "got %d\n", metrics[0].canWrapLineAfter); |
| ok(metrics[1].isWhitespace == 1, "got %d\n", metrics[1].isWhitespace); |
| ok(metrics[1].canWrapLineAfter == 1, "got %d\n", metrics[1].canWrapLineAfter); |
| IDWriteTextLayout_Release(layout); |
| |
| /* layout is fully covered by inline object with after condition DWRITE_BREAK_CONDITION_MAY_NOT_BREAK */ |
| hr = IDWriteFactory_CreateTextLayout(factory, str4W, 2, format, 1000.0, 1000.0, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| range.startPosition = 0; |
| range.length = ~0u; |
| hr = IDWriteTextLayout_SetInlineObject(layout, &testinlineobj3, range); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| count = 0; |
| memset(metrics, 0, sizeof(metrics)); |
| hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 2, &count); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(count == 1, "got %u\n", count); |
| ok(metrics[0].canWrapLineAfter == 1, "got %d\n", metrics[0].canWrapLineAfter); |
| |
| IDWriteTextLayout_Release(layout); |
| |
| /* compare natural cluster width with gdi layout */ |
| hr = IDWriteFactory_CreateTextLayout(factory, str4W, 1, format, 100.0, 100.0, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| count = 0; |
| memset(metrics, 0, sizeof(metrics)); |
| hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 1, &count); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(count == 1, "got %u\n", count); |
| ok(metrics[0].width != floorf(metrics[0].width), "got %f\n", metrics[0].width); |
| |
| IDWriteTextLayout_Release(layout); |
| |
| hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, str4W, 1, format, 100.0, 100.0, 1.0, NULL, FALSE, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| count = 0; |
| memset(metrics, 0, sizeof(metrics)); |
| hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 1, &count); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(count == 1, "got %u\n", count); |
| ok(metrics[0].width == floorf(metrics[0].width), "got %f\n", metrics[0].width); |
| |
| IDWriteTextLayout_Release(layout); |
| |
| /* isNewline tests */ |
| hr = IDWriteFactory_CreateTextLayout(factory, str5W, lstrlenW(str5W), format, 100.0f, 200.0f, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| count = 0; |
| memset(metrics, 0, sizeof(metrics)); |
| hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, sizeof(metrics)/sizeof(metrics[0]), &count); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(count == 22, "got %u\n", count); |
| |
| ok(metrics[1].isNewline == 1, "got %d\n", metrics[1].isNewline); |
| ok(metrics[3].isNewline == 1, "got %d\n", metrics[3].isNewline); |
| ok(metrics[5].isNewline == 1, "got %d\n", metrics[5].isNewline); |
| ok(metrics[6].isNewline == 1, "got %d\n", metrics[6].isNewline); |
| ok(metrics[9].isNewline == 1, "got %d\n", metrics[9].isNewline); |
| ok(metrics[11].isNewline == 1, "got %d\n", metrics[11].isNewline); |
| ok(metrics[13].isNewline == 1, "got %d\n", metrics[13].isNewline); |
| ok(metrics[15].isNewline == 1, "got %d\n", metrics[15].isNewline); |
| ok(metrics[17].isNewline == 1, "got %d\n", metrics[17].isNewline); |
| ok(metrics[19].isNewline == 1, "got %d\n", metrics[19].isNewline); |
| ok(metrics[21].isNewline == 1, "got %d\n", metrics[21].isNewline); |
| |
| ok(metrics[0].isNewline == 0, "got %d\n", metrics[0].isNewline); |
| ok(metrics[2].isNewline == 0, "got %d\n", metrics[2].isNewline); |
| ok(metrics[4].isNewline == 0, "got %d\n", metrics[4].isNewline); |
| ok(metrics[7].isNewline == 0, "got %d\n", metrics[7].isNewline); |
| ok(metrics[8].isNewline == 0, "got %d\n", metrics[8].isNewline); |
| ok(metrics[10].isNewline == 0, "got %d\n", metrics[10].isNewline); |
| ok(metrics[12].isNewline == 0, "got %d\n", metrics[12].isNewline); |
| ok(metrics[14].isNewline == 0, "got %d\n", metrics[14].isNewline); |
| ok(metrics[16].isNewline == 0, "got %d\n", metrics[16].isNewline); |
| ok(metrics[18].isNewline == 0, "got %d\n", metrics[18].isNewline); |
| ok(metrics[20].isNewline == 0, "got %d\n", metrics[20].isNewline); |
| |
| for (i = 0; i < count; i++) { |
| ok(metrics[i].length == 1, "%d: got %d\n", i, metrics[i].length); |
| ok(metrics[i].isSoftHyphen == (i == count - 2), "%d: got %d\n", i, metrics[i].isSoftHyphen); |
| if (metrics[i].isSoftHyphen) |
| ok(!metrics[i].isWhitespace, "%u: got %d\n", i, metrics[i].isWhitespace); |
| if (metrics[i].isNewline) { |
| ok(metrics[i].width == 0.0f, "%u: got width %f\n", i, metrics[i].width); |
| ok(metrics[i].isWhitespace == 1, "%u: got %d\n", i, metrics[i].isWhitespace); |
| ok(metrics[i].canWrapLineAfter == 1, "%u: got %d\n", i, metrics[i].canWrapLineAfter); |
| } |
| } |
| |
| IDWriteTextLayout_Release(layout); |
| |
| /* Test whitespace resolution from linebreaking classes BK, ZW, and SP */ |
| hr = IDWriteFactory_CreateTextLayout(factory, str_white_spaceW, sizeof(str_white_spaceW)/sizeof(WCHAR), format, |
| 100.0f, 200.0f, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| count = 0; |
| memset(metrics, 0, sizeof(metrics)); |
| hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 20, &count); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(count == 6, "got %u\n", count); |
| |
| ok(metrics[0].isWhitespace == 1, "got %d\n", metrics[0].isWhitespace); |
| ok(metrics[1].isWhitespace == 1, "got %d\n", metrics[1].isWhitespace); |
| ok(metrics[2].isWhitespace == 1, "got %d\n", metrics[2].isWhitespace); |
| ok(metrics[3].isWhitespace == 1, "got %d\n", metrics[3].isWhitespace); |
| ok(metrics[4].isWhitespace == 0, "got %d\n", metrics[4].isWhitespace); |
| ok(metrics[5].isWhitespace == 1, "got %d\n", metrics[5].isWhitespace); |
| |
| IDWriteTextLayout_Release(layout); |
| |
| /* trigger line trimming */ |
| hr = IDWriteFactory_CreateTextLayout(factory, strW, lstrlenW(strW), format, 100.0f, 200.0f, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| count = 0; |
| memset(metrics, 0, sizeof(metrics)); |
| hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 4, &count); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(count == 4, "got %u\n", count); |
| |
| hr = IDWriteTextLayout_GetMetrics(layout, &text_metrics); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| width = metrics[0].width + inline_metrics.width; |
| ok(width < text_metrics.width, "unexpected trimming sign width\n"); |
| |
| /* enable trimming, reduce layout width so only first cluster and trimming sign fits */ |
| trimming_options.granularity = DWRITE_TRIMMING_GRANULARITY_CHARACTER; |
| trimming_options.delimiter = 0; |
| trimming_options.delimiterCount = 0; |
| hr = IDWriteTextLayout_SetTrimming(layout, &trimming_options, trimm); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextLayout_SetMaxWidth(layout, width); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| count = 0; |
| memset(metrics, 0, sizeof(metrics)); |
| hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 4, &count); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(count == 4, "got %u\n", count); |
| |
| hr = IDWriteTextLayout_GetLineMetrics(layout, &line, 1, &count); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(count == 1, "got %u\n", count); |
| ok(line.length == 4, "got %u\n", line.length); |
| ok(line.isTrimmed, "got %d\n", line.isTrimmed); |
| |
| IDWriteTextLayout_Release(layout); |
| |
| /* NO_WRAP, check cluster wrapping attribute. */ |
| hr = IDWriteTextFormat_SetWordWrapping(format, DWRITE_WORD_WRAPPING_NO_WRAP); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFactory_CreateTextLayout(factory, str6W, lstrlenW(str6W), format, 1000.0f, 200.0f, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| count = 0; |
| memset(metrics, 0, sizeof(metrics)); |
| hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 3, &count); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(count == 3, "got %u\n", count); |
| |
| ok(metrics[0].canWrapLineAfter == 0, "got %d\n", metrics[0].canWrapLineAfter); |
| ok(metrics[1].canWrapLineAfter == 1, "got %d\n", metrics[1].canWrapLineAfter); |
| ok(metrics[2].canWrapLineAfter == 1, "got %d\n", metrics[2].canWrapLineAfter); |
| |
| IDWriteTextLayout_Release(layout); |
| |
| IDWriteInlineObject_Release(trimm); |
| IDWriteTextFormat_Release(format); |
| IDWriteFactory_Release(factory); |
| } |
| |
| static void test_SetLocaleName(void) |
| { |
| static const WCHAR eNuSW[] = {'e','N','-','u','S',0}; |
| static const WCHAR strW[] = {'a','b','c','d',0}; |
| WCHAR buffW[LOCALE_NAME_MAX_LENGTH+sizeof(strW)/sizeof(WCHAR)]; |
| IDWriteTextFormat *format, *format2; |
| IDWriteTextLayout *layout; |
| DWRITE_TEXT_RANGE range; |
| IDWriteFactory *factory; |
| HRESULT hr; |
| |
| factory = create_factory(); |
| |
| /* create format with mixed case locale name, get it back */ |
| hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, |
| DWRITE_FONT_STRETCH_NORMAL, 10.0, eNuSW, &format); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextFormat_GetLocaleName(format, buffW, sizeof(buffW)/sizeof(buffW[0])); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(!lstrcmpW(buffW, enusW), "got %s\n", wine_dbgstr_w(buffW)); |
| |
| hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 1000.0, 1000.0, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextFormat, (void**)&format2); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextFormat_GetLocaleName(format2, buffW, sizeof(buffW)/sizeof(buffW[0])); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(!lstrcmpW(buffW, enusW), "got %s\n", wine_dbgstr_w(buffW)); |
| |
| hr = IDWriteTextLayout_GetLocaleName(layout, 0, buffW, sizeof(buffW)/sizeof(buffW[0]), NULL); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(!lstrcmpW(buffW, enusW), "got %s\n", wine_dbgstr_w(buffW)); |
| |
| IDWriteTextFormat_Release(format2); |
| IDWriteTextLayout_Release(layout); |
| IDWriteTextFormat_Release(format); |
| |
| hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, |
| DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 1000.0, 1000.0, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| range.startPosition = 0; |
| range.length = 1; |
| hr = IDWriteTextLayout_SetLocaleName(layout, enusW, range); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextLayout_SetLocaleName(layout, NULL, range); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| |
| /* invalid locale name is allowed */ |
| hr = IDWriteTextLayout_SetLocaleName(layout, strW, range); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextLayout_GetLocaleName(layout, 0, NULL, 0, NULL); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| |
| if (0) /* crashes on native */ |
| hr = IDWriteTextLayout_GetLocaleName(layout, 0, NULL, 1, NULL); |
| |
| buffW[0] = 0; |
| range.length = 0; |
| hr = IDWriteTextLayout_GetLocaleName(layout, 0, buffW, sizeof(buffW)/sizeof(WCHAR), &range); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(!lstrcmpW(buffW, strW), "got %s\n", wine_dbgstr_w(buffW)); |
| ok(range.startPosition == 0 && range.length == 1, "got %u,%u\n", range.startPosition, range.length); |
| |
| /* get with a shorter buffer */ |
| buffW[0] = 0xa; |
| hr = IDWriteTextLayout_GetLocaleName(layout, 0, buffW, 1, NULL); |
| ok(hr == E_NOT_SUFFICIENT_BUFFER, "got 0x%08x\n", hr); |
| ok(buffW[0] == 0, "got %x\n", buffW[0]); |
| |
| /* name is too long */ |
| lstrcpyW(buffW, strW); |
| while (lstrlenW(buffW) <= LOCALE_NAME_MAX_LENGTH) |
| lstrcatW(buffW, strW); |
| |
| range.startPosition = 0; |
| range.length = 1; |
| hr = IDWriteTextLayout_SetLocaleName(layout, buffW, range); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| |
| buffW[0] = 0; |
| hr = IDWriteTextLayout_GetLocaleName(layout, 0, buffW, sizeof(buffW)/sizeof(WCHAR), NULL); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(!lstrcmpW(buffW, strW), "got %s\n", wine_dbgstr_w(buffW)); |
| |
| /* set initial locale name for whole text, except with a different casing */ |
| range.startPosition = 0; |
| range.length = 4; |
| hr = IDWriteTextLayout_SetLocaleName(layout, eNuSW, range); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| buffW[0] = 0; |
| range.length = 0; |
| hr = IDWriteTextLayout_GetLocaleName(layout, 0, buffW, sizeof(buffW)/sizeof(WCHAR), &range); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(!lstrcmpW(buffW, enusW), "got %s\n", wine_dbgstr_w(buffW)); |
| ok((range.startPosition == 0 && range.length == ~0u) || |
| broken(range.startPosition == 0 && range.length == 4) /* vista/win7 */, "got %u,%u\n", range.startPosition, range.length); |
| |
| /* check what's returned for positions after the text */ |
| buffW[0] = 0; |
| range.length = 0; |
| hr = IDWriteTextLayout_GetLocaleName(layout, 100, buffW, sizeof(buffW)/sizeof(WCHAR), &range); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(!lstrcmpW(buffW, enusW), "got %s\n", wine_dbgstr_w(buffW)); |
| ok((range.startPosition == 0 && range.length == ~0u) || |
| broken(range.startPosition == 4 && range.length == ~0u-4) /* vista/win7 */, "got %u,%u\n", range.startPosition, range.length); |
| |
| IDWriteTextLayout_Release(layout); |
| IDWriteTextFormat_Release(format); |
| IDWriteFactory_Release(factory); |
| } |
| |
| static void test_SetPairKerning(void) |
| { |
| static const WCHAR strW[] = {'a','e',0x0300,'d',0}; /* accent grave */ |
| DWRITE_CLUSTER_METRICS clusters[4]; |
| IDWriteTextLayout1 *layout1; |
| IDWriteTextFormat *format; |
| IDWriteTextLayout *layout; |
| DWRITE_TEXT_RANGE range; |
| IDWriteFactory *factory; |
| BOOL kerning; |
| UINT32 count; |
| HRESULT hr; |
| |
| factory = create_factory(); |
| |
| hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, |
| DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 1000.0, 1000.0, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| IDWriteTextFormat_Release(format); |
| |
| hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextLayout1, (void**)&layout1); |
| IDWriteTextLayout_Release(layout); |
| |
| if (hr != S_OK) { |
| win_skip("SetPairKerning() is not supported.\n"); |
| IDWriteFactory_Release(factory); |
| return; |
| } |
| |
| if (0) { /* crashes on native */ |
| hr = IDWriteTextLayout1_GetPairKerning(layout1, 0, NULL, NULL); |
| hr = IDWriteTextLayout1_GetPairKerning(layout1, 0, NULL, &range); |
| } |
| |
| hr = IDWriteTextLayout1_GetPairKerning(layout1, 0, &kerning, NULL); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| range.startPosition = 0; |
| range.length = 0; |
| kerning = TRUE; |
| hr = IDWriteTextLayout1_GetPairKerning(layout1, 0, &kerning, &range); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(!kerning, "got %d\n", kerning); |
| ok(range.length == ~0u, "got %u\n", range.length); |
| |
| count = 0; |
| hr = IDWriteTextLayout1_GetClusterMetrics(layout1, clusters, 4, &count); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| todo_wine |
| ok(count == 3, "got %u\n", count); |
| if (count == 3) { |
| ok(clusters[0].length == 1, "got %u\n", clusters[0].length); |
| ok(clusters[1].length == 2, "got %u\n", clusters[1].length); |
| ok(clusters[2].length == 1, "got %u\n", clusters[2].length); |
| } |
| /* pair kerning flag participates in itemization - combining characters |
| breaks */ |
| range.startPosition = 0; |
| range.length = 2; |
| hr = IDWriteTextLayout1_SetPairKerning(layout1, 2, range); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| kerning = FALSE; |
| hr = IDWriteTextLayout1_GetPairKerning(layout1, 0, &kerning, &range); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(kerning == TRUE, "got %d\n", kerning); |
| |
| count = 0; |
| hr = IDWriteTextLayout1_GetClusterMetrics(layout1, clusters, 4, &count); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(count == 4, "got %u\n", count); |
| ok(clusters[0].length == 1, "got %u\n", clusters[0].length); |
| ok(clusters[1].length == 1, "got %u\n", clusters[1].length); |
| ok(clusters[2].length == 1, "got %u\n", clusters[2].length); |
| ok(clusters[3].length == 1, "got %u\n", clusters[3].length); |
| |
| IDWriteTextLayout1_Release(layout1); |
| IDWriteFactory_Release(factory); |
| } |
| |
| static void test_SetVerticalGlyphOrientation(void) |
| { |
| static const WCHAR strW[] = {'a','b','c','d',0}; |
| DWRITE_VERTICAL_GLYPH_ORIENTATION orientation; |
| IDWriteTextLayout2 *layout2; |
| IDWriteTextFormat *format; |
| IDWriteTextLayout *layout; |
| IDWriteFactory *factory; |
| HRESULT hr; |
| |
| factory = create_factory(); |
| |
| hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, |
| DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 1000.0, 1000.0, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| IDWriteTextFormat_Release(format); |
| |
| hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextLayout2, (void**)&layout2); |
| IDWriteTextLayout_Release(layout); |
| |
| if (hr != S_OK) { |
| win_skip("SetVerticalGlyphOrientation() is not supported.\n"); |
| IDWriteFactory_Release(factory); |
| return; |
| } |
| |
| orientation = IDWriteTextLayout2_GetVerticalGlyphOrientation(layout2); |
| ok(orientation == DWRITE_VERTICAL_GLYPH_ORIENTATION_DEFAULT, "got %d\n", orientation); |
| |
| hr = IDWriteTextLayout2_SetVerticalGlyphOrientation(layout2, DWRITE_VERTICAL_GLYPH_ORIENTATION_STACKED+1); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| |
| IDWriteTextLayout2_Release(layout2); |
| IDWriteFactory_Release(factory); |
| } |
| |
| static void test_fallback(void) |
| { |
| static const WCHAR strW[] = {'a','b','c','d',0}; |
| IDWriteFontFallback *fallback, *fallback2; |
| IDWriteTextLayout2 *layout2; |
| IDWriteTextFormat1 *format1; |
| IDWriteTextFormat *format; |
| IDWriteTextLayout *layout; |
| IDWriteFactory2 *factory2; |
| IDWriteFactory *factory; |
| HRESULT hr; |
| |
| factory = create_factory(); |
| |
| hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, |
| DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 1000.0, 1000.0, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| IDWriteTextFormat_Release(format); |
| |
| hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextLayout2, (void**)&layout2); |
| IDWriteTextLayout_Release(layout); |
| |
| if (hr != S_OK) { |
| win_skip("GetFontFallback() is not supported.\n"); |
| IDWriteFactory_Release(factory); |
| return; |
| } |
| |
| if (0) /* crashes on native */ |
| hr = IDWriteTextLayout2_GetFontFallback(layout2, NULL); |
| |
| fallback = (void*)0xdeadbeef; |
| hr = IDWriteTextLayout2_GetFontFallback(layout2, &fallback); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(fallback == NULL, "got %p\n", fallback); |
| |
| hr = IDWriteTextLayout2_QueryInterface(layout2, &IID_IDWriteTextFormat1, (void**)&format1); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| fallback = (void*)0xdeadbeef; |
| hr = IDWriteTextFormat1_GetFontFallback(format1, &fallback); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(fallback == NULL, "got %p\n", fallback); |
| |
| hr = IDWriteFactory_QueryInterface(factory, &IID_IDWriteFactory2, (void**)&factory2); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| fallback = NULL; |
| hr = IDWriteFactory2_GetSystemFontFallback(factory2, &fallback); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(fallback != NULL, "got %p\n", fallback); |
| |
| hr = IDWriteTextFormat1_SetFontFallback(format1, fallback); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| fallback2 = (void*)0xdeadbeef; |
| hr = IDWriteTextLayout2_GetFontFallback(layout2, &fallback2); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(fallback2 == fallback, "got %p\n", fallback2); |
| |
| hr = IDWriteTextLayout2_SetFontFallback(layout2, NULL); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| fallback2 = (void*)0xdeadbeef; |
| hr = IDWriteTextFormat1_GetFontFallback(format1, &fallback2); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(fallback2 == NULL, "got %p\n", fallback2); |
| |
| IDWriteFontFallback_Release(fallback); |
| IDWriteTextFormat1_Release(format1); |
| IDWriteTextLayout2_Release(layout2); |
| IDWriteFactory_Release(factory); |
| } |
| |
| static void test_DetermineMinWidth(void) |
| { |
| struct minwidth_test { |
| const WCHAR text[10]; /* text to create a layout for */ |
| const WCHAR mintext[10]; /* text that represents sequence of minimal width */ |
| } minwidth_tests[] = { |
| { {' ','a','b',' ',0}, {'a','b',0} }, |
| { {'a','\n',' ',' ',0}, {'a',0} }, |
| { {'a','\n',' ',' ','b',0}, {'b',0} }, |
| { {'a','b','c','\n',' ',' ','b',0}, {'a','b','c',0} }, |
| }; |
| static const WCHAR strW[] = {'a','b','c','d',0}; |
| DWRITE_CLUSTER_METRICS metrics[10]; |
| IDWriteTextFormat *format; |
| IDWriteTextLayout *layout; |
| IDWriteFactory *factory; |
| UINT32 count, i, j; |
| FLOAT minwidth; |
| HRESULT hr; |
| |
| factory = create_factory(); |
| |
| hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, |
| DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFactory_CreateTextLayout(factory, strW, lstrlenW(strW), format, 1000.0, 1000.0, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextLayout_DetermineMinWidth(layout, NULL); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| IDWriteTextLayout_Release(layout); |
| |
| /* empty string */ |
| hr = IDWriteFactory_CreateTextLayout(factory, strW, 0, format, 100.0f, 100.0f, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| minwidth = 1.0f; |
| hr = IDWriteTextLayout_DetermineMinWidth(layout, &minwidth); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(minwidth == 0.0f, "got %f\n", minwidth); |
| IDWriteTextLayout_Release(layout); |
| |
| for (i = 0; i < sizeof(minwidth_tests)/sizeof(minwidth_tests[0]); i++) { |
| FLOAT width = 0.0f; |
| |
| /* measure expected width */ |
| hr = IDWriteFactory_CreateTextLayout(factory, minwidth_tests[i].mintext, lstrlenW(minwidth_tests[i].mintext), format, 1000.0f, 1000.0f, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, sizeof(metrics)/sizeof(metrics[0]), &count); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| for (j = 0; j < count; j++) |
| width += metrics[j].width; |
| |
| IDWriteTextLayout_Release(layout); |
| |
| hr = IDWriteFactory_CreateTextLayout(factory, minwidth_tests[i].text, lstrlenW(minwidth_tests[i].text), format, 1000.0f, 1000.0f, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| minwidth = 0.0f; |
| hr = IDWriteTextLayout_DetermineMinWidth(layout, &minwidth); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(minwidth == width, "test %u: expected width %f, got %f\n", i, width, minwidth); |
| |
| IDWriteTextLayout_Release(layout); |
| } |
| |
| IDWriteTextFormat_Release(format); |
| IDWriteFactory_Release(factory); |
| } |
| |
| static void test_SetFontSize(void) |
| { |
| static const WCHAR strW[] = {'a','b','c','d',0}; |
| IDWriteTextFormat *format; |
| IDWriteTextLayout *layout; |
| IDWriteFactory *factory; |
| DWRITE_TEXT_RANGE r; |
| FLOAT size; |
| HRESULT hr; |
| |
| factory = create_factory(); |
| |
| hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, |
| DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 1000.0, 1000.0, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| /* negative/zero size */ |
| r.startPosition = 1; |
| r.length = 1; |
| hr = IDWriteTextLayout_SetFontSize(layout, -15.0, r); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextLayout_SetFontSize(layout, 0.0, r); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| |
| r.startPosition = 1; |
| r.length = 0; |
| size = 0.0; |
| hr = IDWriteTextLayout_GetFontSize(layout, 0, &size, &r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(r.startPosition == 0 && r.length == ~0u, "got %u, %u\n", r.startPosition, r.length); |
| ok(size == 10.0, "got %.2f\n", size); |
| |
| r.startPosition = 1; |
| r.length = 1; |
| hr = IDWriteTextLayout_SetFontSize(layout, 15.0, r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| /* zero length range */ |
| r.startPosition = 1; |
| r.length = 0; |
| hr = IDWriteTextLayout_SetFontSize(layout, 123.0, r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| size = 0.0; |
| hr = IDWriteTextLayout_GetFontSize(layout, 1, &size, &r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(size == 15.0, "got %.2f\n", size); |
| |
| r.startPosition = 0; |
| r.length = 4; |
| hr = IDWriteTextLayout_SetFontSize(layout, 15.0, r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| size = 0.0; |
| hr = IDWriteTextLayout_GetFontSize(layout, 1, &size, &r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(size == 15.0, "got %.2f\n", size); |
| |
| size = 0.0; |
| hr = IDWriteTextLayout_GetFontSize(layout, 0, &size, &r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(r.startPosition == 0 && r.length == 4, "got %u, %u\n", r.startPosition, r.length); |
| ok(size == 15.0, "got %.2f\n", size); |
| |
| size = 15.0; |
| r.startPosition = r.length = 0; |
| hr = IDWriteTextLayout_GetFontSize(layout, 20, &size, &r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(r.startPosition == 4 && r.length == ~0u-4, "got %u, %u\n", r.startPosition, r.length); |
| ok(size == 10.0, "got %.2f\n", size); |
| |
| r.startPosition = 100; |
| r.length = 4; |
| hr = IDWriteTextLayout_SetFontSize(layout, 25.0, r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| size = 15.0; |
| r.startPosition = r.length = 0; |
| hr = IDWriteTextLayout_GetFontSize(layout, 100, &size, &r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(r.startPosition == 100 && r.length == 4, "got %u, %u\n", r.startPosition, r.length); |
| ok(size == 25.0, "got %.2f\n", size); |
| |
| IDWriteTextLayout_Release(layout); |
| IDWriteTextFormat_Release(format); |
| IDWriteFactory_Release(factory); |
| } |
| |
| static void test_SetFontFamilyName(void) |
| { |
| static const WCHAR taHomaW[] = {'T','a','H','o','m','a',0}; |
| static const WCHAR arialW[] = {'A','r','i','a','l',0}; |
| static const WCHAR strW[] = {'a','b','c','d',0}; |
| IDWriteTextFormat *format; |
| IDWriteTextLayout *layout; |
| IDWriteFactory *factory; |
| DWRITE_TEXT_RANGE r; |
| WCHAR nameW[50]; |
| HRESULT hr; |
| |
| factory = create_factory(); |
| |
| hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, |
| DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 1000.0, 1000.0, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| /* NULL name */ |
| r.startPosition = 1; |
| r.length = 1; |
| hr = IDWriteTextLayout_SetFontFamilyName(layout, NULL, r); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| |
| r.startPosition = 1; |
| r.length = 0; |
| nameW[0] = 0; |
| hr = IDWriteTextLayout_GetFontFamilyName(layout, 1, nameW, sizeof(nameW)/sizeof(WCHAR), &r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(r.startPosition == 0 && r.length == ~0u, "got %u, %u\n", r.startPosition, r.length); |
| |
| /* set name only different in casing */ |
| r.startPosition = 1; |
| r.length = 1; |
| hr = IDWriteTextLayout_SetFontFamilyName(layout, taHomaW, r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| /* zero length range */ |
| r.startPosition = 1; |
| r.length = 0; |
| hr = IDWriteTextLayout_SetFontFamilyName(layout, arialW, r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| r.startPosition = 0; |
| r.length = 0; |
| nameW[0] = 0; |
| hr = IDWriteTextLayout_GetFontFamilyName(layout, 1, nameW, sizeof(nameW)/sizeof(WCHAR), &r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(!lstrcmpW(nameW, taHomaW), "got %s\n", wine_dbgstr_w(nameW)); |
| ok(r.startPosition == 1 && r.length == 1, "got %u, %u\n", r.startPosition, r.length); |
| |
| r.startPosition = 1; |
| r.length = 1; |
| hr = IDWriteTextLayout_SetFontFamilyName(layout, arialW, r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| r.startPosition = 1; |
| r.length = 0; |
| hr = IDWriteTextLayout_GetFontFamilyName(layout, 1, nameW, sizeof(nameW)/sizeof(WCHAR), &r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(r.startPosition == 1 && r.length == 1, "got %u, %u\n", r.startPosition, r.length); |
| |
| r.startPosition = 0; |
| r.length = 4; |
| hr = IDWriteTextLayout_SetFontFamilyName(layout, arialW, r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| nameW[0] = 0; |
| hr = IDWriteTextLayout_GetFontFamilyName(layout, 1, nameW, sizeof(nameW)/sizeof(WCHAR), &r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(r.startPosition == 0 && r.length == 4, "got %u, %u\n", r.startPosition, r.length); |
| ok(!lstrcmpW(nameW, arialW), "got name %s\n", wine_dbgstr_w(nameW)); |
| |
| IDWriteTextLayout_Release(layout); |
| IDWriteTextFormat_Release(format); |
| IDWriteFactory_Release(factory); |
| } |
| |
| static void test_SetFontStyle(void) |
| { |
| static const WCHAR strW[] = {'a','b','c','d',0}; |
| IDWriteTextFormat *format; |
| IDWriteTextLayout *layout; |
| IDWriteFactory *factory; |
| DWRITE_FONT_STYLE style; |
| DWRITE_TEXT_RANGE r; |
| HRESULT hr; |
| |
| factory = create_factory(); |
| |
| hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, |
| DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 1000.0, 1000.0, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| /* invalid style value */ |
| r.startPosition = 1; |
| r.length = 1; |
| hr = IDWriteTextLayout_SetFontStyle(layout, DWRITE_FONT_STYLE_ITALIC+1, r); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| |
| r.startPosition = 1; |
| r.length = 0; |
| hr = IDWriteTextLayout_GetFontStyle(layout, 0, &style, &r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(r.startPosition == 0 && r.length == ~0u, "got %u, %u\n", r.startPosition, r.length); |
| ok(style == DWRITE_FONT_STYLE_NORMAL, "got %d\n", style); |
| |
| r.startPosition = 1; |
| r.length = 1; |
| hr = IDWriteTextLayout_SetFontStyle(layout, DWRITE_FONT_STYLE_ITALIC, r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| /* zero length range */ |
| r.startPosition = 1; |
| r.length = 0; |
| hr = IDWriteTextLayout_SetFontStyle(layout, DWRITE_FONT_STYLE_NORMAL, r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| style = DWRITE_FONT_STYLE_NORMAL; |
| hr = IDWriteTextLayout_GetFontStyle(layout, 1, &style, &r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(style == DWRITE_FONT_STYLE_ITALIC, "got %d\n", style); |
| |
| r.startPosition = 0; |
| r.length = 4; |
| hr = IDWriteTextLayout_SetFontStyle(layout, DWRITE_FONT_STYLE_OBLIQUE, r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| style = DWRITE_FONT_STYLE_ITALIC; |
| hr = IDWriteTextLayout_GetFontStyle(layout, 1, &style, &r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(style == DWRITE_FONT_STYLE_OBLIQUE, "got %d\n", style); |
| |
| style = DWRITE_FONT_STYLE_ITALIC; |
| hr = IDWriteTextLayout_GetFontStyle(layout, 0, &style, &r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(r.startPosition == 0 && r.length == 4, "got %u, %u\n", r.startPosition, r.length); |
| ok(style == DWRITE_FONT_STYLE_OBLIQUE, "got %d\n", style); |
| |
| style = DWRITE_FONT_STYLE_ITALIC; |
| r.startPosition = r.length = 0; |
| hr = IDWriteTextLayout_GetFontStyle(layout, 20, &style, &r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(r.startPosition == 4 && r.length == ~0u-4, "got %u, %u\n", r.startPosition, r.length); |
| ok(style == DWRITE_FONT_STYLE_NORMAL, "got %d\n", style); |
| |
| r.startPosition = 100; |
| r.length = 4; |
| hr = IDWriteTextLayout_SetFontStyle(layout, DWRITE_FONT_STYLE_OBLIQUE, r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| style = DWRITE_FONT_STYLE_NORMAL; |
| r.startPosition = r.length = 0; |
| hr = IDWriteTextLayout_GetFontStyle(layout, 100, &style, &r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(r.startPosition == 100 && r.length == 4, "got %u, %u\n", r.startPosition, r.length); |
| ok(style == DWRITE_FONT_STYLE_OBLIQUE, "got %d\n", style); |
| |
| IDWriteTextLayout_Release(layout); |
| IDWriteTextFormat_Release(format); |
| IDWriteFactory_Release(factory); |
| } |
| |
| static void test_SetFontStretch(void) |
| { |
| static const WCHAR strW[] = {'a','b','c','d',0}; |
| DWRITE_FONT_STRETCH stretch; |
| IDWriteTextFormat *format; |
| IDWriteTextLayout *layout; |
| IDWriteFactory *factory; |
| DWRITE_TEXT_RANGE r; |
| HRESULT hr; |
| |
| factory = create_factory(); |
| |
| hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, |
| DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 1000.0, 1000.0, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| /* invalid stretch value */ |
| r.startPosition = 1; |
| r.length = 1; |
| hr = IDWriteTextLayout_SetFontStretch(layout, DWRITE_FONT_STRETCH_ULTRA_EXPANDED+1, r); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| |
| r.startPosition = 1; |
| r.length = 0; |
| stretch = DWRITE_FONT_STRETCH_UNDEFINED; |
| hr = IDWriteTextLayout_GetFontStretch(layout, 0, &stretch, &r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(r.startPosition == 0 && r.length == ~0u, "got %u, %u\n", r.startPosition, r.length); |
| ok(stretch == DWRITE_FONT_STRETCH_NORMAL, "got %d\n", stretch); |
| |
| r.startPosition = 1; |
| r.length = 1; |
| hr = IDWriteTextLayout_SetFontStretch(layout, DWRITE_FONT_STRETCH_CONDENSED, r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| /* zero length range */ |
| r.startPosition = 1; |
| r.length = 0; |
| hr = IDWriteTextLayout_SetFontStretch(layout, DWRITE_FONT_STRETCH_NORMAL, r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| stretch = DWRITE_FONT_STRETCH_UNDEFINED; |
| hr = IDWriteTextLayout_GetFontStretch(layout, 1, &stretch, &r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(stretch == DWRITE_FONT_STRETCH_CONDENSED, "got %d\n", stretch); |
| |
| r.startPosition = 0; |
| r.length = 4; |
| hr = IDWriteTextLayout_SetFontStretch(layout, DWRITE_FONT_STRETCH_EXPANDED, r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| stretch = DWRITE_FONT_STRETCH_UNDEFINED; |
| hr = IDWriteTextLayout_GetFontStretch(layout, 1, &stretch, &r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(stretch == DWRITE_FONT_STRETCH_EXPANDED, "got %d\n", stretch); |
| |
| stretch = DWRITE_FONT_STRETCH_UNDEFINED; |
| hr = IDWriteTextLayout_GetFontStretch(layout, 0, &stretch, &r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(r.startPosition == 0 && r.length == 4, "got %u, %u\n", r.startPosition, r.length); |
| ok(stretch == DWRITE_FONT_STRETCH_EXPANDED, "got %d\n", stretch); |
| |
| stretch = DWRITE_FONT_STRETCH_UNDEFINED; |
| r.startPosition = r.length = 0; |
| hr = IDWriteTextLayout_GetFontStretch(layout, 20, &stretch, &r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(r.startPosition == 4 && r.length == ~0u-4, "got %u, %u\n", r.startPosition, r.length); |
| ok(stretch == DWRITE_FONT_STRETCH_NORMAL, "got %d\n", stretch); |
| |
| r.startPosition = 100; |
| r.length = 4; |
| hr = IDWriteTextLayout_SetFontStretch(layout, DWRITE_FONT_STRETCH_EXPANDED, r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| stretch = DWRITE_FONT_STRETCH_UNDEFINED; |
| r.startPosition = r.length = 0; |
| hr = IDWriteTextLayout_GetFontStretch(layout, 100, &stretch, &r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(r.startPosition == 100 && r.length == 4, "got %u, %u\n", r.startPosition, r.length); |
| ok(stretch == DWRITE_FONT_STRETCH_EXPANDED, "got %d\n", stretch); |
| |
| /* trying to set undefined value */ |
| r.startPosition = 0; |
| r.length = 2; |
| hr = IDWriteTextLayout_SetFontStretch(layout, DWRITE_FONT_STRETCH_UNDEFINED, r); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| |
| IDWriteTextLayout_Release(layout); |
| IDWriteTextFormat_Release(format); |
| IDWriteFactory_Release(factory); |
| } |
| |
| static void test_SetStrikethrough(void) |
| { |
| static const WCHAR strW[] = {'a','b','c','d',0}; |
| IDWriteTextFormat *format; |
| IDWriteTextLayout *layout; |
| IDWriteFactory *factory; |
| DWRITE_TEXT_RANGE r; |
| BOOL value; |
| HRESULT hr; |
| |
| factory = create_factory(); |
| |
| hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, |
| DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 1000.0, 1000.0, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| r.startPosition = 1; |
| r.length = 0; |
| value = TRUE; |
| hr = IDWriteTextLayout_GetStrikethrough(layout, 0, &value, &r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(r.startPosition == 0 && r.length == ~0u, "got %u, %u\n", r.startPosition, r.length); |
| ok(value == FALSE, "got %d\n", value); |
| |
| r.startPosition = 1; |
| r.length = 1; |
| hr = IDWriteTextLayout_SetStrikethrough(layout, TRUE, r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| value = FALSE; |
| hr = IDWriteTextLayout_GetStrikethrough(layout, 1, &value, &r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(value == TRUE, "got %d\n", value); |
| ok(r.startPosition == 1 && r.length == 1, "got %u, %u\n", r.startPosition, r.length); |
| |
| value = TRUE; |
| r.startPosition = r.length = 0; |
| hr = IDWriteTextLayout_GetStrikethrough(layout, 20, &value, &r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(r.startPosition == 2 && r.length == ~0u-2, "got %u, %u\n", r.startPosition, r.length); |
| ok(value == FALSE, "got %d\n", value); |
| |
| r.startPosition = 100; |
| r.length = 4; |
| hr = IDWriteTextLayout_SetStrikethrough(layout, TRUE, r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| value = FALSE; |
| r.startPosition = r.length = 0; |
| hr = IDWriteTextLayout_GetStrikethrough(layout, 100, &value, &r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(r.startPosition == 100 && r.length == 4, "got %u, %u\n", r.startPosition, r.length); |
| ok(value == TRUE, "got %d\n", value); |
| |
| IDWriteTextLayout_Release(layout); |
| IDWriteTextFormat_Release(format); |
| IDWriteFactory_Release(factory); |
| } |
| |
| static void test_GetMetrics(void) |
| { |
| static const WCHAR str2W[] = {0x2066,')',')',0x661,'(',0x627,')',0}; |
| static const WCHAR strW[] = {'a','b','c','d',0}; |
| static const WCHAR str3W[] = {'a',0}; |
| DWRITE_CLUSTER_METRICS clusters[4]; |
| DWRITE_TEXT_METRICS metrics; |
| IDWriteTextFormat *format; |
| IDWriteTextLayout *layout; |
| IDWriteFactory *factory; |
| UINT32 count, i; |
| FLOAT width; |
| HRESULT hr; |
| |
| factory = create_factory(); |
| |
| hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, |
| DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 500.0, 1000.0, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| count = 0; |
| hr = IDWriteTextLayout_GetClusterMetrics(layout, clusters, 4, &count); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(count == 4, "got %u\n", count); |
| for (i = 0, width = 0.0; i < count; i++) |
| width += clusters[i].width; |
| |
| memset(&metrics, 0xcc, sizeof(metrics)); |
| hr = IDWriteTextLayout_GetMetrics(layout, &metrics); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(metrics.left == 0.0, "got %.2f\n", metrics.left); |
| ok(metrics.top == 0.0, "got %.2f\n", metrics.top); |
| ok(metrics.width == width, "got %.2f, expected %.2f\n", metrics.width, width); |
| ok(metrics.widthIncludingTrailingWhitespace == width, "got %.2f, expected %.2f\n", |
| metrics.widthIncludingTrailingWhitespace, width); |
| ok(metrics.height > 0.0, "got %.2f\n", metrics.height); |
| ok(metrics.layoutWidth == 500.0, "got %.2f\n", metrics.layoutWidth); |
| ok(metrics.layoutHeight == 1000.0, "got %.2f\n", metrics.layoutHeight); |
| ok(metrics.maxBidiReorderingDepth == 1, "got %u\n", metrics.maxBidiReorderingDepth); |
| ok(metrics.lineCount == 1, "got %u\n", metrics.lineCount); |
| |
| IDWriteTextLayout_Release(layout); |
| |
| /* a string with more complex bidi sequence */ |
| hr = IDWriteFactory_CreateTextLayout(factory, str2W, 7, format, 500.0, 1000.0, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| memset(&metrics, 0xcc, sizeof(metrics)); |
| metrics.maxBidiReorderingDepth = 0; |
| hr = IDWriteTextLayout_GetMetrics(layout, &metrics); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(metrics.left == 0.0, "got %.2f\n", metrics.left); |
| ok(metrics.top == 0.0, "got %.2f\n", metrics.top); |
| ok(metrics.width > 0.0, "got %.2f\n", metrics.width); |
| ok(metrics.widthIncludingTrailingWhitespace > 0.0, "got %.2f\n", metrics.widthIncludingTrailingWhitespace); |
| ok(metrics.height > 0.0, "got %.2f\n", metrics.height); |
| ok(metrics.layoutWidth == 500.0, "got %.2f\n", metrics.layoutWidth); |
| ok(metrics.layoutHeight == 1000.0, "got %.2f\n", metrics.layoutHeight); |
| todo_wine |
| ok(metrics.maxBidiReorderingDepth > 1, "got %u\n", metrics.maxBidiReorderingDepth); |
| ok(metrics.lineCount == 1, "got %u\n", metrics.lineCount); |
| |
| IDWriteTextLayout_Release(layout); |
| |
| /* single cluster layout */ |
| hr = IDWriteFactory_CreateTextLayout(factory, str3W, 1, format, 500.0, 1000.0, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| count = 0; |
| hr = IDWriteTextLayout_GetClusterMetrics(layout, clusters, 1, &count); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(count == 1, "got %u\n", count); |
| |
| memset(&metrics, 0xcc, sizeof(metrics)); |
| hr = IDWriteTextLayout_GetMetrics(layout, &metrics); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(metrics.left == 0.0, "got %.2f\n", metrics.left); |
| ok(metrics.top == 0.0, "got %.2f\n", metrics.top); |
| ok(metrics.width == clusters[0].width, "got %.2f, expected %.2f\n", metrics.width, clusters[0].width); |
| ok(metrics.widthIncludingTrailingWhitespace == clusters[0].width, "got %.2f\n", metrics.widthIncludingTrailingWhitespace); |
| ok(metrics.height > 0.0, "got %.2f\n", metrics.height); |
| ok(metrics.layoutWidth == 500.0, "got %.2f\n", metrics.layoutWidth); |
| ok(metrics.layoutHeight == 1000.0, "got %.2f\n", metrics.layoutHeight); |
| ok(metrics.maxBidiReorderingDepth == 1, "got %u\n", metrics.maxBidiReorderingDepth); |
| ok(metrics.lineCount == 1, "got %u\n", metrics.lineCount); |
| IDWriteTextLayout_Release(layout); |
| |
| IDWriteTextFormat_Release(format); |
| IDWriteFactory_Release(factory); |
| } |
| |
| static void test_SetFlowDirection(void) |
| { |
| static const WCHAR strW[] = {'a','b','c','d',0}; |
| DWRITE_READING_DIRECTION reading; |
| DWRITE_FLOW_DIRECTION flow; |
| IDWriteTextFormat *format; |
| IDWriteTextLayout *layout; |
| IDWriteFactory *factory; |
| HRESULT hr; |
| |
| factory = create_factory(); |
| |
| hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, |
| DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| flow = IDWriteTextFormat_GetFlowDirection(format); |
| ok(flow == DWRITE_FLOW_DIRECTION_TOP_TO_BOTTOM, "got %d\n", flow); |
| |
| reading = IDWriteTextFormat_GetReadingDirection(format); |
| ok(reading == DWRITE_READING_DIRECTION_LEFT_TO_RIGHT, "got %d\n", reading); |
| |
| hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 500.0, 1000.0, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| IDWriteTextLayout_Release(layout); |
| |
| hr = IDWriteTextFormat_SetFlowDirection(format, DWRITE_FLOW_DIRECTION_LEFT_TO_RIGHT); |
| ok(hr == S_OK || broken(hr == E_INVALIDARG) /* vista,win7 */, "got 0x%08x\n", hr); |
| if (hr == S_OK) { |
| hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 500.0, 1000.0, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| IDWriteTextLayout_Release(layout); |
| |
| hr = IDWriteTextFormat_SetReadingDirection(format, DWRITE_READING_DIRECTION_TOP_TO_BOTTOM); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextFormat_SetFlowDirection(format, DWRITE_FLOW_DIRECTION_TOP_TO_BOTTOM); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 500.0, 1000.0, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| IDWriteTextLayout_Release(layout); |
| } |
| else |
| win_skip("DWRITE_FLOW_DIRECTION_LEFT_TO_RIGHT is not supported\n"); |
| |
| IDWriteTextFormat_Release(format); |
| IDWriteFactory_Release(factory); |
| } |
| |
| static const struct drawcall_entry draweffect_seq[] = { |
| { DRAW_GLYPHRUN|DRAW_EFFECT, {'a','e',0x0300,0}, {'e','n','-','u','s',0}, 2 }, |
| { DRAW_GLYPHRUN, {'d',0}, {'e','n','-','u','s',0}, 1 }, |
| { DRAW_LAST_KIND } |
| }; |
| |
| static const struct drawcall_entry draweffect2_seq[] = { |
| { DRAW_GLYPHRUN|DRAW_EFFECT, {'a','e',0}, {'e','n','-','u','s',0}, 2 }, |
| { DRAW_GLYPHRUN, {'c','d',0}, {'e','n','-','u','s',0}, 2 }, |
| { DRAW_LAST_KIND } |
| }; |
| |
| static const struct drawcall_entry draweffect3_seq[] = { |
| { DRAW_INLINE|DRAW_EFFECT }, |
| { DRAW_LAST_KIND } |
| }; |
| |
| static const struct drawcall_entry draweffect4_seq[] = { |
| { DRAW_INLINE }, |
| { DRAW_LAST_KIND } |
| }; |
| |
| static void test_SetDrawingEffect(void) |
| { |
| static const WCHAR strW[] = {'a','e',0x0300,'d',0}; /* accent grave */ |
| static const WCHAR str2W[] = {'a','e','c','d',0}; |
| IDWriteInlineObject *sign; |
| IDWriteTextFormat *format; |
| IDWriteTextLayout *layout; |
| IDWriteFactory *factory; |
| IUnknown *unk, *effect; |
| DWRITE_TEXT_RANGE r; |
| HRESULT hr; |
| LONG ref; |
| |
| factory = create_factory(); |
| |
| effect = create_test_effect(); |
| |
| hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, |
| DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| /* string with combining mark */ |
| hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 500.0, 1000.0, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| /* set effect past the end of text */ |
| r.startPosition = 100; |
| r.length = 10; |
| hr = IDWriteTextLayout_SetDrawingEffect(layout, effect, r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| r.startPosition = r.length = 0; |
| hr = IDWriteTextLayout_GetDrawingEffect(layout, 101, &unk, &r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(r.startPosition == 100 && r.length == 10, "got %u, %u\n", r.startPosition, r.length); |
| IUnknown_Release(unk); |
| |
| r.startPosition = r.length = 0; |
| unk = (void*)0xdeadbeef; |
| hr = IDWriteTextLayout_GetDrawingEffect(layout, 1000, &unk, &r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(r.startPosition == 110 && r.length == ~0u-110, "got %u, %u\n", r.startPosition, r.length); |
| ok(unk == NULL, "got %p\n", unk); |
| |
| /* effect is applied to clusters, not individual text positions */ |
| r.startPosition = 0; |
| r.length = 2; |
| hr = IDWriteTextLayout_SetDrawingEffect(layout, effect, r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| flush_sequence(sequences, RENDERER_ID); |
| hr = IDWriteTextLayout_Draw(layout, NULL, &testrenderer, 0.0, 0.0); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok_sequence(sequences, RENDERER_ID, draweffect_seq, "effect draw test", TRUE); |
| IDWriteTextLayout_Release(layout); |
| |
| /* simple string */ |
| hr = IDWriteFactory_CreateTextLayout(factory, str2W, 4, format, 500.0, 1000.0, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| r.startPosition = 0; |
| r.length = 2; |
| hr = IDWriteTextLayout_SetDrawingEffect(layout, effect, r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| flush_sequence(sequences, RENDERER_ID); |
| hr = IDWriteTextLayout_Draw(layout, NULL, &testrenderer, 0.0, 0.0); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok_sequence(sequences, RENDERER_ID, draweffect2_seq, "effect draw test 2", FALSE); |
| IDWriteTextLayout_Release(layout); |
| |
| /* Inline object - effect set for same range */ |
| hr = IDWriteFactory_CreateEllipsisTrimmingSign(factory, format, &sign); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFactory_CreateTextLayout(factory, str2W, 4, format, 500.0, 1000.0, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| r.startPosition = 0; |
| r.length = 4; |
| hr = IDWriteTextLayout_SetInlineObject(layout, sign, r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextLayout_SetDrawingEffect(layout, effect, r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| flush_sequence(sequences, RENDERER_ID); |
| hr = IDWriteTextLayout_Draw(layout, NULL, &testrenderer, 0.0, 0.0); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok_sequence(sequences, RENDERER_ID, draweffect3_seq, "effect draw test 3", FALSE); |
| |
| /* now set effect somewhere inside a range replaced by inline object */ |
| hr = IDWriteTextLayout_SetDrawingEffect(layout, NULL, r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| r.startPosition = 1; |
| r.length = 1; |
| hr = IDWriteTextLayout_SetDrawingEffect(layout, effect, r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| /* no effect is reported in this case */ |
| flush_sequence(sequences, RENDERER_ID); |
| hr = IDWriteTextLayout_Draw(layout, NULL, &testrenderer, 0.0, 0.0); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok_sequence(sequences, RENDERER_ID, draweffect4_seq, "effect draw test 4", FALSE); |
| |
| r.startPosition = 0; |
| r.length = 4; |
| hr = IDWriteTextLayout_SetDrawingEffect(layout, NULL, r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| r.startPosition = 0; |
| r.length = 1; |
| hr = IDWriteTextLayout_SetDrawingEffect(layout, effect, r); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| /* first range position is all that matters for inline ranges */ |
| flush_sequence(sequences, RENDERER_ID); |
| hr = IDWriteTextLayout_Draw(layout, NULL, &testrenderer, 0.0, 0.0); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok_sequence(sequences, RENDERER_ID, draweffect3_seq, "effect draw test 5", FALSE); |
| |
| IDWriteTextLayout_Release(layout); |
| |
| ref = IUnknown_Release(effect); |
| ok(ref == 0, "Unexpected effect refcount %u\n", ref); |
| IDWriteInlineObject_Release(sign); |
| IDWriteTextFormat_Release(format); |
| IDWriteFactory_Release(factory); |
| } |
| |
| static BOOL get_enus_string(IDWriteLocalizedStrings *strings, WCHAR *buff, UINT32 size) |
| { |
| UINT32 index; |
| BOOL exists = FALSE; |
| HRESULT hr; |
| |
| hr = IDWriteLocalizedStrings_FindLocaleName(strings, enusW, &index, &exists); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| if (exists) { |
| hr = IDWriteLocalizedStrings_GetString(strings, index, buff, size); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| } |
| else |
| *buff = 0; |
| |
| return exists; |
| } |
| |
| static void test_GetLineMetrics(void) |
| { |
| static const WCHAR str3W[] = {'a','\r','b','\n','c','\n','\r','d','\r','\n',0}; |
| static const WCHAR strW[] = {'a','b','c','d',' ',0}; |
| static const WCHAR str2W[] = {'a','b','\r','c','d',0}; |
| static const WCHAR str4W[] = {'a','\r',0}; |
| static const WCHAR emptyW[] = {0}; |
| IDWriteFontCollection *syscollection; |
| DWRITE_FONT_METRICS fontmetrics; |
| DWRITE_LINE_METRICS metrics[6]; |
| UINT32 count, i, familycount; |
| IDWriteTextFormat *format; |
| IDWriteTextLayout *layout; |
| IDWriteFontFace *fontface; |
| IDWriteFactory *factory; |
| DWRITE_TEXT_RANGE range; |
| WCHAR nameW[256]; |
| HRESULT hr; |
| |
| factory = create_factory(); |
| |
| hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, |
| DWRITE_FONT_STRETCH_NORMAL, 2048.0, enusW, &format); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFactory_CreateTextLayout(factory, strW, 5, format, 30000.0, 1000.0, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| count = 0; |
| hr = IDWriteTextLayout_GetLineMetrics(layout, metrics, 0, &count); |
| ok(hr == E_NOT_SUFFICIENT_BUFFER, "got 0x%08x\n", hr); |
| ok(count == 1, "got count %u\n", count); |
| |
| memset(metrics, 0, sizeof(metrics)); |
| hr = IDWriteTextLayout_GetLineMetrics(layout, metrics, 1, &count); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(metrics[0].length == 5, "got %u\n", metrics[0].length); |
| ok(metrics[0].trailingWhitespaceLength == 1, "got %u\n", metrics[0].trailingWhitespaceLength); |
| |
| ok(metrics[0].newlineLength == 0, "got %u\n", metrics[0].newlineLength); |
| ok(metrics[0].isTrimmed == FALSE, "got %d\n", metrics[0].isTrimmed); |
| |
| IDWriteTextLayout_Release(layout); |
| IDWriteTextFormat_Release(format); |
| |
| /* Test line height and baseline calculation */ |
| hr = IDWriteFactory_GetSystemFontCollection(factory, &syscollection, FALSE); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| familycount = IDWriteFontCollection_GetFontFamilyCount(syscollection); |
| |
| for (i = 0; i < familycount; i++) { |
| IDWriteLocalizedStrings *names; |
| IDWriteFontFamily *family; |
| IDWriteFont *font; |
| BOOL exists; |
| |
| format = NULL; |
| layout = NULL; |
| |
| hr = IDWriteFontCollection_GetFontFamily(syscollection, i, &family); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFontFamily_GetFirstMatchingFont(family, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STRETCH_NORMAL, |
| DWRITE_FONT_STYLE_NORMAL, &font); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFont_CreateFontFace(font, &fontface); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFontFamily_GetFamilyNames(family, &names); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| if (!(exists = get_enus_string(names, nameW, sizeof(nameW)/sizeof(nameW[0])))) { |
| IDWriteLocalFontFileLoader *localloader; |
| IDWriteFontFileLoader *loader; |
| IDWriteFontFile *file; |
| const void *key; |
| UINT32 keysize; |
| UINT32 count; |
| |
| count = 1; |
| hr = IDWriteFontFace_GetFiles(fontface, &count, &file); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFontFile_GetLoader(file, &loader); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFontFileLoader_QueryInterface(loader, &IID_IDWriteLocalFontFileLoader, (void**)&localloader); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| IDWriteFontFileLoader_Release(loader); |
| |
| hr = IDWriteFontFile_GetReferenceKey(file, &key, &keysize); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteLocalFontFileLoader_GetFilePathFromKey(localloader, key, keysize, nameW, sizeof(nameW)/sizeof(*nameW)); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| skip("Failed to get English family name, font file %s\n", wine_dbgstr_w(nameW)); |
| |
| IDWriteLocalFontFileLoader_Release(localloader); |
| IDWriteFontFile_Release(file); |
| } |
| |
| IDWriteLocalizedStrings_Release(names); |
| IDWriteFont_Release(font); |
| |
| if (!exists) |
| goto cleanup; |
| |
| IDWriteFontFace_GetMetrics(fontface, &fontmetrics); |
| hr = IDWriteFactory_CreateTextFormat(factory, nameW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, |
| DWRITE_FONT_STRETCH_NORMAL, fontmetrics.designUnitsPerEm, enusW, &format); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFactory_CreateTextLayout(factory, emptyW, 1, format, 30000.0f, 100.0f, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| memset(metrics, 0, sizeof(metrics)); |
| count = 0; |
| hr = IDWriteTextLayout_GetLineMetrics(layout, metrics, sizeof(metrics)/sizeof(metrics[0]), &count); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(count == 1, "got %u\n", count); |
| |
| ok(metrics[0].baseline == fontmetrics.ascent + fontmetrics.lineGap, "%s: got %.2f, expected %d, " |
| "linegap %d\n", wine_dbgstr_w(nameW), metrics[0].baseline, fontmetrics.ascent + fontmetrics.lineGap, |
| fontmetrics.lineGap); |
| ok(metrics[0].height == fontmetrics.ascent + fontmetrics.descent + fontmetrics.lineGap, |
| "%s: got %.2f, expected %d, linegap %d\n", wine_dbgstr_w(nameW), metrics[0].height, |
| fontmetrics.ascent + fontmetrics.descent + fontmetrics.lineGap, fontmetrics.lineGap); |
| |
| cleanup: |
| if (layout) |
| IDWriteTextLayout_Release(layout); |
| if (format) |
| IDWriteTextFormat_Release(format); |
| IDWriteFontFace_Release(fontface); |
| IDWriteFontFamily_Release(family); |
| } |
| IDWriteFontCollection_Release(syscollection); |
| |
| hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, |
| DWRITE_FONT_STRETCH_NORMAL, 2048.0f, enusW, &format); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| fontface = get_fontface_from_format(format); |
| ok(fontface != NULL, "got %p\n", fontface); |
| |
| /* force 2 lines */ |
| hr = IDWriteFactory_CreateTextLayout(factory, str2W, 5, format, 10000.0, 1000.0, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| memset(metrics, 0, sizeof(metrics)); |
| count = 0; |
| hr = IDWriteTextLayout_GetLineMetrics(layout, metrics, sizeof(metrics)/sizeof(*metrics), &count); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(count == 2, "got %u\n", count); |
| /* baseline is relative to a line, and is not accumulated */ |
| ok(metrics[0].baseline == metrics[1].baseline, "got %.2f, %.2f\n", metrics[0].baseline, |
| metrics[1].baseline); |
| |
| IDWriteTextLayout_Release(layout); |
| IDWriteTextFormat_Release(format); |
| |
| /* line breaks */ |
| hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, |
| DWRITE_FONT_STRETCH_NORMAL, 12.0, enusW, &format); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFactory_CreateTextLayout(factory, str3W, 10, format, 100.0, 300.0, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| memset(metrics, 0xcc, sizeof(metrics)); |
| count = 0; |
| hr = IDWriteTextLayout_GetLineMetrics(layout, metrics, sizeof(metrics)/sizeof(*metrics), &count); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(count == 6, "got %u\n", count); |
| |
| ok(metrics[0].length == 2, "got %u\n", metrics[0].length); |
| ok(metrics[1].length == 2, "got %u\n", metrics[1].length); |
| ok(metrics[2].length == 2, "got %u\n", metrics[2].length); |
| ok(metrics[3].length == 1, "got %u\n", metrics[3].length); |
| ok(metrics[4].length == 3, "got %u\n", metrics[4].length); |
| ok(metrics[5].length == 0, "got %u\n", metrics[5].length); |
| |
| ok(metrics[0].newlineLength == 1, "got %u\n", metrics[0].newlineLength); |
| ok(metrics[1].newlineLength == 1, "got %u\n", metrics[1].newlineLength); |
| ok(metrics[2].newlineLength == 1, "got %u\n", metrics[2].newlineLength); |
| ok(metrics[3].newlineLength == 1, "got %u\n", metrics[3].newlineLength); |
| ok(metrics[4].newlineLength == 2, "got %u\n", metrics[4].newlineLength); |
| ok(metrics[5].newlineLength == 0, "got %u\n", metrics[5].newlineLength); |
| |
| ok(metrics[0].trailingWhitespaceLength == 1, "got %u\n", metrics[0].newlineLength); |
| ok(metrics[1].trailingWhitespaceLength == 1, "got %u\n", metrics[1].newlineLength); |
| ok(metrics[2].trailingWhitespaceLength == 1, "got %u\n", metrics[2].newlineLength); |
| ok(metrics[3].trailingWhitespaceLength == 1, "got %u\n", metrics[3].newlineLength); |
| ok(metrics[4].trailingWhitespaceLength == 2, "got %u\n", metrics[4].newlineLength); |
| ok(metrics[5].trailingWhitespaceLength == 0, "got %u\n", metrics[5].newlineLength); |
| |
| IDWriteTextLayout_Release(layout); |
| |
| /* empty text layout */ |
| hr = IDWriteFactory_CreateTextLayout(factory, strW, 0, format, 100.0f, 300.0f, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| count = 0; |
| memset(metrics, 0, sizeof(metrics)); |
| hr = IDWriteTextLayout_GetLineMetrics(layout, metrics, 1, &count); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(count == 1, "got %u\n", count); |
| ok(metrics[0].length == 0, "got %u\n", metrics[0].length); |
| ok(metrics[0].trailingWhitespaceLength == 0, "got %u\n", metrics[0].trailingWhitespaceLength); |
| ok(metrics[0].newlineLength == 0, "got %u\n", metrics[0].newlineLength); |
| ok(metrics[0].height > 0.0f, "got %f\n", metrics[0].height); |
| ok(metrics[0].baseline > 0.0f, "got %f\n", metrics[0].baseline); |
| ok(!metrics[0].isTrimmed, "got %d\n", metrics[0].isTrimmed); |
| |
| /* change font size at first position, see if metrics changed */ |
| range.startPosition = 0; |
| range.length = 1; |
| hr = IDWriteTextLayout_SetFontSize(layout, 80.0f, range); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| count = 0; |
| hr = IDWriteTextLayout_GetLineMetrics(layout, metrics + 1, 1, &count); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(count == 1, "got %u\n", count); |
| ok(metrics[1].height > metrics[0].height, "got %f\n", metrics[1].height); |
| ok(metrics[1].baseline > metrics[0].baseline, "got %f\n", metrics[1].baseline); |
| |
| /* revert font size back to format value, set different size for position 1 */ |
| hr = IDWriteTextLayout_SetFontSize(layout, 12.0f, range); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| range.startPosition = 1; |
| range.length = 1; |
| hr = IDWriteTextLayout_SetFontSize(layout, 80.0f, range); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| memset(metrics + 1, 0, sizeof(*metrics)); |
| count = 0; |
| hr = IDWriteTextLayout_GetLineMetrics(layout, metrics + 1, 1, &count); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(count == 1, "got %u\n", count); |
| ok(metrics[1].height == metrics[0].height, "got %f\n", metrics[1].height); |
| ok(metrics[1].baseline == metrics[0].baseline, "got %f\n", metrics[1].baseline); |
| |
| IDWriteTextLayout_Release(layout); |
| |
| /* text is "a\r" */ |
| hr = IDWriteFactory_CreateTextLayout(factory, str4W, 2, format, 100.0f, 300.0f, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| count = 0; |
| memset(metrics, 0, sizeof(metrics)); |
| hr = IDWriteTextLayout_GetLineMetrics(layout, metrics, sizeof(metrics)/sizeof(*metrics), &count); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(count == 2, "got %u\n", count); |
| ok(metrics[0].length == 2, "got %u\n", metrics[0].length); |
| ok(metrics[0].newlineLength == 1, "got %u\n", metrics[0].newlineLength); |
| ok(metrics[0].height > 0.0f, "got %f\n", metrics[0].height); |
| ok(metrics[0].baseline > 0.0f, "got %f\n", metrics[0].baseline); |
| ok(metrics[1].length == 0, "got %u\n", metrics[1].length); |
| ok(metrics[1].newlineLength == 0, "got %u\n", metrics[1].newlineLength); |
| ok(metrics[1].height > 0.0f, "got %f\n", metrics[1].height); |
| ok(metrics[1].baseline > 0.0f, "got %f\n", metrics[1].baseline); |
| |
| range.startPosition = 1; |
| range.length = 1; |
| hr = IDWriteTextLayout_SetFontSize(layout, 80.0f, range); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextLayout_GetLineMetrics(layout, metrics + 2, 2, &count); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(count == 2, "got %u\n", count); |
| ok(metrics[3].height > metrics[1].height, "got %f, old %f\n", metrics[3].height, metrics[1].height); |
| ok(metrics[3].baseline > metrics[1].baseline, "got %f, old %f\n", metrics[3].baseline, metrics[1].baseline); |
| |
| /* revert to original format */ |
| hr = IDWriteTextLayout_SetFontSize(layout, 12.0f, range); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| hr = IDWriteTextLayout_GetLineMetrics(layout, metrics + 2, 2, &count); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(count == 2, "got %u\n", count); |
| ok(metrics[3].height == metrics[1].height, "got %f, old %f\n", metrics[3].height, metrics[1].height); |
| ok(metrics[3].baseline == metrics[1].baseline, "got %f, old %f\n", metrics[3].baseline, metrics[1].baseline); |
| |
| /* Switch to uniform spacing */ |
| hr = IDWriteTextLayout_SetLineSpacing(layout, DWRITE_LINE_SPACING_METHOD_UNIFORM, 456.0f, 123.0f); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextLayout_GetLineMetrics(layout, metrics, sizeof(metrics)/sizeof(metrics[0]), &count); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(count == 2, "got %u\n", count); |
| |
| for (i = 0; i < count; i++) { |
| ok(metrics[i].height == 456.0f, "%u: got line height %f\n", i, metrics[i].height); |
| ok(metrics[i].baseline == 123.0f, "%u: got line baseline %f\n", i, metrics[i].baseline); |
| } |
| |
| IDWriteTextLayout_Release(layout); |
| |
| /* Switch to proportional */ |
| hr = IDWriteTextFormat_SetLineSpacing(format, DWRITE_LINE_SPACING_METHOD_PROPORTIONAL, 2.0f, 4.0f); |
| if (hr == S_OK) { |
| hr = IDWriteFactory_CreateTextLayout(factory, str4W, 1, format, 100.0f, 300.0f, &layout); |
| ok(hr == S_OK, "Failed to create layout, hr %#x.\n", hr); |
| |
| hr = IDWriteTextLayout_GetLineMetrics(layout, metrics, sizeof(metrics)/sizeof(metrics[0]), &count); |
| ok(hr == S_OK, "Failed to get line metrics, hr %#x.\n", hr); |
| ok(count == 1, "Unexpected line count %u\n", count); |
| |
| /* Back to default mode. */ |
| hr = IDWriteTextLayout_SetLineSpacing(layout, DWRITE_LINE_SPACING_METHOD_DEFAULT, 0.0f, 0.0f); |
| ok(hr == S_OK, "Failed to set spacing method, hr %#x.\n", hr); |
| |
| hr = IDWriteTextLayout_GetLineMetrics(layout, metrics + 1, 1, &count); |
| ok(hr == S_OK, "Failed to get line metrics, hr %#x.\n", hr); |
| ok(count == 1, "Unexpected line count %u\n", count); |
| |
| /* Proportional spacing applies multipliers to default, content based spacing. */ |
| ok(metrics[0].height == 2.0f * metrics[1].height, "Unexpected line height %f.\n", metrics[0].height); |
| ok(metrics[0].baseline == 4.0f * metrics[1].baseline, "Unexpected line baseline %f.\n", metrics[0].baseline); |
| |
| IDWriteTextLayout_Release(layout); |
| } |
| else |
| win_skip("Proportional spacing is not supported.\n"); |
| |
| IDWriteTextFormat_Release(format); |
| IDWriteFontFace_Release(fontface); |
| IDWriteFactory_Release(factory); |
| } |
| |
| static void test_SetTextAlignment(void) |
| { |
| static const WCHAR strW[] = {'a',0}; |
| |
| static const WCHAR stringsW[][10] = { |
| {'a',0}, |
| {0} |
| }; |
| |
| DWRITE_CLUSTER_METRICS clusters[10]; |
| DWRITE_TEXT_METRICS metrics; |
| IDWriteTextFormat1 *format1; |
| IDWriteTextFormat *format; |
| IDWriteTextLayout *layout; |
| IDWriteFactory *factory; |
| DWRITE_TEXT_ALIGNMENT v; |
| UINT32 count, i; |
| HRESULT hr; |
| |
| factory = create_factory(); |
| |
| hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, |
| DWRITE_FONT_STRETCH_NORMAL, 12.0, enusW, &format); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| v = IDWriteTextFormat_GetTextAlignment(format); |
| ok(v == DWRITE_TEXT_ALIGNMENT_LEADING, "got %d\n", v); |
| |
| hr = IDWriteFactory_CreateTextLayout(factory, strW, 1, format, 500.0, 100.0, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| v = IDWriteTextLayout_GetTextAlignment(layout); |
| ok(v == DWRITE_TEXT_ALIGNMENT_LEADING, "got %d\n", v); |
| |
| hr = IDWriteTextLayout_SetTextAlignment(layout, DWRITE_TEXT_ALIGNMENT_TRAILING); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextLayout_SetTextAlignment(layout, DWRITE_TEXT_ALIGNMENT_TRAILING); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| v = IDWriteTextFormat_GetTextAlignment(format); |
| ok(v == DWRITE_TEXT_ALIGNMENT_LEADING, "got %d\n", v); |
| |
| v = IDWriteTextLayout_GetTextAlignment(layout); |
| ok(v == DWRITE_TEXT_ALIGNMENT_TRAILING, "got %d\n", v); |
| |
| hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextFormat1, (void**)&format1); |
| if (hr == S_OK) { |
| hr = IDWriteTextFormat1_SetTextAlignment(format1, DWRITE_TEXT_ALIGNMENT_CENTER); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| v = IDWriteTextFormat_GetTextAlignment(format); |
| ok(v == DWRITE_TEXT_ALIGNMENT_LEADING, "got %d\n", v); |
| |
| v = IDWriteTextLayout_GetTextAlignment(layout); |
| ok(v == DWRITE_TEXT_ALIGNMENT_CENTER, "got %d\n", v); |
| |
| v = IDWriteTextFormat1_GetTextAlignment(format1); |
| ok(v == DWRITE_TEXT_ALIGNMENT_CENTER, "got %d\n", v); |
| |
| IDWriteTextFormat1_Release(format1); |
| } |
| else |
| win_skip("IDWriteTextFormat1 is not supported\n"); |
| |
| IDWriteTextLayout_Release(layout); |
| |
| for (i = 0; i < sizeof(stringsW)/sizeof(stringsW[0]); i++) { |
| FLOAT text_width; |
| |
| hr = IDWriteTextFormat_SetTextAlignment(format, DWRITE_TEXT_ALIGNMENT_LEADING); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFactory_CreateTextLayout(factory, stringsW[i], lstrlenW(stringsW[i]), format, 500.0f, 100.0f, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextLayout_SetWordWrapping(layout, DWRITE_WORD_WRAPPING_NO_WRAP); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| count = 0; |
| hr = IDWriteTextLayout_GetClusterMetrics(layout, clusters, sizeof(clusters)/sizeof(*clusters), &count); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| if (lstrlenW(stringsW[i])) |
| ok(count > 0, "got %u\n", count); |
| else |
| ok(count == 0, "got %u\n", count); |
| |
| text_width = 0.0f; |
| while (count) |
| text_width += clusters[--count].width; |
| |
| /* maxwidth is 500, leading alignment */ |
| hr = IDWriteTextLayout_SetTextAlignment(layout, DWRITE_TEXT_ALIGNMENT_LEADING); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextLayout_GetMetrics(layout, &metrics); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| ok(metrics.left == 0.0f, "got %.2f\n", metrics.left); |
| ok(metrics.width == text_width, "got %.2f\n", metrics.width); |
| ok(metrics.layoutWidth == 500.0f, "got %.2f\n", metrics.layoutWidth); |
| ok(metrics.lineCount == 1, "got %d\n", metrics.lineCount); |
| |
| /* maxwidth is 500, trailing alignment */ |
| hr = IDWriteTextLayout_SetTextAlignment(layout, DWRITE_TEXT_ALIGNMENT_TRAILING); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextLayout_GetMetrics(layout, &metrics); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| ok(metrics.left == metrics.layoutWidth - metrics.width, "got %.2f\n", metrics.left); |
| ok(metrics.width == text_width, "got %.2f\n", metrics.width); |
| ok(metrics.layoutWidth == 500.0f, "got %.2f\n", metrics.layoutWidth); |
| ok(metrics.lineCount == 1, "got %d\n", metrics.lineCount); |
| IDWriteTextLayout_Release(layout); |
| |
| /* initially created with trailing alignment */ |
| hr = IDWriteTextFormat_SetTextAlignment(format, DWRITE_TEXT_ALIGNMENT_TRAILING); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFactory_CreateTextLayout(factory, stringsW[i], lstrlenW(stringsW[i]), format, 500.0f, 100.0f, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextLayout_GetMetrics(layout, &metrics); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| ok(metrics.left == metrics.layoutWidth - metrics.width, "got %.2f\n", metrics.left); |
| ok(metrics.width == text_width, "got %.2f\n", metrics.width); |
| ok(metrics.layoutWidth == 500.0f, "got %.2f\n", metrics.layoutWidth); |
| ok(metrics.lineCount == 1, "got %d\n", metrics.lineCount); |
| IDWriteTextLayout_Release(layout); |
| |
| if (lstrlenW(stringsW[i]) > 0) { |
| /* max width less than total run width, trailing alignment */ |
| hr = IDWriteTextFormat_SetWordWrapping(format, DWRITE_WORD_WRAPPING_NO_WRAP); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFactory_CreateTextLayout(factory, stringsW[i], lstrlenW(stringsW[i]), format, clusters[0].width, 100.0f, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| hr = IDWriteTextLayout_GetMetrics(layout, &metrics); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(metrics.left == metrics.layoutWidth - metrics.width, "got %.2f\n", metrics.left); |
| ok(metrics.width == text_width, "got %.2f\n", metrics.width); |
| ok(metrics.lineCount == 1, "got %d\n", metrics.lineCount); |
| IDWriteTextLayout_Release(layout); |
| } |
| |
| /* maxwidth is 500, centered */ |
| hr = IDWriteTextFormat_SetTextAlignment(format, DWRITE_TEXT_ALIGNMENT_CENTER); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFactory_CreateTextLayout(factory, stringsW[i], lstrlenW(stringsW[i]), format, 500.0f, 100.0f, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextLayout_GetMetrics(layout, &metrics); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(metrics.left == (metrics.layoutWidth - metrics.width) / 2.0f, "got %.2f\n", metrics.left); |
| ok(metrics.width == text_width, "got %.2f\n", metrics.width); |
| ok(metrics.lineCount == 1, "got %d\n", metrics.lineCount); |
| |
| IDWriteTextLayout_Release(layout); |
| } |
| |
| IDWriteTextFormat_Release(format); |
| IDWriteFactory_Release(factory); |
| } |
| |
| static void test_SetParagraphAlignment(void) |
| { |
| static const WCHAR strW[] = {'a',0}; |
| DWRITE_TEXT_METRICS metrics; |
| IDWriteTextFormat *format; |
| IDWriteTextLayout *layout; |
| IDWriteFactory *factory; |
| DWRITE_PARAGRAPH_ALIGNMENT v; |
| DWRITE_LINE_METRICS lines[1]; |
| UINT32 count; |
| HRESULT hr; |
| |
| factory = create_factory(); |
| |
| hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, |
| DWRITE_FONT_STRETCH_NORMAL, 12.0, enusW, &format); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| v = IDWriteTextFormat_GetParagraphAlignment(format); |
| ok(v == DWRITE_PARAGRAPH_ALIGNMENT_NEAR, "got %d\n", v); |
| |
| hr = IDWriteFactory_CreateTextLayout(factory, strW, 1, format, 500.0, 100.0, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| v = IDWriteTextLayout_GetParagraphAlignment(layout); |
| ok(v == DWRITE_PARAGRAPH_ALIGNMENT_NEAR, "got %d\n", v); |
| |
| hr = IDWriteTextLayout_SetParagraphAlignment(layout, DWRITE_PARAGRAPH_ALIGNMENT_FAR); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextLayout_SetParagraphAlignment(layout, DWRITE_PARAGRAPH_ALIGNMENT_FAR); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| v = IDWriteTextFormat_GetParagraphAlignment(format); |
| ok(v == DWRITE_PARAGRAPH_ALIGNMENT_NEAR, "got %d\n", v); |
| |
| v = IDWriteTextLayout_GetParagraphAlignment(layout); |
| ok(v == DWRITE_PARAGRAPH_ALIGNMENT_FAR, "got %d\n", v); |
| |
| hr = IDWriteTextLayout_SetParagraphAlignment(layout, DWRITE_PARAGRAPH_ALIGNMENT_CENTER); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| v = IDWriteTextLayout_GetParagraphAlignment(layout); |
| ok(v == DWRITE_PARAGRAPH_ALIGNMENT_CENTER, "got %d\n", v); |
| |
| count = 0; |
| hr = IDWriteTextLayout_GetLineMetrics(layout, lines, 1, &count); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(count == 1, "got %u\n", count); |
| |
| /* maxheight is 100, near alignment */ |
| hr = IDWriteTextLayout_SetParagraphAlignment(layout, DWRITE_PARAGRAPH_ALIGNMENT_NEAR); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextLayout_GetMetrics(layout, &metrics); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| ok(metrics.top == 0.0, "got %.2f\n", metrics.top); |
| ok(metrics.height == lines[0].height, "got %.2f\n", metrics.height); |
| ok(metrics.layoutHeight == 100.0, "got %.2f\n", metrics.layoutHeight); |
| ok(metrics.lineCount == 1, "got %d\n", metrics.lineCount); |
| |
| /* maxwidth is 100, far alignment */ |
| hr = IDWriteTextLayout_SetParagraphAlignment(layout, DWRITE_PARAGRAPH_ALIGNMENT_FAR); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextLayout_GetMetrics(layout, &metrics); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| ok(metrics.top == metrics.layoutHeight - metrics.height, "got %.2f\n", metrics.top); |
| ok(metrics.height == lines[0].height, "got %.2f\n", metrics.height); |
| ok(metrics.layoutHeight == 100.0, "got %.2f\n", metrics.layoutHeight); |
| ok(metrics.lineCount == 1, "got %d\n", metrics.lineCount); |
| IDWriteTextLayout_Release(layout); |
| |
| /* initially created with centered alignment */ |
| hr = IDWriteTextFormat_SetParagraphAlignment(format, DWRITE_PARAGRAPH_ALIGNMENT_CENTER); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFactory_CreateTextLayout(factory, strW, 1, format, 500.0, 100.0, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextLayout_GetMetrics(layout, &metrics); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| ok(metrics.top == (metrics.layoutHeight - lines[0].height) / 2, "got %.2f\n", metrics.top); |
| ok(metrics.height == lines[0].height, "got %.2f\n", metrics.height); |
| ok(metrics.lineCount == 1, "got %d\n", metrics.lineCount); |
| IDWriteTextLayout_Release(layout); |
| |
| IDWriteTextFormat_Release(format); |
| IDWriteFactory_Release(factory); |
| } |
| |
| static void test_SetReadingDirection(void) |
| { |
| static const WCHAR strW[] = {'a',0}; |
| DWRITE_CLUSTER_METRICS clusters[1]; |
| DWRITE_TEXT_METRICS metrics; |
| IDWriteTextFormat *format; |
| IDWriteTextLayout *layout; |
| IDWriteFactory *factory; |
| DWRITE_READING_DIRECTION v; |
| DWRITE_LINE_METRICS lines[1]; |
| UINT32 count; |
| HRESULT hr; |
| |
| factory = create_factory(); |
| |
| hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, |
| DWRITE_FONT_STRETCH_NORMAL, 12.0, enusW, &format); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| v = IDWriteTextFormat_GetReadingDirection(format); |
| ok(v == DWRITE_READING_DIRECTION_LEFT_TO_RIGHT, "got %d\n", v); |
| |
| hr = IDWriteFactory_CreateTextLayout(factory, strW, 1, format, 500.0, 100.0, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| v = IDWriteTextLayout_GetReadingDirection(layout); |
| ok(v == DWRITE_READING_DIRECTION_LEFT_TO_RIGHT, "got %d\n", v); |
| |
| v = IDWriteTextFormat_GetReadingDirection(format); |
| ok(v == DWRITE_READING_DIRECTION_LEFT_TO_RIGHT, "got %d\n", v); |
| |
| hr = IDWriteTextLayout_SetReadingDirection(layout, DWRITE_READING_DIRECTION_RIGHT_TO_LEFT); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| count = 0; |
| hr = IDWriteTextLayout_GetLineMetrics(layout, lines, 1, &count); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(count == 1, "got %u\n", count); |
| |
| count = 0; |
| hr = IDWriteTextLayout_GetClusterMetrics(layout, clusters, 1, &count); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(count == 1, "got %u\n", count); |
| |
| /* leading alignment, RTL */ |
| hr = IDWriteTextLayout_GetMetrics(layout, &metrics); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| ok(metrics.left == metrics.layoutWidth - clusters[0].width, "got %.2f\n", metrics.left); |
| ok(metrics.top == 0.0, "got %.2f\n", metrics.top); |
| ok(metrics.width == clusters[0].width, "got %.2f\n", metrics.width); |
| ok(metrics.height == lines[0].height, "got %.2f\n", metrics.height); |
| ok(metrics.layoutWidth == 500.0, "got %.2f\n", metrics.layoutWidth); |
| ok(metrics.layoutHeight == 100.0, "got %.2f\n", metrics.layoutHeight); |
| ok(metrics.lineCount == 1, "got %d\n", metrics.lineCount); |
| |
| /* trailing alignment, RTL */ |
| hr = IDWriteTextLayout_SetTextAlignment(layout, DWRITE_TEXT_ALIGNMENT_TRAILING); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextLayout_GetMetrics(layout, &metrics); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| ok(metrics.left == 0.0, "got %.2f\n", metrics.left); |
| ok(metrics.top == 0.0, "got %.2f\n", metrics.top); |
| ok(metrics.width == clusters[0].width, "got %.2f\n", metrics.width); |
| ok(metrics.height == lines[0].height, "got %.2f\n", metrics.height); |
| ok(metrics.layoutWidth == 500.0, "got %.2f\n", metrics.layoutWidth); |
| ok(metrics.layoutHeight == 100.0, "got %.2f\n", metrics.layoutHeight); |
| ok(metrics.lineCount == 1, "got %d\n", metrics.lineCount); |
| |
| /* centered alignment, RTL */ |
| hr = IDWriteTextLayout_SetTextAlignment(layout, DWRITE_TEXT_ALIGNMENT_CENTER); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextLayout_GetMetrics(layout, &metrics); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| ok(metrics.left == (metrics.layoutWidth - clusters[0].width) / 2.0, "got %.2f\n", metrics.left); |
| ok(metrics.top == 0.0, "got %.2f\n", metrics.top); |
| ok(metrics.width == clusters[0].width, "got %.2f\n", metrics.width); |
| ok(metrics.height == lines[0].height, "got %.2f\n", metrics.height); |
| ok(metrics.layoutWidth == 500.0, "got %.2f\n", metrics.layoutWidth); |
| ok(metrics.layoutHeight == 100.0, "got %.2f\n", metrics.layoutHeight); |
| ok(metrics.lineCount == 1, "got %d\n", metrics.lineCount); |
| |
| IDWriteTextLayout_Release(layout); |
| |
| IDWriteTextFormat_Release(format); |
| IDWriteFactory_Release(factory); |
| } |
| |
| static inline FLOAT get_scaled_font_metric(UINT32 metric, FLOAT emSize, const DWRITE_FONT_METRICS *metrics) |
| { |
| return (FLOAT)metric * emSize / (FLOAT)metrics->designUnitsPerEm; |
| } |
| |
| static FLOAT snap_coord(const DWRITE_MATRIX *m, FLOAT ppdip, FLOAT coord) |
| { |
| FLOAT vec[2], det, vec2[2]; |
| BOOL transform; |
| |
| /* has to be a diagonal matrix */ |
| if ((ppdip <= 0.0) || |
| (m->m11 * m->m22 != 0.0 && (m->m12 != 0.0 || m->m21 != 0.0)) || |
| (m->m12 * m->m21 != 0.0 && (m->m11 != 0.0 || m->m22 != 0.0))) |
| return coord; |
| |
| det = m->m11 * m->m22 - m->m12 * m->m21; |
| transform = fabsf(det) > 1e-10; |
| |
| if (transform) { |
| /* apply transform */ |
| vec[0] = 0.0; |
| vec[1] = coord * ppdip; |
| |
| vec2[0] = m->m11 * vec[0] + m->m21 * vec[1] + m->dx; |
| vec2[1] = m->m12 * vec[0] + m->m22 * vec[1] + m->dy; |
| |
| /* snap */ |
| vec2[0] = floorf(vec2[0] + 0.5f); |
| vec2[1] = floorf(vec2[1] + 0.5f); |
| |
| /* apply inverted transform */ |
| vec[1] = (-m->m12 * vec2[0] + m->m11 * vec2[1] - (m->m11 * m->dy - m->m12 * m->dx)) / det; |
| vec[1] /= ppdip; |
| } |
| else |
| vec[1] = floorf(coord * ppdip + 0.5f) / ppdip; |
| return vec[1]; |
| } |
| |
| static inline BOOL float_eq(FLOAT left, FLOAT right) |
| { |
| int x = *(int *)&left; |
| int y = *(int *)&right; |
| |
| if (x < 0) |
| x = INT_MIN - x; |
| if (y < 0) |
| y = INT_MIN - y; |
| |
| return abs(x - y) <= 16; |
| } |
| |
| struct snapping_test { |
| DWRITE_MATRIX m; |
| FLOAT ppdip; |
| }; |
| |
| static struct snapping_test snapping_tests[] = { |
| { { 0.0, 1.0, 2.0, 0.0, 0.2, 0.3 }, 1.0 }, |
| { { 0.0, 1.0, 2.0, 0.0, 0.0, 0.0 }, 1.0 }, |
| { { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }, 1.0 }, /* identity transform */ |
| { { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }, 0.9 }, |
| { { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }, -1.0 }, |
| { { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }, 0.0 }, |
| { { 1.0, 0.0, 0.0, 1.0, 0.0, 0.3 }, 1.0 }, /* simple Y shift */ |
| { { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }, 10.0 }, /* identity, 10 ppdip */ |
| { { 1.0, 0.0, 0.0, 10.0, 0.0, 0.0 }, 10.0 }, |
| { { 0.0, 1.0, 1.0, 0.0, 0.2, 0.6 }, 1.0 }, |
| { { 0.0, 2.0, 2.0, 0.0, 0.2, 0.6 }, 1.0 }, |
| { { 0.0, 0.5, -0.5, 0.0, 0.2, 0.6 }, 1.0 }, |
| { { 1.0, 2.0, 0.0, 1.0, 0.2, 0.6 }, 1.0 }, |
| { { 1.0, 1.0, 0.0, 1.0, 0.2, 0.6 }, 1.0 }, |
| { { 0.5, 0.5, -0.5, 0.5, 0.2, 0.6 }, 1.0 }, /* 45 degrees rotation */ |
| { { 0.5, 0.5, -0.5, 0.5, 0.0, 0.0 }, 100.0 }, /* 45 degrees rotation */ |
| { { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }, 100.0 }, |
| { { 0.0, 1.0, -1.0, 0.0, 0.2, 0.6 }, 1.0 }, /* 90 degrees rotation */ |
| { { -1.0, 0.0, 0.0, -1.0, 0.2, 0.6 }, 1.0 }, /* 180 degrees rotation */ |
| { { 0.0, -1.0, 1.0, 0.0, 0.2, 0.6 }, 1.0 }, /* 270 degrees rotation */ |
| { { 1.0, 0.0, 0.0, 1.0,-0.1, 0.2 }, 1.0 }, |
| { { 0.0, 1.0, -1.0, 0.0,-0.2,-0.3 }, 1.0 }, /* 90 degrees rotation */ |
| { { -1.0, 0.0, 0.0, -1.0,-0.3,-1.6 }, 1.0 }, /* 180 degrees rotation */ |
| { { 0.0, -1.0, 1.0, 0.0,-0.7, 0.6 }, 10.0 }, /* 270 degrees rotation */ |
| { { 0.0, 2.0, 1.0, 0.0, 0.2, 0.6 }, 1.0 }, |
| { { 0.0, 0.0, 1.0, 0.0, 0.0, 0.0 }, 1.0 }, |
| { { 3.0, 0.0, 0.0, 5.0, 0.2,-0.3 }, 10.0 }, |
| { { 0.0, -3.0, 5.0, 0.0,-0.1, 0.7 }, 10.0 }, |
| }; |
| |
| static DWRITE_MATRIX compattransforms[] = { |
| { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }, |
| { 1.0, 0.0, 0.0, 1.0, 0.2, 0.3 }, |
| { 2.0, 0.0, 0.0, 2.0, 0.2, 0.3 }, |
| { 2.0, 1.0, 2.0, 2.0, 0.2, 0.3 }, |
| }; |
| |
| static void test_pixelsnapping(void) |
| { |
| static const WCHAR strW[] = {'a',0}; |
| IDWriteTextLayout *layout, *layout2; |
| struct renderer_context ctxt; |
| DWRITE_FONT_METRICS metrics; |
| IDWriteTextFormat *format; |
| IDWriteFontFace *fontface; |
| IDWriteFactory *factory; |
| FLOAT baseline, originX; |
| HRESULT hr; |
| int i, j; |
| |
| factory = create_factory(); |
| |
| hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, |
| DWRITE_FONT_STRETCH_NORMAL, 12.0, enusW, &format); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| fontface = get_fontface_from_format(format); |
| IDWriteFontFace_GetMetrics(fontface, &metrics); |
| |
| hr = IDWriteFactory_CreateTextLayout(factory, strW, 1, format, 500.0, 100.0, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| /* disabled snapping */ |
| ctxt.snapping_disabled = TRUE; |
| ctxt.gdicompat = FALSE; |
| ctxt.use_gdi_natural = FALSE; |
| ctxt.ppdip = 1.0f; |
| memset(&ctxt.m, 0, sizeof(ctxt.m)); |
| ctxt.m.m11 = ctxt.m.m22 = 1.0; |
| originX = 0.1; |
| |
| hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, originX, 0.0); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| baseline = get_scaled_font_metric(metrics.ascent, 12.0, &metrics); |
| ok(ctxt.originX == originX, "got %f, originX %f\n", ctxt.originX, originX); |
| ok(ctxt.originY == baseline, "got %f, baseline %f\n", ctxt.originY, baseline); |
| ok(floor(baseline) != baseline, "got %f\n", baseline); |
| |
| ctxt.snapping_disabled = FALSE; |
| |
| for (i = 0; i < sizeof(snapping_tests)/sizeof(snapping_tests[0]); i++) { |
| struct snapping_test *ptr = &snapping_tests[i]; |
| FLOAT expectedY; |
| |
| ctxt.m = ptr->m; |
| ctxt.ppdip = ptr->ppdip; |
| ctxt.originX = 678.9; |
| ctxt.originY = 678.9; |
| |
| expectedY = snap_coord(&ctxt.m, ctxt.ppdip, baseline); |
| hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, originX, 0.0); |
| ok(hr == S_OK, "%d: got 0x%08x\n", i, hr); |
| ok(ctxt.originX == originX, "%d: got %f, originX %f\n", i, ctxt.originX, originX); |
| ok(float_eq(ctxt.originY, expectedY), "%d: got %f, expected %f, baseline %f\n", |
| i, ctxt.originY, expectedY, baseline); |
| |
| /* gdicompat layout transform doesn't affect snapping */ |
| for (j = 0; j < sizeof(compattransforms)/sizeof(compattransforms[0]); j++) { |
| hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, strW, 1, format, 500.0, 100.0, |
| 1.0, &compattransforms[j], FALSE, &layout2); |
| ok(hr == S_OK, "%d: got 0x%08x\n", i, hr); |
| |
| expectedY = snap_coord(&ctxt.m, ctxt.ppdip, baseline); |
| hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, originX, 0.0); |
| ok(hr == S_OK, "%d: got 0x%08x\n", i, hr); |
| ok(ctxt.originX == originX, "%d: got %f, originX %f\n", i, ctxt.originX, originX); |
| ok(float_eq(ctxt.originY, expectedY), "%d: got %f, expected %f, baseline %f\n", |
| i, ctxt.originY, expectedY, baseline); |
| |
| IDWriteTextLayout_Release(layout2); |
| } |
| } |
| |
| IDWriteTextLayout_Release(layout); |
| IDWriteTextFormat_Release(format); |
| IDWriteFontFace_Release(fontface); |
| IDWriteFactory_Release(factory); |
| } |
| |
| static void test_SetWordWrapping(void) |
| { |
| static const WCHAR strW[] = {'a',' ','s','o','m','e',' ','t','e','x','t',' ','a','n','d', |
| ' ','a',' ','b','i','t',' ','m','o','r','e','\n','b'}; |
| IDWriteTextFormat *format; |
| IDWriteTextLayout *layout; |
| IDWriteFactory *factory; |
| DWRITE_WORD_WRAPPING v; |
| UINT32 count; |
| HRESULT hr; |
| |
| factory = create_factory(); |
| |
| hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, |
| DWRITE_FONT_STRETCH_NORMAL, 12.0, enusW, &format); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| v = IDWriteTextFormat_GetWordWrapping(format); |
| ok(v == DWRITE_WORD_WRAPPING_WRAP, "got %d\n", v); |
| |
| hr = IDWriteFactory_CreateTextLayout(factory, strW, sizeof(strW)/sizeof(WCHAR), format, 10.0f, 100.0f, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| v = IDWriteTextLayout_GetWordWrapping(layout); |
| ok(v == DWRITE_WORD_WRAPPING_WRAP, "got %d\n", v); |
| |
| hr = IDWriteTextLayout_SetWordWrapping(layout, DWRITE_WORD_WRAPPING_NO_WRAP); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextLayout_SetWordWrapping(layout, DWRITE_WORD_WRAPPING_NO_WRAP); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| v = IDWriteTextFormat_GetWordWrapping(format); |
| ok(v == DWRITE_WORD_WRAPPING_WRAP, "got %d\n", v); |
| |
| /* disable wrapping, text has explicit newline */ |
| hr = IDWriteTextLayout_SetWordWrapping(layout, DWRITE_WORD_WRAPPING_NO_WRAP); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| count = 0; |
| hr = IDWriteTextLayout_GetLineMetrics(layout, NULL, 0, &count); |
| ok(hr == E_NOT_SUFFICIENT_BUFFER, "got 0x%08x\n", hr); |
| ok(count == 2, "got %u\n", count); |
| |
| hr = IDWriteTextLayout_SetWordWrapping(layout, DWRITE_WORD_WRAPPING_WRAP); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| count = 0; |
| hr = IDWriteTextLayout_GetLineMetrics(layout, NULL, 0, &count); |
| ok(hr == E_NOT_SUFFICIENT_BUFFER, "got 0x%08x\n", hr); |
| ok(count > 2, "got %u\n", count); |
| |
| IDWriteTextLayout_Release(layout); |
| IDWriteTextFormat_Release(format); |
| IDWriteFactory_Release(factory); |
| } |
| |
| /* Collection dedicated to fallback testing */ |
| |
| static const WCHAR g_blahfontW[] = {'B','l','a','h',0}; |
| static HRESULT WINAPI fontcollection_QI(IDWriteFontCollection *iface, REFIID riid, void **obj) |
| { |
| if (IsEqualIID(riid, &IID_IDWriteFontCollection) || IsEqualIID(riid, &IID_IUnknown)) { |
| *obj = iface; |
| IDWriteFontCollection_AddRef(iface); |
| return S_OK; |
| } |
| |
| *obj = NULL; |
| return E_NOINTERFACE; |
| } |
| |
| static ULONG WINAPI fontcollection_AddRef(IDWriteFontCollection *iface) |
| { |
| return 2; |
| } |
| |
| static ULONG WINAPI fontcollection_Release(IDWriteFontCollection *iface) |
| { |
| return 1; |
| } |
| |
| static UINT32 WINAPI fontcollection_GetFontFamilyCount(IDWriteFontCollection *iface) |
| { |
| ok(0, "unexpected call\n"); |
| return 0; |
| } |
| |
| static HRESULT WINAPI fontcollection_GetFontFamily(IDWriteFontCollection *iface, UINT32 index, IDWriteFontFamily **family) |
| { |
| if (index == 123456) { |
| IDWriteFactory *factory = create_factory(); |
| IDWriteFontCollection *syscollection; |
| BOOL exists; |
| HRESULT hr; |
| |
| hr = IDWriteFactory_GetSystemFontCollection(factory, &syscollection, FALSE); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFontCollection_FindFamilyName(syscollection, tahomaW, &index, &exists); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFontCollection_GetFontFamily(syscollection, index, family); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| IDWriteFontCollection_Release(syscollection); |
| IDWriteFactory_Release(factory); |
| return S_OK; |
| } |
| ok(0, "unexpected call\n"); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI fontcollection_FindFamilyName(IDWriteFontCollection *iface, WCHAR const *name, UINT32 *index, BOOL *exists) |
| { |
| if (!lstrcmpW(name, g_blahfontW)) { |
| *index = 123456; |
| *exists = TRUE; |
| return S_OK; |
| } |
| ok(0, "unexpected call, name %s\n", wine_dbgstr_w(name)); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI fontcollection_GetFontFromFontFace(IDWriteFontCollection *iface, IDWriteFontFace *face, IDWriteFont **font) |
| { |
| ok(0, "unexpected call\n"); |
| return E_NOTIMPL; |
| } |
| |
| static const IDWriteFontCollectionVtbl fallbackcollectionvtbl = { |
| fontcollection_QI, |
| fontcollection_AddRef, |
| fontcollection_Release, |
| fontcollection_GetFontFamilyCount, |
| fontcollection_GetFontFamily, |
| fontcollection_FindFamilyName, |
| fontcollection_GetFontFromFontFace |
| }; |
| |
| static IDWriteFontCollection fallbackcollection = { &fallbackcollectionvtbl }; |
| |
| static void test_MapCharacters(void) |
| { |
| static const WCHAR strW[] = {'a','b','c',0}; |
| static const WCHAR str2W[] = {'a',0x3058,'b',0}; |
| IDWriteLocalizedStrings *strings; |
| IDWriteFontFallback *fallback; |
| IDWriteFactory2 *factory2; |
| IDWriteFactory *factory; |
| UINT32 mappedlength; |
| IDWriteFont *font; |
| WCHAR buffW[50]; |
| BOOL exists; |
| FLOAT scale; |
| HRESULT hr; |
| |
| factory = create_factory(); |
| |
| hr = IDWriteFactory_QueryInterface(factory, &IID_IDWriteFactory2, (void**)&factory2); |
| IDWriteFactory_Release(factory); |
| if (hr != S_OK) { |
| win_skip("MapCharacters() is not supported\n"); |
| return; |
| } |
| |
| fallback = NULL; |
| hr = IDWriteFactory2_GetSystemFontFallback(factory2, &fallback); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(fallback != NULL, "got %p\n", fallback); |
| |
| mappedlength = 1; |
| scale = 0.0f; |
| font = (void*)0xdeadbeef; |
| hr = IDWriteFontFallback_MapCharacters(fallback, NULL, 0, 0, NULL, NULL, DWRITE_FONT_WEIGHT_NORMAL, |
| DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, &mappedlength, &font, &scale); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| ok(mappedlength == 0, "got %u\n", mappedlength); |
| ok(scale == 1.0f, "got %f\n", scale); |
| ok(font == NULL, "got %p\n", font); |
| |
| /* zero length source */ |
| g_source = strW; |
| mappedlength = 1; |
| scale = 0.0f; |
| font = (void*)0xdeadbeef; |
| hr = IDWriteFontFallback_MapCharacters(fallback, &analysissource, 0, 0, NULL, NULL, DWRITE_FONT_WEIGHT_NORMAL, |
| DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, &mappedlength, &font, &scale); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(mappedlength == 0, "got %u\n", mappedlength); |
| ok(scale == 1.0f, "got %f\n", scale); |
| ok(font == NULL, "got %p\n", font); |
| |
| g_source = strW; |
| mappedlength = 0; |
| scale = 0.0f; |
| font = NULL; |
| hr = IDWriteFontFallback_MapCharacters(fallback, &analysissource, 0, 1, NULL, NULL, DWRITE_FONT_WEIGHT_NORMAL, |
| DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, &mappedlength, &font, &scale); |
| todo_wine { |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(mappedlength == 1, "got %u\n", mappedlength); |
| } |
| ok(scale == 1.0f, "got %f\n", scale); |
| todo_wine |
| ok(font != NULL, "got %p\n", font); |
| if (font) { |
| IDWriteFont_Release(font); |
| } |
| /* same latin text, full length */ |
| g_source = strW; |
| mappedlength = 0; |
| scale = 0.0f; |
| font = NULL; |
| hr = IDWriteFontFallback_MapCharacters(fallback, &analysissource, 0, 3, NULL, NULL, DWRITE_FONT_WEIGHT_NORMAL, |
| DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, &mappedlength, &font, &scale); |
| todo_wine { |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(mappedlength == 3, "got %u\n", mappedlength); |
| } |
| ok(scale == 1.0f, "got %f\n", scale); |
| todo_wine |
| ok(font != NULL, "got %p\n", font); |
| if (font) { |
| IDWriteFont_Release(font); |
| } |
| /* string 'a\x3058b' */ |
| g_source = str2W; |
| mappedlength = 0; |
| scale = 0.0f; |
| font = NULL; |
| hr = IDWriteFontFallback_MapCharacters(fallback, &analysissource, 0, 3, NULL, NULL, DWRITE_FONT_WEIGHT_NORMAL, |
| DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, &mappedlength, &font, &scale); |
| todo_wine { |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(mappedlength == 1, "got %u\n", mappedlength); |
| } |
| ok(scale == 1.0f, "got %f\n", scale); |
| todo_wine |
| ok(font != NULL, "got %p\n", font); |
| if (font) { |
| IDWriteFont_Release(font); |
| } |
| g_source = str2W; |
| mappedlength = 0; |
| scale = 0.0f; |
| font = NULL; |
| hr = IDWriteFontFallback_MapCharacters(fallback, &analysissource, 1, 2, NULL, NULL, DWRITE_FONT_WEIGHT_NORMAL, |
| DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, &mappedlength, &font, &scale); |
| todo_wine { |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(mappedlength == 1, "got %u\n", mappedlength); |
| } |
| ok(scale == 1.0f, "got %f\n", scale); |
| todo_wine |
| ok(font != NULL, "got %p\n", font); |
| if (font) { |
| /* font returned for Hiragana character, check if it supports Latin too */ |
| exists = FALSE; |
| hr = IDWriteFont_HasCharacter(font, 'b', &exists); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(exists, "got %d\n", exists); |
| |
| IDWriteFont_Release(font); |
| } |
| /* Try with explicit collection, Tahoma will be forced. */ |
| /* 1. Latin part */ |
| g_source = str2W; |
| mappedlength = 0; |
| scale = 0.0f; |
| font = NULL; |
| hr = IDWriteFontFallback_MapCharacters(fallback, &analysissource, 0, 3, &fallbackcollection, g_blahfontW, DWRITE_FONT_WEIGHT_NORMAL, |
| DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, &mappedlength, &font, &scale); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(mappedlength == 1, "got %u\n", mappedlength); |
| ok(scale == 1.0f, "got %f\n", scale); |
| ok(font != NULL, "got %p\n", font); |
| |
| exists = FALSE; |
| hr = IDWriteFont_GetInformationalStrings(font, DWRITE_INFORMATIONAL_STRING_WIN32_FAMILY_NAMES, &strings, &exists); |
| ok(hr == S_OK && exists, "got 0x%08x, exists %d\n", hr, exists); |
| hr = IDWriteLocalizedStrings_GetString(strings, 0, buffW, sizeof(buffW)/sizeof(WCHAR)); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(!lstrcmpW(buffW, tahomaW), "%s\n", wine_dbgstr_w(buffW)); |
| IDWriteLocalizedStrings_Release(strings); |
| IDWriteFont_Release(font); |
| |
| /* 2. Hiragana character, force Tahoma font does not support Japanese */ |
| g_source = str2W; |
| mappedlength = 0; |
| scale = 0.0f; |
| font = NULL; |
| hr = IDWriteFontFallback_MapCharacters(fallback, &analysissource, 1, 1, &fallbackcollection, g_blahfontW, DWRITE_FONT_WEIGHT_NORMAL, |
| DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, &mappedlength, &font, &scale); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(mappedlength == 1, "got %u\n", mappedlength); |
| ok(scale == 1.0f, "got %f\n", scale); |
| ok(font != NULL, "got %p\n", font); |
| |
| exists = FALSE; |
| hr = IDWriteFont_GetInformationalStrings(font, DWRITE_INFORMATIONAL_STRING_WIN32_FAMILY_NAMES, &strings, &exists); |
| ok(hr == S_OK && exists, "got 0x%08x, exists %d\n", hr, exists); |
| hr = IDWriteLocalizedStrings_GetString(strings, 0, buffW, sizeof(buffW)/sizeof(WCHAR)); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| todo_wine |
| ok(lstrcmpW(buffW, tahomaW), "%s\n", wine_dbgstr_w(buffW)); |
| IDWriteLocalizedStrings_Release(strings); |
| IDWriteFont_Release(font); |
| |
| IDWriteFontFallback_Release(fallback); |
| IDWriteFactory2_Release(factory2); |
| } |
| |
| static void test_FontFallbackBuilder(void) |
| { |
| static const WCHAR localeW[] = {'l','o','c','a','l','e',0}; |
| static const WCHAR strW[] = {'A',0}; |
| IDWriteFontFallback *fallback, *fallback2; |
| IDWriteFontFallbackBuilder *builder; |
| DWRITE_UNICODE_RANGE range; |
| IDWriteFactory2 *factory2; |
| IDWriteFactory *factory; |
| const WCHAR *familyW; |
| UINT32 mappedlength; |
| IDWriteFont *font; |
| FLOAT scale; |
| HRESULT hr; |
| ULONG ref; |
| |
| factory = create_factory(); |
| |
| hr = IDWriteFactory_QueryInterface(factory, &IID_IDWriteFactory2, (void**)&factory2); |
| IDWriteFactory_Release(factory); |
| |
| if (hr != S_OK) { |
| win_skip("IDWriteFontFallbackBuilder is not supported\n"); |
| return; |
| } |
| |
| EXPECT_REF(factory2, 1); |
| hr = IDWriteFactory2_CreateFontFallbackBuilder(factory2, &builder); |
| EXPECT_REF(factory2, 2); |
| |
| fallback = NULL; |
| EXPECT_REF(factory2, 2); |
| EXPECT_REF(builder, 1); |
| hr = IDWriteFontFallbackBuilder_CreateFontFallback(builder, &fallback); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| EXPECT_REF(factory2, 3); |
| EXPECT_REF(fallback, 1); |
| EXPECT_REF(builder, 1); |
| |
| IDWriteFontFallback_AddRef(fallback); |
| EXPECT_REF(builder, 1); |
| EXPECT_REF(fallback, 2); |
| EXPECT_REF(factory2, 3); |
| IDWriteFontFallback_Release(fallback); |
| |
| /* New instance is created every time, even if mappings have not changed. */ |
| hr = IDWriteFontFallbackBuilder_CreateFontFallback(builder, &fallback2); |
| ok(hr == S_OK, "Failed to create fallback object, hr %#x.\n", hr); |
| ok(fallback != fallback2, "Unexpected fallback instance.\n"); |
| IDWriteFontFallback_Release(fallback2); |
| |
| hr = IDWriteFontFallbackBuilder_AddMapping(builder, NULL, 0, NULL, 0, NULL, NULL, NULL, 0.0f); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| |
| range.first = 'A'; |
| range.last = 'B'; |
| hr = IDWriteFontFallbackBuilder_AddMapping(builder, &range, 0, NULL, 0, NULL, NULL, NULL, 0.0f); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFontFallbackBuilder_AddMapping(builder, &range, 0, NULL, 0, NULL, NULL, NULL, 1.0f); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFontFallbackBuilder_AddMapping(builder, &range, 0, &familyW, 1, NULL, NULL, NULL, 1.0f); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFontFallbackBuilder_AddMapping(builder, NULL, 0, &familyW, 1, NULL, NULL, NULL, 1.0f); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| |
| /* negative scaling factor */ |
| range.first = range.last = 0; |
| familyW = g_blahfontW; |
| hr = IDWriteFontFallbackBuilder_AddMapping(builder, &range, 1, &familyW, 1, NULL, NULL, NULL, -1.0f); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFontFallbackBuilder_AddMapping(builder, &range, 1, &familyW, 1, NULL, NULL, NULL, 0.0f); |
| ok(hr == S_OK, "Unexected hr %#x.\n", hr); |
| |
| /* empty range */ |
| range.first = range.last = 0; |
| familyW = g_blahfontW; |
| hr = IDWriteFontFallbackBuilder_AddMapping(builder, &range, 1, &familyW, 1, NULL, NULL, NULL, 1.0f); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| range.first = range.last = 0; |
| hr = IDWriteFontFallbackBuilder_AddMapping(builder, &range, 1, &familyW, 1, NULL, NULL, NULL, 2.0f); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| range.first = range.last = 'A'; |
| hr = IDWriteFontFallbackBuilder_AddMapping(builder, &range, 1, &familyW, 1, NULL, NULL, NULL, 3.0f); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| range.first = 'B'; |
| range.last = 'A'; |
| hr = IDWriteFontFallbackBuilder_AddMapping(builder, &range, 1, &familyW, 1, NULL, NULL, NULL, 4.0f); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| IDWriteFontFallback_Release(fallback); |
| |
| if (0) /* crashes on native */ |
| hr = IDWriteFontFallbackBuilder_CreateFontFallback(builder, NULL); |
| |
| hr = IDWriteFontFallbackBuilder_CreateFontFallback(builder, &fallback); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| /* fallback font missing from system collection */ |
| g_source = strW; |
| mappedlength = 0; |
| scale = 0.0f; |
| font = (void*)0xdeadbeef; |
| hr = IDWriteFontFallback_MapCharacters(fallback, &analysissource, 0, 1, NULL, NULL, DWRITE_FONT_WEIGHT_NORMAL, |
| DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, &mappedlength, &font, &scale); |
| todo_wine { |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(mappedlength == 1, "got %u\n", mappedlength); |
| ok(scale == 1.0f, "got %f\n", scale); |
| ok(font == NULL, "got %p\n", font); |
| } |
| IDWriteFontFallback_Release(fallback); |
| |
| /* remap with custom collection */ |
| range.first = range.last = 'A'; |
| hr = IDWriteFontFallbackBuilder_AddMapping(builder, &range, 1, &familyW, 1, &fallbackcollection, NULL, NULL, 5.0f); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFontFallbackBuilder_CreateFontFallback(builder, &fallback); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| g_source = strW; |
| mappedlength = 0; |
| scale = 0.0f; |
| font = NULL; |
| hr = IDWriteFontFallback_MapCharacters(fallback, &analysissource, 0, 1, NULL, NULL, DWRITE_FONT_WEIGHT_NORMAL, |
| DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, &mappedlength, &font, &scale); |
| todo_wine { |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(mappedlength == 1, "got %u\n", mappedlength); |
| ok(scale == 5.0f, "got %f\n", scale); |
| ok(font != NULL, "got %p\n", font); |
| } |
| if (font) |
| IDWriteFont_Release(font); |
| |
| IDWriteFontFallback_Release(fallback); |
| |
| range.first = 'B'; |
| range.last = 'A'; |
| hr = IDWriteFontFallbackBuilder_AddMapping(builder, &range, 1, &familyW, 1, &fallbackcollection, NULL, NULL, 6.0f); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFontFallbackBuilder_CreateFontFallback(builder, &fallback); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| g_source = strW; |
| mappedlength = 0; |
| scale = 0.0f; |
| font = NULL; |
| hr = IDWriteFontFallback_MapCharacters(fallback, &analysissource, 0, 1, NULL, NULL, DWRITE_FONT_WEIGHT_NORMAL, |
| DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, &mappedlength, &font, &scale); |
| todo_wine { |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(mappedlength == 1, "got %u\n", mappedlength); |
| ok(scale == 5.0f, "got %f\n", scale); |
| ok(font != NULL, "got %p\n", font); |
| } |
| if (font) |
| IDWriteFont_Release(font); |
| |
| IDWriteFontFallback_Release(fallback); |
| |
| /* explicit locale */ |
| range.first = 'A'; |
| range.last = 'B'; |
| hr = IDWriteFontFallbackBuilder_AddMapping(builder, &range, 1, &familyW, 1, &fallbackcollection, localeW, NULL, 6.0f); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFontFallbackBuilder_CreateFontFallback(builder, &fallback); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| g_source = strW; |
| mappedlength = 0; |
| scale = 0.0f; |
| font = NULL; |
| hr = IDWriteFontFallback_MapCharacters(fallback, &analysissource, 0, 1, NULL, NULL, DWRITE_FONT_WEIGHT_NORMAL, |
| DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, &mappedlength, &font, &scale); |
| todo_wine { |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(mappedlength == 1, "got %u\n", mappedlength); |
| ok(scale == 5.0f, "got %f\n", scale); |
| ok(font != NULL, "got %p\n", font); |
| } |
| if (font) |
| IDWriteFont_Release(font); |
| |
| IDWriteFontFallback_Release(fallback); |
| |
| IDWriteFontFallbackBuilder_Release(builder); |
| ref = IDWriteFactory2_Release(factory2); |
| ok(ref == 0, "Factory is not released, ref %u.\n", ref); |
| } |
| |
| static void test_SetTypography(void) |
| { |
| static const WCHAR strW[] = {'a','f','i','b',0}; |
| IDWriteTypography *typography, *typography2; |
| IDWriteTextFormat *format; |
| IDWriteTextLayout *layout; |
| DWRITE_TEXT_RANGE range; |
| IDWriteFactory *factory; |
| HRESULT hr; |
| |
| factory = create_factory(); |
| |
| hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, |
| DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 1000.0, 1000.0, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| IDWriteTextFormat_Release(format); |
| |
| hr = IDWriteFactory_CreateTypography(factory, &typography); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| EXPECT_REF(typography, 1); |
| range.startPosition = 0; |
| range.length = 2; |
| hr = IDWriteTextLayout_SetTypography(layout, typography, range); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| EXPECT_REF(typography, 2); |
| |
| hr = IDWriteTextLayout_GetTypography(layout, 0, &typography2, NULL); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(typography2 == typography, "got %p, expected %p\n", typography2, typography); |
| IDWriteTypography_Release(typography2); |
| IDWriteTypography_Release(typography); |
| |
| hr = IDWriteFactory_CreateTypography(factory, &typography2); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| range.startPosition = 0; |
| range.length = 1; |
| hr = IDWriteTextLayout_SetTypography(layout, typography2, range); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| EXPECT_REF(typography2, 2); |
| IDWriteTypography_Release(typography2); |
| |
| hr = IDWriteTextLayout_GetTypography(layout, 0, &typography, &range); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(range.length == 1, "got %u\n", range.length); |
| |
| IDWriteTypography_Release(typography); |
| |
| IDWriteTextLayout_Release(layout); |
| IDWriteFactory_Release(factory); |
| } |
| |
| static void test_SetLastLineWrapping(void) |
| { |
| static const WCHAR strW[] = {'a',0}; |
| IDWriteTextLayout2 *layout2; |
| IDWriteTextFormat1 *format1; |
| IDWriteTextLayout *layout; |
| IDWriteTextFormat *format; |
| IDWriteFactory *factory; |
| HRESULT hr; |
| BOOL ret; |
| |
| factory = create_factory(); |
| |
| hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, |
| DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextFormat_QueryInterface(format, &IID_IDWriteTextFormat1, (void**)&format1); |
| IDWriteTextFormat_Release(format); |
| if (hr != S_OK) { |
| win_skip("SetLastLineWrapping() is not supported\n"); |
| IDWriteFactory_Release(factory); |
| return; |
| } |
| |
| ret = IDWriteTextFormat1_GetLastLineWrapping(format1); |
| ok(ret, "got %d\n", ret); |
| |
| hr = IDWriteTextFormat1_SetLastLineWrapping(format1, FALSE); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFactory_CreateTextLayout(factory, strW, 1, (IDWriteTextFormat*)format1, 1000.0, 1000.0, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextLayout2, (void**)&layout2); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| IDWriteTextLayout_Release(layout); |
| |
| ret = IDWriteTextLayout2_GetLastLineWrapping(layout2); |
| ok(!ret, "got %d\n", ret); |
| |
| hr = IDWriteTextLayout2_SetLastLineWrapping(layout2, TRUE); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| IDWriteTextLayout2_Release(layout2); |
| IDWriteTextFormat1_Release(format1); |
| IDWriteFactory_Release(factory); |
| } |
| |
| static void test_SetOpticalAlignment(void) |
| { |
| static const WCHAR strW[] = {'a',0}; |
| DWRITE_OPTICAL_ALIGNMENT alignment; |
| IDWriteTextLayout2 *layout2; |
| IDWriteTextFormat1 *format1; |
| IDWriteTextLayout *layout; |
| IDWriteTextFormat *format; |
| IDWriteFactory *factory; |
| HRESULT hr; |
| |
| factory = create_factory(); |
| |
| hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, |
| DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextFormat_QueryInterface(format, &IID_IDWriteTextFormat1, (void**)&format1); |
| IDWriteTextFormat_Release(format); |
| if (hr != S_OK) { |
| win_skip("SetOpticalAlignment() is not supported\n"); |
| IDWriteFactory_Release(factory); |
| return; |
| } |
| |
| alignment = IDWriteTextFormat1_GetOpticalAlignment(format1); |
| ok(alignment == DWRITE_OPTICAL_ALIGNMENT_NONE, "got %d\n", alignment); |
| |
| hr = IDWriteFactory_CreateTextLayout(factory, strW, 1, (IDWriteTextFormat*)format1, 1000.0, 1000.0, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextLayout2, (void**)&layout2); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| IDWriteTextLayout_Release(layout); |
| IDWriteTextFormat1_Release(format1); |
| |
| alignment = IDWriteTextLayout2_GetOpticalAlignment(layout2); |
| ok(alignment == DWRITE_OPTICAL_ALIGNMENT_NONE, "got %d\n", alignment); |
| |
| hr = IDWriteTextLayout2_QueryInterface(layout2, &IID_IDWriteTextFormat1, (void**)&format1); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| alignment = IDWriteTextFormat1_GetOpticalAlignment(format1); |
| ok(alignment == DWRITE_OPTICAL_ALIGNMENT_NONE, "got %d\n", alignment); |
| |
| hr = IDWriteTextLayout2_SetOpticalAlignment(layout2, DWRITE_OPTICAL_ALIGNMENT_NO_SIDE_BEARINGS); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextLayout2_SetOpticalAlignment(layout2, DWRITE_OPTICAL_ALIGNMENT_NO_SIDE_BEARINGS+1); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| |
| alignment = IDWriteTextFormat1_GetOpticalAlignment(format1); |
| ok(alignment == DWRITE_OPTICAL_ALIGNMENT_NO_SIDE_BEARINGS, "got %d\n", alignment); |
| |
| hr = IDWriteTextFormat1_SetOpticalAlignment(format1, DWRITE_OPTICAL_ALIGNMENT_NONE); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextFormat1_SetOpticalAlignment(format1, DWRITE_OPTICAL_ALIGNMENT_NO_SIDE_BEARINGS+1); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| |
| alignment = IDWriteTextLayout2_GetOpticalAlignment(layout2); |
| ok(alignment == DWRITE_OPTICAL_ALIGNMENT_NONE, "got %d\n", alignment); |
| |
| IDWriteTextLayout2_Release(layout2); |
| IDWriteTextFormat1_Release(format1); |
| IDWriteFactory_Release(factory); |
| } |
| |
| static const struct drawcall_entry drawunderline_seq[] = { |
| { DRAW_GLYPHRUN, {'a','e',0x0300,0}, {'e','n','-','u','s',0}, 2 }, /* reported runs can't mix different underline values */ |
| { DRAW_GLYPHRUN, {'d',0}, {'e','n','-','u','s',0}, 1 }, |
| { DRAW_UNDERLINE, {0}, {'e','n','-','u','s',0} }, |
| { DRAW_LAST_KIND } |
| }; |
| |
| static const struct drawcall_entry drawunderline2_seq[] = { |
| { DRAW_GLYPHRUN, {'a',0}, {'e','n','-','u','s',0}, 1 }, |
| { DRAW_GLYPHRUN, {'e',0}, {'e','n','-','u','s',0}, 1 }, |
| { DRAW_UNDERLINE, {0}, {'e','n','-','u','s',0} }, |
| { DRAW_LAST_KIND } |
| }; |
| |
| static const struct drawcall_entry drawunderline3_seq[] = { |
| { DRAW_GLYPHRUN, {'a',0}, {'e','n','-','c','a',0}, 1 }, |
| { DRAW_GLYPHRUN, {'e',0}, {'e','n','-','u','s',0}, 1 }, |
| { DRAW_UNDERLINE, {0}, {'e','n','-','c','a',0} }, |
| { DRAW_UNDERLINE, {0}, {'e','n','-','u','s',0} }, |
| { DRAW_LAST_KIND } |
| }; |
| |
| static const struct drawcall_entry drawunderline4_seq[] = { |
| { DRAW_GLYPHRUN, {'a',0}, {'e','n','-','u','s',0}, 1 }, |
| { DRAW_GLYPHRUN, {'e',0}, {'e','n','-','u','s',0}, 1 }, |
| { DRAW_UNDERLINE, {0}, {'e','n','-','u','s',0} }, |
| { DRAW_STRIKETHROUGH }, |
| { DRAW_LAST_KIND } |
| }; |
| |
| static void test_SetUnderline(void) |
| { |
| static const WCHAR encaW[] = {'e','n','-','C','A',0}; |
| static const WCHAR strW[] = {'a','e',0x0300,'d',0}; /* accent grave */ |
| IDWriteFontCollection *syscollection; |
| DWRITE_CLUSTER_METRICS clusters[4]; |
| IDWriteTextFormat *format; |
| IDWriteTextLayout *layout; |
| DWRITE_TEXT_RANGE range; |
| IDWriteFactory *factory; |
| UINT32 count, i; |
| HRESULT hr; |
| |
| factory = create_factory(); |
| |
| hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, |
| DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 1000.0, 1000.0, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| count = 0; |
| hr = IDWriteTextLayout_GetClusterMetrics(layout, clusters, sizeof(clusters)/sizeof(clusters[0]), &count); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| todo_wine |
| ok(count == 3, "got %u\n", count); |
| |
| range.startPosition = 0; |
| range.length = 2; |
| hr = IDWriteTextLayout_SetUnderline(layout, TRUE, range); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| count = 0; |
| hr = IDWriteTextLayout_GetClusterMetrics(layout, clusters, sizeof(clusters)/sizeof(clusters[0]), &count); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| todo_wine |
| ok(count == 3, "got %u\n", count); |
| |
| flush_sequence(sequences, RENDERER_ID); |
| hr = IDWriteTextLayout_Draw(layout, NULL, &testrenderer, 0.0, 0.0); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok_sequence(sequences, RENDERER_ID, drawunderline_seq, "draw underline test", TRUE); |
| |
| IDWriteTextLayout_Release(layout); |
| |
| /* 2 characters, same font, significantly different font size. Set underline for both, see how many |
| underline drawing calls is there. */ |
| hr = IDWriteFactory_CreateTextLayout(factory, strW, 2, format, 1000.0f, 1000.0f, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| range.startPosition = 0; |
| range.length = 2; |
| hr = IDWriteTextLayout_SetUnderline(layout, TRUE, range); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| range.startPosition = 0; |
| range.length = 1; |
| hr = IDWriteTextLayout_SetFontSize(layout, 100.0f, range); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| flush_sequence(sequences, RENDERER_ID); |
| hr = IDWriteTextLayout_Draw(layout, NULL, &testrenderer, 0.0f, 0.0f); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok_sequence(sequences, RENDERER_ID, drawunderline2_seq, "draw underline test 2", FALSE); |
| |
| /* now set different locale for second char, draw again */ |
| range.startPosition = 0; |
| range.length = 1; |
| hr = IDWriteTextLayout_SetLocaleName(layout, encaW, range); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| flush_sequence(sequences, RENDERER_ID); |
| hr = IDWriteTextLayout_Draw(layout, NULL, &testrenderer, 0.0f, 0.0f); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok_sequence(sequences, RENDERER_ID, drawunderline3_seq, "draw underline test 2", FALSE); |
| |
| IDWriteTextLayout_Release(layout); |
| |
| /* 2 characters, same font properties, first with strikethrough, both underlined */ |
| hr = IDWriteFactory_CreateTextLayout(factory, strW, 2, format, 1000.0f, 1000.0f, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| range.startPosition = 0; |
| range.length = 1; |
| hr = IDWriteTextLayout_SetStrikethrough(layout, TRUE, range); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| range.startPosition = 0; |
| range.length = 2; |
| hr = IDWriteTextLayout_SetUnderline(layout, TRUE, range); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| flush_sequence(sequences, RENDERER_ID); |
| hr = IDWriteTextLayout_Draw(layout, NULL, &testrenderer, 0.0f, 0.0f); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok_sequence(sequences, RENDERER_ID, drawunderline4_seq, "draw underline test 4", FALSE); |
| |
| IDWriteTextLayout_Release(layout); |
| |
| IDWriteTextFormat_Release(format); |
| |
| /* Test runHeight value with all available fonts */ |
| hr = IDWriteFactory_GetSystemFontCollection(factory, &syscollection, FALSE); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| count = IDWriteFontCollection_GetFontFamilyCount(syscollection); |
| |
| for (i = 0; i < count; i++) { |
| DWRITE_FONT_METRICS fontmetrics; |
| IDWriteLocalizedStrings *names; |
| struct renderer_context ctxt; |
| IDWriteFontFamily *family; |
| IDWriteFontFace *fontface; |
| IDWriteFont *font; |
| WCHAR nameW[256]; |
| BOOL exists; |
| |
| format = NULL; |
| layout = NULL; |
| |
| hr = IDWriteFontCollection_GetFontFamily(syscollection, i, &family); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFontFamily_GetFirstMatchingFont(family, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STRETCH_NORMAL, |
| DWRITE_FONT_STYLE_NORMAL, &font); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFont_CreateFontFace(font, &fontface); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFontFamily_GetFamilyNames(family, &names); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| if (!(exists = get_enus_string(names, nameW, sizeof(nameW)/sizeof(nameW[0])))) { |
| IDWriteLocalFontFileLoader *localloader; |
| IDWriteFontFileLoader *loader; |
| IDWriteFontFile *file; |
| const void *key; |
| UINT32 keysize; |
| UINT32 count; |
| |
| count = 1; |
| hr = IDWriteFontFace_GetFiles(fontface, &count, &file); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFontFile_GetLoader(file, &loader); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFontFileLoader_QueryInterface(loader, &IID_IDWriteLocalFontFileLoader, (void**)&localloader); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| IDWriteFontFileLoader_Release(loader); |
| |
| hr = IDWriteFontFile_GetReferenceKey(file, &key, &keysize); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteLocalFontFileLoader_GetFilePathFromKey(localloader, key, keysize, nameW, sizeof(nameW)/sizeof(*nameW)); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| skip("Failed to get English family name, font file %s\n", wine_dbgstr_w(nameW)); |
| |
| IDWriteLocalFontFileLoader_Release(localloader); |
| IDWriteFontFile_Release(file); |
| } |
| |
| IDWriteLocalizedStrings_Release(names); |
| IDWriteFont_Release(font); |
| |
| if (!exists) |
| goto cleanup; |
| |
| IDWriteFontFace_GetMetrics(fontface, &fontmetrics); |
| hr = IDWriteFactory_CreateTextFormat(factory, nameW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, |
| DWRITE_FONT_STRETCH_NORMAL, fontmetrics.designUnitsPerEm, enusW, &format); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFactory_CreateTextLayout(factory, strW, 2, format, 30000.0f, 100.0f, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| range.startPosition = 0; |
| range.length = 2; |
| hr = IDWriteTextLayout_SetUnderline(layout, TRUE, range); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| memset(&ctxt, 0, sizeof(ctxt)); |
| ctxt.format = format; |
| ctxt.familyW = nameW; |
| hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0f, 0.0f); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| cleanup: |
| if (layout) |
| IDWriteTextLayout_Release(layout); |
| if (format) |
| IDWriteTextFormat_Release(format); |
| IDWriteFontFace_Release(fontface); |
| IDWriteFontFamily_Release(family); |
| } |
| IDWriteFontCollection_Release(syscollection); |
| |
| IDWriteFactory_Release(factory); |
| } |
| |
| static void test_InvalidateLayout(void) |
| { |
| static const WCHAR strW[] = {'a',0}; |
| IDWriteTextLayout3 *layout3; |
| IDWriteTextLayout *layout; |
| IDWriteTextFormat *format; |
| IDWriteFactory *factory; |
| HRESULT hr; |
| |
| factory = create_factory(); |
| |
| hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, |
| DWRITE_FONT_STRETCH_NORMAL, 10.0f, enusW, &format); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFactory_CreateTextLayout(factory, strW, 1, format, 1000.0f, 1000.0f, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextLayout3, (void**)&layout3); |
| if (hr == S_OK) { |
| IDWriteTextFormat1 *format1; |
| IDWriteTextFormat2 *format2; |
| |
| hr = IDWriteTextFormat_QueryInterface(format, &IID_IDWriteTextFormat2, (void**)&format2); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| IDWriteTextFormat2_Release(format2); |
| |
| hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextFormat2, (void**)&format2); |
| ok(hr == E_NOINTERFACE, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextFormat1, (void**)&format1); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextFormat1_QueryInterface(format1, &IID_IDWriteTextFormat2, (void**)&format2); |
| ok(hr == E_NOINTERFACE, "got 0x%08x\n", hr); |
| IDWriteTextFormat1_Release(format1); |
| |
| hr = IDWriteTextLayout3_QueryInterface(layout3, &IID_IDWriteTextFormat2, (void**)&format2); |
| ok(hr == E_NOINTERFACE, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextLayout3_InvalidateLayout(layout3); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| IDWriteTextLayout3_Release(layout3); |
| } |
| else |
| win_skip("IDWriteTextLayout3::InvalidateLayout() is not supported.\n"); |
| |
| IDWriteTextLayout_Release(layout); |
| IDWriteTextFormat_Release(format); |
| IDWriteFactory_Release(factory); |
| } |
| |
| static void test_line_spacing(void) |
| { |
| static const WCHAR strW[] = {'a',0}; |
| IDWriteTextFormat2 *format2; |
| IDWriteTextLayout *layout; |
| IDWriteTextFormat *format; |
| IDWriteFactory *factory; |
| HRESULT hr; |
| |
| factory = create_factory(); |
| |
| hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, |
| DWRITE_FONT_STRETCH_NORMAL, 10.0f, enusW, &format); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextFormat_SetLineSpacing(format, DWRITE_LINE_SPACING_METHOD_DEFAULT, 0.0f, 0.0f); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextFormat_SetLineSpacing(format, DWRITE_LINE_SPACING_METHOD_DEFAULT, 0.0f, -10.0f); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextFormat_SetLineSpacing(format, DWRITE_LINE_SPACING_METHOD_DEFAULT, -10.0f, 0.0f); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextFormat_SetLineSpacing(format, DWRITE_LINE_SPACING_METHOD_PROPORTIONAL+1, 0.0f, 0.0f); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| |
| hr = IDWriteFactory_CreateTextLayout(factory, strW, 1, format, 1000.0f, 1000.0f, &layout); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextLayout_SetLineSpacing(layout, DWRITE_LINE_SPACING_METHOD_DEFAULT, 0.0f, 0.0f); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextLayout_SetLineSpacing(layout, DWRITE_LINE_SPACING_METHOD_DEFAULT, 0.0f, -10.0f); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextLayout_SetLineSpacing(layout, DWRITE_LINE_SPACING_METHOD_DEFAULT, -10.0f, 0.0f); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextLayout_SetLineSpacing(layout, DWRITE_LINE_SPACING_METHOD_PROPORTIONAL+1, 0.0f, 0.0f); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| |
| if (IDWriteTextFormat_QueryInterface(format, &IID_IDWriteTextFormat2, (void**)&format2) == S_OK) { |
| DWRITE_LINE_SPACING spacing; |
| |
| hr = IDWriteTextFormat2_GetLineSpacing(format2, &spacing); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| ok(spacing.method == DWRITE_LINE_SPACING_METHOD_DEFAULT, "got method %d\n", spacing.method); |
| ok(spacing.height == 0.0f, "got %f\n", spacing.height); |
| ok(spacing.baseline == -10.0f, "got %f\n", spacing.baseline); |
| ok(spacing.leadingBefore == 0.0f, "got %f\n", spacing.leadingBefore); |
| ok(spacing.fontLineGapUsage == DWRITE_FONT_LINE_GAP_USAGE_DEFAULT, "got %f\n", spacing.leadingBefore); |
| |
| spacing.leadingBefore = -1.0f; |
| |
| hr = IDWriteTextFormat2_SetLineSpacing(format2, &spacing); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| |
| spacing.leadingBefore = 1.1f; |
| |
| hr = IDWriteTextFormat2_SetLineSpacing(format2, &spacing); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| |
| spacing.leadingBefore = 1.0f; |
| |
| hr = IDWriteTextFormat2_SetLineSpacing(format2, &spacing); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| spacing.method = DWRITE_LINE_SPACING_METHOD_PROPORTIONAL + 1; |
| hr = IDWriteTextFormat2_SetLineSpacing(format2, &spacing); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| |
| spacing.method = DWRITE_LINE_SPACING_METHOD_PROPORTIONAL; |
| spacing.fontLineGapUsage = DWRITE_FONT_LINE_GAP_USAGE_ENABLED + 1; |
| hr = IDWriteTextFormat2_SetLineSpacing(format2, &spacing); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = IDWriteTextFormat2_GetLineSpacing(format2, &spacing); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| ok(spacing.fontLineGapUsage == DWRITE_FONT_LINE_GAP_USAGE_ENABLED + 1, "got %d\n", spacing.fontLineGapUsage); |
| |
| IDWriteTextFormat2_Release(format2); |
| } |
| |
| IDWriteTextLayout_Release(layout); |
| IDWriteTextFormat_Release(format); |
| IDWriteFactory_Release(factory); |
| } |
| |
| static void test_GetOverhangMetrics(void) |
| { |
| static const struct overhangs_test |
| { |
| FLOAT uniform_baseline; |
| DWRITE_INLINE_OBJECT_METRICS metrics; |
| DWRITE_OVERHANG_METRICS overhang_metrics; |
| DWRITE_OVERHANG_METRICS expected; |
| } overhangs_tests[] = { |
| { 16.0f, { 10.0f, 50.0f, 20.0f }, { 1.0f, 2.0f, 3.0f, 4.0f }, { 1.0f, 6.0f, 3.0f, 0.0f } }, |
| { 15.0f, { 10.0f, 50.0f, 20.0f }, { 1.0f, 2.0f, 3.0f, 4.0f }, { 1.0f, 7.0f, 3.0f, -1.0f } }, |
| { 16.0f, { 10.0f, 50.0f, 20.0f }, { -1.0f, 0.0f, -3.0f, 4.0f }, { -1.0f, 4.0f, -3.0f, 0.0f } }, |
| { 15.0f, { 10.0f, 50.0f, 20.0f }, { -1.0f, 10.0f, 3.0f, -4.0f }, { -1.0f, 15.0f, 3.0f, -9.0f } }, |
| }; |
| static const WCHAR strW[] = {'A',0}; |
| IDWriteFactory *factory; |
| IDWriteTextFormat *format; |
| IDWriteTextLayout *layout; |
| HRESULT hr; |
| UINT32 i; |
| |
| factory = create_factory(); |
| |
| hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, |
| DWRITE_FONT_STRETCH_NORMAL, 100.0f, enusW, &format); |
| ok(hr == S_OK, "Failed to create text format, hr %#x.\n", hr); |
| |
| hr = IDWriteFactory_CreateTextLayout(factory, strW, 1, format, 1000.0f, 1000.0f, &layout); |
| ok(hr == S_OK, "Failed to create text layout, hr %x.\n", hr); |
| |
| for (i = 0; i < sizeof(overhangs_tests)/sizeof(overhangs_tests[0]); i++) { |
| const struct overhangs_test *test = &overhangs_tests[i]; |
| DWRITE_OVERHANG_METRICS overhang_metrics; |
| DWRITE_TEXT_RANGE range = { 0, 1 }; |
| DWRITE_TEXT_METRICS metrics; |
| struct test_inline_obj obj; |
| |
| test_inline_obj_init(&obj, &test->metrics, &test->overhang_metrics); |
| |
| hr = IDWriteTextLayout_SetLineSpacing(layout, DWRITE_LINE_SPACING_METHOD_UNIFORM, test->metrics.height * 2.0f, |
| test->uniform_baseline); |
| ok(hr == S_OK, "Failed to set line spacing, hr %#x.\n", hr); |
| |
| hr = IDWriteTextLayout_SetInlineObject(layout, NULL, range); |
| ok(hr == S_OK, "Failed to reset inline object, hr %#x.\n", hr); |
| |
| hr = IDWriteTextLayout_SetInlineObject(layout, &obj.IDWriteInlineObject_iface, range); |
| ok(hr == S_OK, "Failed to set inline object, hr %#x.\n", hr); |
| |
| hr = IDWriteTextLayout_GetMetrics(layout, &metrics); |
| ok(hr == S_OK, "Failed to get layout metrics, hr %#x.\n", hr); |
| |
| ok(metrics.width == test->metrics.width, "%u: unexpected formatted width.\n", i); |
| ok(metrics.height == test->metrics.height * 2.0f, "%u: unexpected formatted height.\n", i); |
| |
| hr = IDWriteTextLayout_SetMaxWidth(layout, metrics.width); |
| hr = IDWriteTextLayout_SetMaxHeight(layout, test->metrics.height); |
| |
| hr = IDWriteTextLayout_GetOverhangMetrics(layout, &overhang_metrics); |
| ok(hr == S_OK, "Failed to get overhang metrics, hr %#x.\n", hr); |
| |
| ok(!memcmp(&overhang_metrics, &test->expected, sizeof(overhang_metrics)), |
| "%u: unexpected overhang metrics (%f, %f, %f, %f).\n", i, overhang_metrics.left, overhang_metrics.top, |
| overhang_metrics.right, overhang_metrics.bottom); |
| } |
| |
| IDWriteTextLayout_Release(layout); |
| IDWriteTextFormat_Release(format); |
| IDWriteFactory_Release(factory); |
| } |
| |
| START_TEST(layout) |
| { |
| IDWriteFactory *factory; |
| |
| if (!(factory = create_factory())) { |
| win_skip("failed to create factory\n"); |
| return; |
| } |
| |
| init_call_sequences(sequences, NUM_CALL_SEQUENCES); |
| init_call_sequences(expected_seq, 1); |
| |
| test_CreateTextLayout(); |
| test_CreateGdiCompatibleTextLayout(); |
| test_CreateTextFormat(); |
| test_GetLocaleName(); |
| test_CreateEllipsisTrimmingSign(); |
| test_fontweight(); |
| test_SetInlineObject(); |
| test_Draw(); |
| test_typography(); |
| test_GetClusterMetrics(); |
| test_SetLocaleName(); |
| test_SetPairKerning(); |
| test_SetVerticalGlyphOrientation(); |
| test_fallback(); |
| test_DetermineMinWidth(); |
| test_SetFontSize(); |
| test_SetFontFamilyName(); |
| test_SetFontStyle(); |
| test_SetFontStretch(); |
| test_SetStrikethrough(); |
| test_GetMetrics(); |
| test_SetFlowDirection(); |
| test_SetDrawingEffect(); |
| test_GetLineMetrics(); |
| test_SetTextAlignment(); |
| test_SetParagraphAlignment(); |
| test_SetReadingDirection(); |
| test_pixelsnapping(); |
| test_SetWordWrapping(); |
| test_MapCharacters(); |
| test_FontFallbackBuilder(); |
| test_SetTypography(); |
| test_SetLastLineWrapping(); |
| test_SetOpticalAlignment(); |
| test_SetUnderline(); |
| test_InvalidateLayout(); |
| test_line_spacing(); |
| test_GetOverhangMetrics(); |
| |
| IDWriteFactory_Release(factory); |
| } |