dwrite/tests: Use ARRAY_SIZE() macro in tests.
[wine.git] / dlls / dwrite / tests / layout.c
blobc2dd273c6c11e4019c2f4b676d3e26595e0c5159
1 /*
2 * Text layout/format tests
4 * Copyright 2012, 2014-2017 Nikolay Sivov for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #define COBJMACROS
23 #include <assert.h>
24 #include <math.h>
25 #include <limits.h>
27 #include "windows.h"
28 #include "dwrite_3.h"
30 #include "wine/test.h"
32 #define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
34 static const WCHAR tahomaW[] = {'T','a','h','o','m','a',0};
35 static const WCHAR enusW[] = {'e','n','-','u','s',0};
37 struct testanalysissink
39 IDWriteTextAnalysisSink IDWriteTextAnalysisSink_iface;
40 DWRITE_SCRIPT_ANALYSIS sa; /* last analysis, with SetScriptAnalysis() */
43 static inline struct testanalysissink *impl_from_IDWriteTextAnalysisSink(IDWriteTextAnalysisSink *iface)
45 return CONTAINING_RECORD(iface, struct testanalysissink, IDWriteTextAnalysisSink_iface);
48 /* test IDWriteTextAnalysisSink */
49 static HRESULT WINAPI analysissink_QueryInterface(IDWriteTextAnalysisSink *iface, REFIID riid, void **obj)
51 if (IsEqualIID(riid, &IID_IDWriteTextAnalysisSink) || IsEqualIID(riid, &IID_IUnknown))
53 *obj = iface;
54 return S_OK;
57 *obj = NULL;
58 return E_NOINTERFACE;
61 static ULONG WINAPI analysissink_AddRef(IDWriteTextAnalysisSink *iface)
63 return 2;
66 static ULONG WINAPI analysissink_Release(IDWriteTextAnalysisSink *iface)
68 return 1;
71 static HRESULT WINAPI analysissink_SetScriptAnalysis(IDWriteTextAnalysisSink *iface,
72 UINT32 position, UINT32 length, DWRITE_SCRIPT_ANALYSIS const* sa)
74 struct testanalysissink *sink = impl_from_IDWriteTextAnalysisSink(iface);
75 sink->sa = *sa;
76 return S_OK;
79 static HRESULT WINAPI analysissink_SetLineBreakpoints(IDWriteTextAnalysisSink *iface,
80 UINT32 position, UINT32 length, DWRITE_LINE_BREAKPOINT const* breakpoints)
82 ok(0, "unexpected call\n");
83 return E_NOTIMPL;
86 static HRESULT WINAPI analysissink_SetBidiLevel(IDWriteTextAnalysisSink *iface,
87 UINT32 position, UINT32 length, UINT8 explicitLevel, UINT8 resolvedLevel)
89 ok(0, "unexpected\n");
90 return E_NOTIMPL;
93 static HRESULT WINAPI analysissink_SetNumberSubstitution(IDWriteTextAnalysisSink *iface,
94 UINT32 position, UINT32 length, IDWriteNumberSubstitution* substitution)
96 ok(0, "unexpected\n");
97 return E_NOTIMPL;
100 static IDWriteTextAnalysisSinkVtbl analysissinkvtbl = {
101 analysissink_QueryInterface,
102 analysissink_AddRef,
103 analysissink_Release,
104 analysissink_SetScriptAnalysis,
105 analysissink_SetLineBreakpoints,
106 analysissink_SetBidiLevel,
107 analysissink_SetNumberSubstitution
110 static struct testanalysissink analysissink = {
111 { &analysissinkvtbl },
112 { 0 }
115 /* test IDWriteTextAnalysisSource */
116 static HRESULT WINAPI analysissource_QueryInterface(IDWriteTextAnalysisSource *iface,
117 REFIID riid, void **obj)
119 if (IsEqualIID(riid, &IID_IDWriteTextAnalysisSource) || IsEqualIID(riid, &IID_IUnknown)) {
120 *obj = iface;
121 IDWriteTextAnalysisSource_AddRef(iface);
122 return S_OK;
124 return E_NOINTERFACE;
127 static ULONG WINAPI analysissource_AddRef(IDWriteTextAnalysisSource *iface)
129 return 2;
132 static ULONG WINAPI analysissource_Release(IDWriteTextAnalysisSource *iface)
134 return 1;
137 static const WCHAR *g_source;
139 static HRESULT WINAPI analysissource_GetTextAtPosition(IDWriteTextAnalysisSource *iface,
140 UINT32 position, WCHAR const** text, UINT32* text_len)
142 if (position >= lstrlenW(g_source))
144 *text = NULL;
145 *text_len = 0;
147 else
149 *text = &g_source[position];
150 *text_len = lstrlenW(g_source) - position;
153 return S_OK;
156 static HRESULT WINAPI analysissource_GetTextBeforePosition(IDWriteTextAnalysisSource *iface,
157 UINT32 position, WCHAR const** text, UINT32* text_len)
159 ok(0, "unexpected\n");
160 return E_NOTIMPL;
163 static DWRITE_READING_DIRECTION WINAPI analysissource_GetParagraphReadingDirection(
164 IDWriteTextAnalysisSource *iface)
166 ok(0, "unexpected\n");
167 return DWRITE_READING_DIRECTION_RIGHT_TO_LEFT;
170 static HRESULT WINAPI analysissource_GetLocaleName(IDWriteTextAnalysisSource *iface,
171 UINT32 position, UINT32* text_len, WCHAR const** locale)
173 *locale = NULL;
174 *text_len = 0;
175 return S_OK;
178 static HRESULT WINAPI analysissource_GetNumberSubstitution(IDWriteTextAnalysisSource *iface,
179 UINT32 position, UINT32* text_len, IDWriteNumberSubstitution **substitution)
181 ok(0, "unexpected\n");
182 return E_NOTIMPL;
185 static IDWriteTextAnalysisSourceVtbl analysissourcevtbl = {
186 analysissource_QueryInterface,
187 analysissource_AddRef,
188 analysissource_Release,
189 analysissource_GetTextAtPosition,
190 analysissource_GetTextBeforePosition,
191 analysissource_GetParagraphReadingDirection,
192 analysissource_GetLocaleName,
193 analysissource_GetNumberSubstitution
196 static IDWriteTextAnalysisSource analysissource = { &analysissourcevtbl };
198 static IDWriteFactory *create_factory(void)
200 IDWriteFactory *factory;
201 HRESULT hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_ISOLATED, &IID_IDWriteFactory, (IUnknown**)&factory);
202 ok(hr == S_OK, "got 0x%08x\n", hr);
203 return factory;
206 /* obvious limitation is that only last script data is returned, so this
207 helper is suitable for single script strings only */
208 static void get_script_analysis(const WCHAR *str, UINT32 len, DWRITE_SCRIPT_ANALYSIS *sa)
210 IDWriteTextAnalyzer *analyzer;
211 IDWriteFactory *factory;
212 HRESULT hr;
214 g_source = str;
216 factory = create_factory();
217 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
218 ok(hr == S_OK, "got 0x%08x\n", hr);
220 hr = IDWriteTextAnalyzer_AnalyzeScript(analyzer, &analysissource, 0, len, &analysissink.IDWriteTextAnalysisSink_iface);
221 ok(hr == S_OK, "got 0x%08x\n", hr);
223 *sa = analysissink.sa;
224 IDWriteFactory_Release(factory);
227 static IDWriteFontFace *get_fontface_from_format(IDWriteTextFormat *format)
229 IDWriteFontCollection *collection;
230 IDWriteFontFamily *family;
231 IDWriteFontFace *fontface;
232 IDWriteFont *font;
233 WCHAR nameW[255];
234 UINT32 index;
235 BOOL exists;
236 HRESULT hr;
238 hr = IDWriteTextFormat_GetFontCollection(format, &collection);
239 ok(hr == S_OK, "got 0x%08x\n", hr);
241 hr = IDWriteTextFormat_GetFontFamilyName(format, nameW, ARRAY_SIZE(nameW));
242 ok(hr == S_OK, "got 0x%08x\n", hr);
244 hr = IDWriteFontCollection_FindFamilyName(collection, nameW, &index, &exists);
245 ok(hr == S_OK, "got 0x%08x\n", hr);
247 hr = IDWriteFontCollection_GetFontFamily(collection, index, &family);
248 ok(hr == S_OK, "got 0x%08x\n", hr);
249 IDWriteFontCollection_Release(collection);
251 hr = IDWriteFontFamily_GetFirstMatchingFont(family,
252 IDWriteTextFormat_GetFontWeight(format),
253 IDWriteTextFormat_GetFontStretch(format),
254 IDWriteTextFormat_GetFontStyle(format),
255 &font);
256 ok(hr == S_OK, "got 0x%08x\n", hr);
258 hr = IDWriteFont_CreateFontFace(font, &fontface);
259 ok(hr == S_OK, "got 0x%08x\n", hr);
261 IDWriteFont_Release(font);
262 IDWriteFontFamily_Release(family);
264 return fontface;
267 #define EXPECT_REF(obj,ref) _expect_ref((IUnknown*)obj, ref, __LINE__)
268 static void _expect_ref(IUnknown* obj, ULONG ref, int line)
270 ULONG rc;
271 IUnknown_AddRef(obj);
272 rc = IUnknown_Release(obj);
273 ok_(__FILE__,line)(rc == ref, "expected refcount %d, got %d\n", ref, rc);
276 enum drawcall_modifiers_kind {
277 DRAW_EFFECT = 0x1000
280 enum drawcall_kind {
281 DRAW_GLYPHRUN = 0,
282 DRAW_UNDERLINE = 1,
283 DRAW_STRIKETHROUGH = 2,
284 DRAW_INLINE = 3,
285 DRAW_LAST_KIND = 4,
286 DRAW_TOTAL_KINDS = 5,
287 DRAW_KINDS_MASK = 0xff
290 static const char *get_draw_kind_name(unsigned short kind)
292 static const char *kind_names[] = {
293 "GLYPH_RUN",
294 "UNDERLINE",
295 "STRIKETHROUGH",
296 "INLINE",
297 "END_OF_SEQ",
299 "GLYPH_RUN|EFFECT",
300 "UNDERLINE|EFFECT",
301 "STRIKETHROUGH|EFFECT",
302 "INLINE|EFFECT",
303 "END_OF_SEQ"
305 if ((kind & DRAW_KINDS_MASK) > DRAW_LAST_KIND)
306 return "unknown";
307 return (kind & DRAW_EFFECT) ? kind_names[(kind & DRAW_KINDS_MASK) + DRAW_TOTAL_KINDS] :
308 kind_names[kind];
311 struct drawcall_entry {
312 enum drawcall_kind kind;
313 WCHAR string[10]; /* only meaningful for DrawGlyphRun() */
314 WCHAR locale[LOCALE_NAME_MAX_LENGTH];
315 UINT32 glyphcount; /* only meaningful for DrawGlyphRun() */
318 struct drawcall_sequence
320 int count;
321 int size;
322 struct drawcall_entry *sequence;
325 struct drawtestcontext {
326 unsigned short kind;
327 BOOL todo;
328 int *failcount;
329 const char *file;
330 int line;
333 #define NUM_CALL_SEQUENCES 1
334 #define RENDERER_ID 0
335 static struct drawcall_sequence *sequences[NUM_CALL_SEQUENCES];
336 static struct drawcall_sequence *expected_seq[1];
338 static void add_call(struct drawcall_sequence **seq, int sequence_index, const struct drawcall_entry *call)
340 struct drawcall_sequence *call_seq = seq[sequence_index];
342 if (!call_seq->sequence) {
343 call_seq->size = 10;
344 call_seq->sequence = HeapAlloc(GetProcessHeap(), 0, call_seq->size * sizeof (struct drawcall_entry));
347 if (call_seq->count == call_seq->size) {
348 call_seq->size *= 2;
349 call_seq->sequence = HeapReAlloc(GetProcessHeap(), 0,
350 call_seq->sequence,
351 call_seq->size * sizeof (struct drawcall_entry));
354 assert(call_seq->sequence);
355 call_seq->sequence[call_seq->count++] = *call;
358 static inline void flush_sequence(struct drawcall_sequence **seg, int sequence_index)
360 struct drawcall_sequence *call_seq = seg[sequence_index];
362 HeapFree(GetProcessHeap(), 0, call_seq->sequence);
363 call_seq->sequence = NULL;
364 call_seq->count = call_seq->size = 0;
367 static void init_call_sequences(struct drawcall_sequence **seq, int n)
369 int i;
371 for (i = 0; i < n; i++)
372 seq[i] = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct drawcall_sequence));
375 static void ok_sequence_(struct drawcall_sequence **seq, int sequence_index,
376 const struct drawcall_entry *expected, const char *context, BOOL todo,
377 const char *file, int line)
379 static const struct drawcall_entry end_of_sequence = { DRAW_LAST_KIND };
380 struct drawcall_sequence *call_seq = seq[sequence_index];
381 const struct drawcall_entry *actual, *sequence;
382 int failcount = 0;
384 add_call(seq, sequence_index, &end_of_sequence);
386 sequence = call_seq->sequence;
387 actual = sequence;
389 while (expected->kind != DRAW_LAST_KIND && actual->kind != DRAW_LAST_KIND) {
390 if (expected->kind != actual->kind) {
391 if (todo) {
392 failcount++;
393 todo_wine
394 ok_(file, line) (0, "%s: call %s was expected, but got call %s instead\n",
395 context, get_draw_kind_name(expected->kind), get_draw_kind_name(actual->kind));
397 flush_sequence(seq, sequence_index);
398 return;
400 else
401 ok_(file, line) (0, "%s: call %s was expected, but got call %s instead\n",
402 context, get_draw_kind_name(expected->kind), get_draw_kind_name(actual->kind));
404 else if ((expected->kind & DRAW_KINDS_MASK) == DRAW_GLYPHRUN) {
405 int cmp = lstrcmpW(expected->string, actual->string);
406 if (cmp != 0 && todo) {
407 failcount++;
408 todo_wine
409 ok_(file, line) (0, "%s: glyphrun string %s was expected, but got %s instead\n",
410 context, wine_dbgstr_w(expected->string), wine_dbgstr_w(actual->string));
412 else
413 ok_(file, line) (cmp == 0, "%s: glyphrun string %s was expected, but got %s instead\n",
414 context, wine_dbgstr_w(expected->string), wine_dbgstr_w(actual->string));
416 cmp = lstrcmpW(expected->locale, actual->locale);
417 if (cmp != 0 && todo) {
418 failcount++;
419 todo_wine
420 ok_(file, line) (0, "%s: glyph run locale %s was expected, but got %s instead\n",
421 context, wine_dbgstr_w(expected->locale), wine_dbgstr_w(actual->locale));
423 else
424 ok_(file, line) (cmp == 0, "%s: glyph run locale %s was expected, but got %s instead\n",
425 context, wine_dbgstr_w(expected->locale), wine_dbgstr_w(actual->locale));
427 if (expected->glyphcount != actual->glyphcount && todo) {
428 failcount++;
429 todo_wine
430 ok_(file, line) (0, "%s: wrong glyph count, %u was expected, but got %u instead\n",
431 context, expected->glyphcount, actual->glyphcount);
433 else
434 ok_(file, line) (expected->glyphcount == actual->glyphcount,
435 "%s: wrong glyph count, %u was expected, but got %u instead\n",
436 context, expected->glyphcount, actual->glyphcount);
438 else if ((expected->kind & DRAW_KINDS_MASK) == DRAW_UNDERLINE) {
439 int cmp = lstrcmpW(expected->locale, actual->locale);
440 if (cmp != 0 && todo) {
441 failcount++;
442 todo_wine
443 ok_(file, line) (0, "%s: underline locale %s was expected, but got %s instead\n",
444 context, wine_dbgstr_w(expected->locale), wine_dbgstr_w(actual->locale));
446 else
447 ok_(file, line) (cmp == 0, "%s: underline locale %s was expected, but got %s instead\n",
448 context, wine_dbgstr_w(expected->locale), wine_dbgstr_w(actual->locale));
450 expected++;
451 actual++;
454 if (todo) {
455 todo_wine {
456 if (expected->kind != DRAW_LAST_KIND || actual->kind != DRAW_LAST_KIND) {
457 failcount++;
458 ok_(file, line) (0, "%s: the call sequence is not complete: expected %s - actual %s\n",
459 context, get_draw_kind_name(expected->kind), get_draw_kind_name(actual->kind));
463 else if (expected->kind != DRAW_LAST_KIND || actual->kind != DRAW_LAST_KIND)
464 ok_(file, line) (0, "%s: the call sequence is not complete: expected %s - actual %s\n",
465 context, get_draw_kind_name(expected->kind), get_draw_kind_name(actual->kind));
467 if (todo && !failcount) /* succeeded yet marked todo */
468 todo_wine
469 ok_(file, line)(1, "%s: marked \"todo_wine\" but succeeds\n", context);
471 flush_sequence(seq, sequence_index);
474 #define ok_sequence(seq, index, exp, contx, todo) \
475 ok_sequence_(seq, index, (exp), (contx), (todo), __FILE__, __LINE__)
477 static HRESULT WINAPI testrenderer_QI(IDWriteTextRenderer *iface, REFIID riid, void **obj)
479 if (IsEqualIID(riid, &IID_IDWriteTextRenderer) ||
480 IsEqualIID(riid, &IID_IDWritePixelSnapping) ||
481 IsEqualIID(riid, &IID_IUnknown)
483 *obj = iface;
484 return S_OK;
487 *obj = NULL;
489 /* IDWriteTextRenderer1 overrides drawing calls, ignore for now */
490 if (IsEqualIID(riid, &IID_IDWriteTextRenderer1))
491 return E_NOINTERFACE;
493 ok(0, "unexpected QI %s\n", wine_dbgstr_guid(riid));
494 return E_NOINTERFACE;
497 static ULONG WINAPI testrenderer_AddRef(IDWriteTextRenderer *iface)
499 return 2;
502 static ULONG WINAPI testrenderer_Release(IDWriteTextRenderer *iface)
504 return 1;
507 struct renderer_context {
508 BOOL gdicompat;
509 BOOL use_gdi_natural;
510 BOOL snapping_disabled;
511 DWRITE_MATRIX m;
512 FLOAT ppdip;
513 FLOAT originX;
514 FLOAT originY;
515 IDWriteTextFormat *format;
516 const WCHAR *familyW;
519 static HRESULT WINAPI testrenderer_IsPixelSnappingDisabled(IDWriteTextRenderer *iface,
520 void *context, BOOL *disabled)
522 struct renderer_context *ctxt = (struct renderer_context*)context;
523 if (ctxt)
524 *disabled = ctxt->snapping_disabled;
525 else
526 *disabled = TRUE;
527 return S_OK;
530 static HRESULT WINAPI testrenderer_GetCurrentTransform(IDWriteTextRenderer *iface,
531 void *context, DWRITE_MATRIX *m)
533 struct renderer_context *ctxt = (struct renderer_context*)context;
534 ok(!ctxt->snapping_disabled, "expected enabled snapping\n");
535 *m = ctxt->m;
536 return S_OK;
539 static HRESULT WINAPI testrenderer_GetPixelsPerDip(IDWriteTextRenderer *iface,
540 void *context, FLOAT *pixels_per_dip)
542 struct renderer_context *ctxt = (struct renderer_context*)context;
543 *pixels_per_dip = ctxt->ppdip;
544 return S_OK;
547 #define TEST_MEASURING_MODE(ctxt, mode) test_measuring_mode(ctxt, mode, __LINE__)
548 static void test_measuring_mode(const struct renderer_context *ctxt, DWRITE_MEASURING_MODE mode, int line)
550 if (ctxt->gdicompat) {
551 if (ctxt->use_gdi_natural)
552 ok_(__FILE__, line)(mode == DWRITE_MEASURING_MODE_GDI_NATURAL, "got %d\n", mode);
553 else
554 ok_(__FILE__, line)(mode == DWRITE_MEASURING_MODE_GDI_CLASSIC, "got %d\n", mode);
556 else
557 ok_(__FILE__, line)(mode == DWRITE_MEASURING_MODE_NATURAL, "got %d\n", mode);
560 static HRESULT WINAPI testrenderer_DrawGlyphRun(IDWriteTextRenderer *iface,
561 void *context,
562 FLOAT baselineOriginX,
563 FLOAT baselineOriginY,
564 DWRITE_MEASURING_MODE mode,
565 DWRITE_GLYPH_RUN const *run,
566 DWRITE_GLYPH_RUN_DESCRIPTION const *descr,
567 IUnknown *effect)
569 struct renderer_context *ctxt = (struct renderer_context*)context;
570 struct drawcall_entry entry;
571 DWRITE_SCRIPT_ANALYSIS sa;
573 if (ctxt) {
574 TEST_MEASURING_MODE(ctxt, mode);
575 ctxt->originX = baselineOriginX;
576 ctxt->originY = baselineOriginY;
579 ok(descr->stringLength < ARRAY_SIZE(entry.string), "string is too long\n");
580 if (descr->stringLength && descr->stringLength < ARRAY_SIZE(entry.string)) {
581 memcpy(entry.string, descr->string, descr->stringLength*sizeof(WCHAR));
582 entry.string[descr->stringLength] = 0;
584 else
585 entry.string[0] = 0;
587 /* see what's reported for control codes runs */
588 get_script_analysis(descr->string, descr->stringLength, &sa);
589 if (sa.shapes == DWRITE_SCRIPT_SHAPES_NO_VISUAL) {
590 UINT32 i;
592 /* glyphs are not reported at all for control code runs */
593 ok(run->glyphCount == 0, "got %u\n", run->glyphCount);
594 ok(run->glyphAdvances != NULL, "advances array %p\n", run->glyphAdvances);
595 ok(run->glyphOffsets != NULL, "offsets array %p\n", run->glyphOffsets);
596 ok(run->fontFace != NULL, "got %p\n", run->fontFace);
597 /* text positions are still valid */
598 ok(descr->string != NULL, "got string %p\n", descr->string);
599 ok(descr->stringLength > 0, "got string length %u\n", descr->stringLength);
600 ok(descr->clusterMap != NULL, "clustermap %p\n", descr->clusterMap);
601 for (i = 0; i < descr->stringLength; i++)
602 ok(descr->clusterMap[i] == i, "got %u\n", descr->clusterMap[i]);
605 entry.kind = DRAW_GLYPHRUN;
606 if (effect)
607 entry.kind |= DRAW_EFFECT;
608 ok(lstrlenW(descr->localeName) < LOCALE_NAME_MAX_LENGTH, "unexpectedly long locale name\n");
609 lstrcpyW(entry.locale, descr->localeName);
610 entry.glyphcount = run->glyphCount;
611 add_call(sequences, RENDERER_ID, &entry);
612 return S_OK;
615 static HRESULT WINAPI testrenderer_DrawUnderline(IDWriteTextRenderer *iface,
616 void *context,
617 FLOAT baselineOriginX,
618 FLOAT baselineOriginY,
619 DWRITE_UNDERLINE const* underline,
620 IUnknown *effect)
622 struct renderer_context *ctxt = (struct renderer_context*)context;
623 struct drawcall_entry entry = { 0 };
625 if (ctxt)
626 TEST_MEASURING_MODE(ctxt, underline->measuringMode);
628 ok(underline->runHeight > 0.0f, "Expected non-zero run height\n");
629 if (ctxt && ctxt->format) {
630 DWRITE_FONT_METRICS metrics;
631 IDWriteFontFace *fontface;
632 FLOAT emsize;
634 fontface = get_fontface_from_format(ctxt->format);
635 emsize = IDWriteTextFormat_GetFontSize(ctxt->format);
636 IDWriteFontFace_GetMetrics(fontface, &metrics);
638 ok(emsize == metrics.designUnitsPerEm, "Unexpected font size %f\n", emsize);
639 /* Expected height is in design units, allow some absolute difference from it. Seems to only happen on Vista */
640 ok(fabs(metrics.capHeight - underline->runHeight) < 2.0f, "Expected runHeight %u, got %f, family %s\n",
641 metrics.capHeight, underline->runHeight, wine_dbgstr_w(ctxt->familyW));
643 IDWriteFontFace_Release(fontface);
646 entry.kind = DRAW_UNDERLINE;
647 if (effect)
648 entry.kind |= DRAW_EFFECT;
649 lstrcpyW(entry.locale, underline->localeName);
650 add_call(sequences, RENDERER_ID, &entry);
651 return S_OK;
654 static HRESULT WINAPI testrenderer_DrawStrikethrough(IDWriteTextRenderer *iface,
655 void *context,
656 FLOAT baselineOriginX,
657 FLOAT baselineOriginY,
658 DWRITE_STRIKETHROUGH const* strikethrough,
659 IUnknown *effect)
661 struct renderer_context *ctxt = (struct renderer_context*)context;
662 struct drawcall_entry entry = { 0 };
664 if (ctxt)
665 TEST_MEASURING_MODE(ctxt, strikethrough->measuringMode);
667 entry.kind = DRAW_STRIKETHROUGH;
668 if (effect)
669 entry.kind |= DRAW_EFFECT;
670 add_call(sequences, RENDERER_ID, &entry);
671 return S_OK;
674 static HRESULT WINAPI testrenderer_DrawInlineObject(IDWriteTextRenderer *iface,
675 void *context,
676 FLOAT originX,
677 FLOAT originY,
678 IDWriteInlineObject *object,
679 BOOL is_sideways,
680 BOOL is_rtl,
681 IUnknown *effect)
683 struct drawcall_entry entry = { 0 };
684 entry.kind = DRAW_INLINE;
685 if (effect)
686 entry.kind |= DRAW_EFFECT;
687 add_call(sequences, RENDERER_ID, &entry);
688 return S_OK;
691 static const IDWriteTextRendererVtbl testrenderervtbl = {
692 testrenderer_QI,
693 testrenderer_AddRef,
694 testrenderer_Release,
695 testrenderer_IsPixelSnappingDisabled,
696 testrenderer_GetCurrentTransform,
697 testrenderer_GetPixelsPerDip,
698 testrenderer_DrawGlyphRun,
699 testrenderer_DrawUnderline,
700 testrenderer_DrawStrikethrough,
701 testrenderer_DrawInlineObject
704 static IDWriteTextRenderer testrenderer = { &testrenderervtbl };
706 /* test IDWriteInlineObject */
707 static HRESULT WINAPI testinlineobj_QI(IDWriteInlineObject *iface, REFIID riid, void **obj)
709 if (IsEqualIID(riid, &IID_IDWriteInlineObject) || IsEqualIID(riid, &IID_IUnknown)) {
710 *obj = iface;
711 IDWriteInlineObject_AddRef(iface);
712 return S_OK;
715 *obj = NULL;
716 return E_NOINTERFACE;
719 static ULONG WINAPI testinlineobj_AddRef(IDWriteInlineObject *iface)
721 return 2;
724 static ULONG WINAPI testinlineobj_Release(IDWriteInlineObject *iface)
726 return 1;
729 static HRESULT WINAPI testinlineobj_Draw(IDWriteInlineObject *iface,
730 void* client_drawingontext, IDWriteTextRenderer* renderer,
731 FLOAT originX, FLOAT originY, BOOL is_sideways, BOOL is_rtl, IUnknown *drawing_effect)
733 ok(0, "unexpected call\n");
734 return E_NOTIMPL;
737 static HRESULT WINAPI testinlineobj_GetMetrics(IDWriteInlineObject *iface, DWRITE_INLINE_OBJECT_METRICS *metrics)
739 metrics->width = 123.0;
740 return 0x8faecafe;
743 static HRESULT WINAPI testinlineobj_GetOverhangMetrics(IDWriteInlineObject *iface, DWRITE_OVERHANG_METRICS *overhangs)
745 ok(0, "unexpected call\n");
746 return E_NOTIMPL;
749 static HRESULT WINAPI testinlineobj_GetBreakConditions(IDWriteInlineObject *iface, DWRITE_BREAK_CONDITION *before,
750 DWRITE_BREAK_CONDITION *after)
752 *before = *after = DWRITE_BREAK_CONDITION_MUST_BREAK;
753 return 0x8feacafe;
756 static HRESULT WINAPI testinlineobj2_GetBreakConditions(IDWriteInlineObject *iface, DWRITE_BREAK_CONDITION *before,
757 DWRITE_BREAK_CONDITION *after)
759 *before = *after = DWRITE_BREAK_CONDITION_MAY_NOT_BREAK;
760 return S_OK;
763 static IDWriteInlineObjectVtbl testinlineobjvtbl = {
764 testinlineobj_QI,
765 testinlineobj_AddRef,
766 testinlineobj_Release,
767 testinlineobj_Draw,
768 testinlineobj_GetMetrics,
769 testinlineobj_GetOverhangMetrics,
770 testinlineobj_GetBreakConditions
773 static IDWriteInlineObjectVtbl testinlineobjvtbl2 = {
774 testinlineobj_QI,
775 testinlineobj_AddRef,
776 testinlineobj_Release,
777 testinlineobj_Draw,
778 testinlineobj_GetMetrics,
779 testinlineobj_GetOverhangMetrics,
780 testinlineobj2_GetBreakConditions
783 static IDWriteInlineObject testinlineobj = { &testinlineobjvtbl };
784 static IDWriteInlineObject testinlineobj2 = { &testinlineobjvtbl };
785 static IDWriteInlineObject testinlineobj3 = { &testinlineobjvtbl2 };
787 struct test_inline_obj
789 IDWriteInlineObject IDWriteInlineObject_iface;
790 DWRITE_INLINE_OBJECT_METRICS metrics;
791 DWRITE_OVERHANG_METRICS overhangs;
794 static inline struct test_inline_obj *impl_from_IDWriteInlineObject(IDWriteInlineObject *iface)
796 return CONTAINING_RECORD(iface, struct test_inline_obj, IDWriteInlineObject_iface);
799 static HRESULT WINAPI testinlineobj3_GetMetrics(IDWriteInlineObject *iface, DWRITE_INLINE_OBJECT_METRICS *metrics)
801 struct test_inline_obj *obj = impl_from_IDWriteInlineObject(iface);
802 *metrics = obj->metrics;
803 return S_OK;
806 static HRESULT WINAPI testinlineobj3_GetOverhangMetrics(IDWriteInlineObject *iface, DWRITE_OVERHANG_METRICS *overhangs)
808 struct test_inline_obj *obj = impl_from_IDWriteInlineObject(iface);
809 *overhangs = obj->overhangs;
810 /* Return value is ignored. */
811 return E_NOTIMPL;
814 static const IDWriteInlineObjectVtbl testinlineobjvtbl3 = {
815 testinlineobj_QI,
816 testinlineobj_AddRef,
817 testinlineobj_Release,
818 testinlineobj_Draw,
819 testinlineobj3_GetMetrics,
820 testinlineobj3_GetOverhangMetrics,
821 testinlineobj_GetBreakConditions,
824 static void test_inline_obj_init(struct test_inline_obj *obj, const DWRITE_INLINE_OBJECT_METRICS *metrics,
825 const DWRITE_OVERHANG_METRICS *overhangs)
827 obj->IDWriteInlineObject_iface.lpVtbl = &testinlineobjvtbl3;
828 obj->metrics = *metrics;
829 obj->overhangs = *overhangs;
832 struct test_effect
834 IUnknown IUnknown_iface;
835 LONG ref;
838 static inline struct test_effect *test_effect_from_IUnknown(IUnknown *iface)
840 return CONTAINING_RECORD(iface, struct test_effect, IUnknown_iface);
843 static HRESULT WINAPI testeffect_QI(IUnknown *iface, REFIID riid, void **obj)
845 if (IsEqualIID(riid, &IID_IUnknown)) {
846 *obj = iface;
847 IUnknown_AddRef(iface);
848 return S_OK;
851 ok(0, "Unexpected riid %s.\n", wine_dbgstr_guid(riid));
852 *obj = NULL;
853 return E_NOINTERFACE;
856 static ULONG WINAPI testeffect_AddRef(IUnknown *iface)
858 struct test_effect *effect = test_effect_from_IUnknown(iface);
859 return InterlockedIncrement(&effect->ref);
862 static ULONG WINAPI testeffect_Release(IUnknown *iface)
864 struct test_effect *effect = test_effect_from_IUnknown(iface);
865 LONG ref = InterlockedDecrement(&effect->ref);
867 if (!ref)
868 HeapFree(GetProcessHeap(), 0, effect);
870 return ref;
873 static const IUnknownVtbl testeffectvtbl = {
874 testeffect_QI,
875 testeffect_AddRef,
876 testeffect_Release
879 static IUnknown *create_test_effect(void)
881 struct test_effect *effect;
883 effect = HeapAlloc(GetProcessHeap(), 0, sizeof(*effect));
884 effect->IUnknown_iface.lpVtbl = &testeffectvtbl;
885 effect->ref = 1;
887 return &effect->IUnknown_iface;
890 static void test_CreateTextLayout(void)
892 static const WCHAR strW[] = {'s','t','r','i','n','g',0};
893 IDWriteTextLayout2 *layout2;
894 IDWriteTextLayout *layout;
895 IDWriteTextFormat *format;
896 IDWriteFactory *factory;
897 HRESULT hr;
899 factory = create_factory();
901 layout = (void*)0xdeadbeef;
902 hr = IDWriteFactory_CreateTextLayout(factory, NULL, 0, NULL, 0.0, 0.0, &layout);
903 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
904 ok(layout == NULL, "got %p\n", layout);
906 layout = (void*)0xdeadbeef;
907 hr = IDWriteFactory_CreateTextLayout(factory, strW, 6, NULL, 0.0, 0.0, &layout);
908 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
909 ok(layout == NULL, "got %p\n", layout);
911 layout = (void*)0xdeadbeef;
912 hr = IDWriteFactory_CreateTextLayout(factory, strW, 6, NULL, 1.0, 0.0, &layout);
913 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
914 ok(layout == NULL, "got %p\n", layout);
916 layout = (void*)0xdeadbeef;
917 hr = IDWriteFactory_CreateTextLayout(factory, strW, 6, NULL, 0.0, 1.0, &layout);
918 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
919 ok(layout == NULL, "got %p\n", layout);
921 layout = (void*)0xdeadbeef;
922 hr = IDWriteFactory_CreateTextLayout(factory, strW, 6, NULL, 1000.0, 1000.0, &layout);
923 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
924 ok(layout == NULL, "got %p\n", layout);
926 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
927 DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format);
928 ok(hr == S_OK, "got 0x%08x\n", hr);
930 layout = (void*)0xdeadbeef;
931 hr = IDWriteFactory_CreateTextLayout(factory, NULL, 0, format, 100.0f, 100.0f, &layout);
932 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
933 ok(layout == NULL, "got %p\n", layout);
935 hr = IDWriteFactory_CreateTextLayout(factory, strW, 0, format, 0.0f, 0.0f, &layout);
936 ok(hr == S_OK, "got 0x%08x\n", hr);
937 IDWriteTextLayout_Release(layout);
939 EXPECT_REF(format, 1);
940 hr = IDWriteFactory_CreateTextLayout(factory, strW, 6, format, 1000.0, 1000.0, &layout);
941 ok(hr == S_OK, "got 0x%08x\n", hr);
942 EXPECT_REF(format, 1);
944 hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextLayout2, (void**)&layout2);
945 if (hr == S_OK) {
946 IDWriteTextLayout1 *layout1;
947 IDWriteTextFormat1 *format1;
948 IDWriteTextFormat *format;
950 hr = IDWriteTextLayout2_QueryInterface(layout2, &IID_IDWriteTextLayout1, (void**)&layout1);
951 ok(hr == S_OK, "got 0x%08x\n", hr);
952 IDWriteTextLayout1_Release(layout1);
954 EXPECT_REF(layout2, 2);
955 hr = IDWriteTextLayout2_QueryInterface(layout2, &IID_IDWriteTextFormat1, (void**)&format1);
956 ok(hr == S_OK, "got 0x%08x\n", hr);
957 EXPECT_REF(layout2, 3);
959 hr = IDWriteTextLayout2_QueryInterface(layout2, &IID_IDWriteTextFormat, (void**)&format);
960 ok(hr == S_OK, "got 0x%08x\n", hr);
961 ok(format == (IDWriteTextFormat*)format1, "got %p, %p\n", format, format1);
962 ok(format != (IDWriteTextFormat*)layout2, "got %p, %p\n", format, layout2);
963 EXPECT_REF(layout2, 4);
965 hr = IDWriteTextFormat_QueryInterface(format, &IID_IDWriteTextLayout1, (void**)&layout1);
966 ok(hr == S_OK, "got 0x%08x\n", hr);
967 IDWriteTextLayout1_Release(layout1);
969 IDWriteTextFormat1_Release(format1);
970 IDWriteTextFormat_Release(format);
972 hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextFormat1, (void**)&format1);
973 ok(hr == S_OK, "got 0x%08x\n", hr);
974 EXPECT_REF(layout2, 3);
976 hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextFormat, (void**)&format);
977 ok(hr == S_OK, "got 0x%08x\n", hr);
978 ok(format == (IDWriteTextFormat*)format1, "got %p, %p\n", format, format1);
979 EXPECT_REF(layout2, 4);
981 IDWriteTextFormat1_Release(format1);
982 IDWriteTextFormat_Release(format);
983 IDWriteTextLayout2_Release(layout2);
985 else
986 win_skip("IDWriteTextLayout2 is not supported.\n");
988 IDWriteTextLayout_Release(layout);
989 IDWriteTextFormat_Release(format);
990 IDWriteFactory_Release(factory);
993 static DWRITE_MATRIX layoutcreate_transforms[] = {
994 { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 },
995 { 1.0, 0.0, 0.0, 1.0, 0.3, 0.2 },
996 { 1.0, 0.0, 0.0, 1.0,-0.3,-0.2 },
998 { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 },
999 { 1.0, 0.0, 0.0, 0.0, 0.0, 0.0 },
1000 { 1.0, 2.0, 0.5, 1.0, 0.0, 0.0 },
1003 static void test_CreateGdiCompatibleTextLayout(void)
1005 static const WCHAR strW[] = {'s','t','r','i','n','g',0};
1006 IDWriteTextLayout *layout;
1007 IDWriteTextFormat *format;
1008 IDWriteFactory *factory;
1009 FLOAT dimension;
1010 HRESULT hr;
1011 int i;
1013 factory = create_factory();
1015 layout = (void*)0xdeadbeef;
1016 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, NULL, 0, NULL, 0.0, 0.0, 0.0, NULL, FALSE, &layout);
1017 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1018 ok(layout == NULL, "got %p\n", layout);
1020 layout = (void*)0xdeadbeef;
1021 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, strW, 6, NULL, 0.0, 0.0, 0.0, NULL, FALSE, &layout);
1022 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1023 ok(layout == NULL, "got %p\n", layout);
1025 layout = (void*)0xdeadbeef;
1026 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, strW, 6, NULL, 1.0, 0.0, 0.0, NULL, FALSE, &layout);
1027 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1028 ok(layout == NULL, "got %p\n", layout);
1030 layout = (void*)0xdeadbeef;
1031 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, strW, 6, NULL, 1.0, 0.0, 1.0, NULL, FALSE, &layout);
1032 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1033 ok(layout == NULL, "got %p\n", layout);
1035 layout = (void*)0xdeadbeef;
1036 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, strW, 6, NULL, 1000.0, 1000.0, 1.0, NULL, FALSE, &layout);
1037 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1038 ok(layout == NULL, "got %p\n", layout);
1040 /* create with text format */
1041 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
1042 DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format);
1043 ok(hr == S_OK, "got 0x%08x\n", hr);
1044 EXPECT_REF(format, 1);
1046 layout = (void*)0xdeadbeef;
1047 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, NULL, 0, format, 100.0f, 100.0f, 1.0f, NULL, FALSE, &layout);
1048 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1049 ok(layout == NULL, "got %p\n", layout);
1051 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, strW, 6, format, 100.0, 100.0, 1.0, NULL, FALSE, &layout);
1052 ok(hr == S_OK, "got 0x%08x\n", hr);
1053 EXPECT_REF(format, 1);
1054 EXPECT_REF(layout, 1);
1056 IDWriteTextLayout_AddRef(layout);
1057 EXPECT_REF(format, 1);
1058 EXPECT_REF(layout, 2);
1059 IDWriteTextLayout_Release(layout);
1060 IDWriteTextLayout_Release(layout);
1062 /* zero length string is okay */
1063 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, strW, 0, format, 100.0, 100.0, 1.0, NULL, FALSE, &layout);
1064 ok(hr == S_OK, "got 0x%08x\n", hr);
1066 dimension = IDWriteTextLayout_GetMaxWidth(layout);
1067 ok(dimension == 100.0, "got %f\n", dimension);
1069 dimension = IDWriteTextLayout_GetMaxHeight(layout);
1070 ok(dimension == 100.0, "got %f\n", dimension);
1072 IDWriteTextLayout_Release(layout);
1074 /* negative, zero ppdip */
1075 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, strW, 1, format, 100.0, 100.0, -1.0, NULL, FALSE, &layout);
1076 ok(hr == S_OK, "got 0x%08x\n", hr);
1077 IDWriteTextLayout_Release(layout);
1079 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, strW, 1, format, 100.0, 100.0, 0.0, NULL, FALSE, &layout);
1080 ok(hr == S_OK, "got 0x%08x\n", hr);
1081 IDWriteTextLayout_Release(layout);
1083 /* transforms */
1084 for (i = 0; i < ARRAY_SIZE(layoutcreate_transforms); i++) {
1085 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, strW, 1, format, 100.0, 100.0, 1.0,
1086 &layoutcreate_transforms[i], FALSE, &layout);
1087 ok(hr == S_OK, "got 0x%08x\n", hr);
1088 IDWriteTextLayout_Release(layout);
1091 IDWriteTextFormat_Release(format);
1092 IDWriteFactory_Release(factory);
1095 static void test_CreateTextFormat(void)
1097 static const WCHAR emptyW[] = {0};
1098 IDWriteFontCollection *collection, *syscoll;
1099 DWRITE_PARAGRAPH_ALIGNMENT paralign;
1100 DWRITE_READING_DIRECTION readdir;
1101 DWRITE_WORD_WRAPPING wrapping;
1102 DWRITE_TEXT_ALIGNMENT align;
1103 DWRITE_FLOW_DIRECTION flow;
1104 DWRITE_LINE_SPACING_METHOD method;
1105 DWRITE_TRIMMING trimming;
1106 IDWriteTextFormat *format;
1107 FLOAT spacing, baseline;
1108 IDWriteInlineObject *trimmingsign;
1109 IDWriteFactory *factory;
1110 HRESULT hr;
1112 factory = create_factory();
1114 /* zero/negative font size */
1115 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
1116 DWRITE_FONT_STRETCH_NORMAL, 0.0f, enusW, &format);
1117 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1119 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
1120 DWRITE_FONT_STRETCH_NORMAL, -10.0f, enusW, &format);
1121 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1123 /* invalid font properties */
1124 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, 1000, DWRITE_FONT_STYLE_NORMAL,
1125 DWRITE_FONT_STRETCH_NORMAL, 10.0f, enusW, &format);
1126 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1128 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_ITALIC + 1,
1129 DWRITE_FONT_STRETCH_NORMAL, 10.0f, enusW, &format);
1130 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1132 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_ITALIC,
1133 10, 10.0f, enusW, &format);
1134 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1136 /* empty family name */
1137 hr = IDWriteFactory_CreateTextFormat(factory, emptyW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
1138 DWRITE_FONT_STRETCH_NORMAL, 10.0f, enusW, &format);
1139 ok(hr == S_OK, "got 0x%08x\n", hr);
1140 IDWriteTextFormat_Release(format);
1142 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
1143 DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format);
1144 ok(hr == S_OK, "got 0x%08x\n", hr);
1146 if (0) /* crashes on native */
1147 hr = IDWriteTextFormat_GetFontCollection(format, NULL);
1149 collection = NULL;
1150 hr = IDWriteTextFormat_GetFontCollection(format, &collection);
1151 ok(hr == S_OK, "got 0x%08x\n", hr);
1152 ok(collection != NULL, "got %p\n", collection);
1154 hr = IDWriteFactory_GetSystemFontCollection(factory, &syscoll, FALSE);
1155 ok(hr == S_OK, "got 0x%08x\n", hr);
1156 ok(collection == syscoll, "got %p, was %p\n", syscoll, collection);
1157 IDWriteFontCollection_Release(syscoll);
1158 IDWriteFontCollection_Release(collection);
1160 /* default format properties */
1161 align = IDWriteTextFormat_GetTextAlignment(format);
1162 ok(align == DWRITE_TEXT_ALIGNMENT_LEADING, "got %d\n", align);
1164 paralign = IDWriteTextFormat_GetParagraphAlignment(format);
1165 ok(paralign == DWRITE_PARAGRAPH_ALIGNMENT_NEAR, "got %d\n", paralign);
1167 wrapping = IDWriteTextFormat_GetWordWrapping(format);
1168 ok(wrapping == DWRITE_WORD_WRAPPING_WRAP, "got %d\n", wrapping);
1170 readdir = IDWriteTextFormat_GetReadingDirection(format);
1171 ok(readdir == DWRITE_READING_DIRECTION_LEFT_TO_RIGHT, "got %d\n", readdir);
1173 flow = IDWriteTextFormat_GetFlowDirection(format);
1174 ok(flow == DWRITE_FLOW_DIRECTION_TOP_TO_BOTTOM, "got %d\n", flow);
1176 hr = IDWriteTextFormat_GetLineSpacing(format, &method, &spacing, &baseline);
1177 ok(hr == S_OK, "got 0x%08x\n", hr);
1178 ok(spacing == 0.0, "got %f\n", spacing);
1179 ok(baseline == 0.0, "got %f\n", baseline);
1180 ok(method == DWRITE_LINE_SPACING_METHOD_DEFAULT, "got %d\n", method);
1182 trimming.granularity = DWRITE_TRIMMING_GRANULARITY_WORD;
1183 trimming.delimiter = 10;
1184 trimming.delimiterCount = 10;
1185 trimmingsign = (void*)0xdeadbeef;
1186 hr = IDWriteTextFormat_GetTrimming(format, &trimming, &trimmingsign);
1187 ok(hr == S_OK, "got 0x%08x\n", hr);
1188 ok(trimming.granularity == DWRITE_TRIMMING_GRANULARITY_NONE, "got %d\n", trimming.granularity);
1189 ok(trimming.delimiter == 0, "got %d\n", trimming.delimiter);
1190 ok(trimming.delimiterCount == 0, "got %d\n", trimming.delimiterCount);
1191 ok(trimmingsign == NULL, "got %p\n", trimmingsign);
1193 /* setters */
1194 hr = IDWriteTextFormat_SetTextAlignment(format, DWRITE_TEXT_ALIGNMENT_LEADING);
1195 ok(hr == S_OK, "got 0x%08x\n", hr);
1197 hr = IDWriteTextFormat_SetTextAlignment(format, DWRITE_TEXT_ALIGNMENT_JUSTIFIED+1);
1198 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1200 hr = IDWriteTextFormat_SetParagraphAlignment(format, DWRITE_PARAGRAPH_ALIGNMENT_NEAR);
1201 ok(hr == S_OK, "got 0x%08x\n", hr);
1203 hr = IDWriteTextFormat_SetParagraphAlignment(format, DWRITE_PARAGRAPH_ALIGNMENT_CENTER+1);
1204 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1206 hr = IDWriteTextFormat_SetWordWrapping(format, DWRITE_WORD_WRAPPING_WRAP);
1207 ok(hr == S_OK, "got 0x%08x\n", hr);
1209 hr = IDWriteTextFormat_SetWordWrapping(format, DWRITE_WORD_WRAPPING_CHARACTER+1);
1210 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1212 hr = IDWriteTextFormat_SetReadingDirection(format, DWRITE_READING_DIRECTION_LEFT_TO_RIGHT);
1213 ok(hr == S_OK, "got 0x%08x\n", hr);
1215 hr = IDWriteTextFormat_SetFlowDirection(format, DWRITE_FLOW_DIRECTION_TOP_TO_BOTTOM);
1216 ok(hr == S_OK, "got 0x%08x\n", hr);
1219 hr = IDWriteTextFormat_SetTrimming(format, &trimming, NULL);
1220 ok(hr == S_OK, "got 0x%08x\n", hr);
1222 /* invalid granularity */
1223 trimming.granularity = 10;
1224 trimming.delimiter = 0;
1225 trimming.delimiterCount = 0;
1226 hr = IDWriteTextFormat_SetTrimming(format, &trimming, NULL);
1227 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1229 IDWriteTextFormat_Release(format);
1230 IDWriteFactory_Release(factory);
1233 static void test_GetLocaleName(void)
1235 static const WCHAR strW[] = {'s','t','r','i','n','g',0};
1236 static const WCHAR ruW[] = {'r','u',0};
1237 IDWriteTextLayout *layout;
1238 IDWriteTextFormat *format, *format2;
1239 IDWriteFactory *factory;
1240 WCHAR buff[10];
1241 UINT32 len;
1242 HRESULT hr;
1244 factory = create_factory();
1246 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
1247 DWRITE_FONT_STRETCH_NORMAL, 10.0, ruW, &format);
1248 ok(hr == S_OK, "got 0x%08x\n", hr);
1250 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, strW, 0, format, 100.0, 100.0, 1.0, NULL, FALSE, &layout);
1251 ok(hr == S_OK, "got 0x%08x\n", hr);
1253 hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextFormat, (void**)&format2);
1254 ok(hr == S_OK, "got 0x%08x\n", hr);
1256 len = IDWriteTextFormat_GetLocaleNameLength(format2);
1257 ok(len == 2, "got %u\n", len);
1258 len = IDWriteTextFormat_GetLocaleNameLength(format);
1259 ok(len == 2, "got %u\n", len);
1260 hr = IDWriteTextFormat_GetLocaleName(format2, buff, len);
1261 ok(hr == E_NOT_SUFFICIENT_BUFFER, "got 0x%08x\n", hr);
1262 hr = IDWriteTextFormat_GetLocaleName(format2, buff, len+1);
1263 ok(hr == S_OK, "got 0x%08x\n", hr);
1264 ok(!lstrcmpW(buff, ruW), "got %s\n", wine_dbgstr_w(buff));
1265 hr = IDWriteTextFormat_GetLocaleName(format, buff, len);
1266 ok(hr == E_NOT_SUFFICIENT_BUFFER, "got 0x%08x\n", hr);
1267 hr = IDWriteTextFormat_GetLocaleName(format, buff, len+1);
1268 ok(hr == S_OK, "got 0x%08x\n", hr);
1269 ok(!lstrcmpW(buff, ruW), "got %s\n", wine_dbgstr_w(buff));
1271 IDWriteTextLayout_Release(layout);
1272 IDWriteTextFormat_Release(format);
1273 IDWriteTextFormat_Release(format2);
1274 IDWriteFactory_Release(factory);
1277 static const struct drawcall_entry drawellipsis_seq[] = {
1278 { DRAW_GLYPHRUN, {0x2026, 0}, {'e','n','-','g','b',0}, 1 },
1279 { DRAW_LAST_KIND }
1282 static void test_CreateEllipsisTrimmingSign(void)
1284 static const WCHAR engbW[] = {'e','n','-','G','B',0};
1285 DWRITE_INLINE_OBJECT_METRICS metrics;
1286 DWRITE_BREAK_CONDITION before, after;
1287 struct renderer_context ctxt;
1288 IDWriteTextFormat *format;
1289 IDWriteInlineObject *sign;
1290 IDWriteFactory *factory;
1291 IUnknown *unk, *effect;
1292 HRESULT hr;
1294 factory = create_factory();
1296 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
1297 DWRITE_FONT_STRETCH_NORMAL, 10.0, engbW, &format);
1298 ok(hr == S_OK, "got 0x%08x\n", hr);
1300 EXPECT_REF(format, 1);
1301 hr = IDWriteFactory_CreateEllipsisTrimmingSign(factory, format, &sign);
1302 ok(hr == S_OK, "got 0x%08x\n", hr);
1303 EXPECT_REF(format, 1);
1305 hr = IDWriteInlineObject_QueryInterface(sign, &IID_IDWriteTextLayout, (void**)&unk);
1306 ok(hr == E_NOINTERFACE, "got 0x%08x\n", hr);
1308 if (0) {/* crashes on native */
1309 hr = IDWriteInlineObject_GetBreakConditions(sign, NULL, NULL);
1310 hr = IDWriteInlineObject_GetMetrics(sign, NULL);
1312 metrics.width = 0.0;
1313 metrics.height = 123.0;
1314 metrics.baseline = 123.0;
1315 metrics.supportsSideways = TRUE;
1316 hr = IDWriteInlineObject_GetMetrics(sign, &metrics);
1317 ok(hr == S_OK, "got 0x%08x\n", hr);
1318 ok(metrics.width > 0.0, "got %.2f\n", metrics.width);
1319 ok(metrics.height == 0.0, "got %.2f\n", metrics.height);
1320 ok(metrics.baseline == 0.0, "got %.2f\n", metrics.baseline);
1321 ok(!metrics.supportsSideways, "got %d\n", metrics.supportsSideways);
1323 before = after = DWRITE_BREAK_CONDITION_CAN_BREAK;
1324 hr = IDWriteInlineObject_GetBreakConditions(sign, &before, &after);
1325 ok(hr == S_OK, "got 0x%08x\n", hr);
1326 ok(before == DWRITE_BREAK_CONDITION_NEUTRAL, "got %d\n", before);
1327 ok(after == DWRITE_BREAK_CONDITION_NEUTRAL, "got %d\n", after);
1329 /* Draw tests */
1330 flush_sequence(sequences, RENDERER_ID);
1331 hr = IDWriteInlineObject_Draw(sign, NULL, &testrenderer, 0.0, 0.0, FALSE, FALSE, NULL);
1332 ok(hr == S_OK, "got 0x%08x\n", hr);
1333 ok_sequence(sequences, RENDERER_ID, drawellipsis_seq, "ellipsis sign draw test", FALSE);
1335 effect = create_test_effect();
1337 EXPECT_REF(effect, 1);
1338 flush_sequence(sequences, RENDERER_ID);
1339 hr = IDWriteInlineObject_Draw(sign, NULL, &testrenderer, 0.0f, 0.0f, FALSE, FALSE, effect);
1340 ok(hr == S_OK, "Failed to draw trimming sign, hr %#x.\n", hr);
1341 ok_sequence(sequences, RENDERER_ID, drawellipsis_seq, "ellipsis sign draw with effect test", FALSE);
1342 EXPECT_REF(effect, 1);
1344 IUnknown_Release(effect);
1346 flush_sequence(sequences, RENDERER_ID);
1347 hr = IDWriteInlineObject_Draw(sign, NULL, &testrenderer, 0.0f, 0.0f, FALSE, FALSE, (void *)0xdeadbeef);
1348 ok(hr == S_OK, "Failed to draw trimming sign, hr %#x.\n", hr);
1349 ok_sequence(sequences, RENDERER_ID, drawellipsis_seq, "ellipsis sign draw with effect test", FALSE);
1351 memset(&ctxt, 0, sizeof(ctxt));
1352 hr = IDWriteInlineObject_Draw(sign, &ctxt, &testrenderer, 123.0f, 456.0f, FALSE, FALSE, NULL);
1353 ok(hr == S_OK, "Failed to draw trimming sign, hr %#x.\n", hr);
1354 ok(ctxt.originX == 123.0f && ctxt.originY == 456.0f, "Unexpected drawing origin\n");
1356 IDWriteInlineObject_Release(sign);
1358 /* Centered format */
1359 hr = IDWriteTextFormat_SetTextAlignment(format, DWRITE_TEXT_ALIGNMENT_CENTER);
1360 ok(hr == S_OK, "Failed to set text alignment, hr %#x.\n", hr);
1362 hr = IDWriteFactory_CreateEllipsisTrimmingSign(factory, format, &sign);
1363 ok(hr == S_OK, "got 0x%08x\n", hr);
1365 memset(&ctxt, 0, sizeof(ctxt));
1366 hr = IDWriteInlineObject_Draw(sign, &ctxt, &testrenderer, 123.0f, 456.0f, FALSE, FALSE, NULL);
1367 ok(hr == S_OK, "Failed to draw trimming sign, hr %#x.\n", hr);
1368 ok(ctxt.originX == 123.0f && ctxt.originY == 456.0f, "Unexpected drawing origin\n");
1370 IDWriteInlineObject_Release(sign);
1372 /* non-orthogonal flow/reading combination */
1373 hr = IDWriteTextFormat_SetReadingDirection(format, DWRITE_READING_DIRECTION_LEFT_TO_RIGHT);
1374 ok(hr == S_OK, "got 0x%08x\n", hr);
1376 hr = IDWriteTextFormat_SetFlowDirection(format, DWRITE_FLOW_DIRECTION_LEFT_TO_RIGHT);
1377 ok(hr == S_OK || broken(hr == E_INVALIDARG) /* vista, win7 */, "got 0x%08x\n", hr);
1378 if (hr == S_OK) {
1379 hr = IDWriteFactory_CreateEllipsisTrimmingSign(factory, format, &sign);
1380 ok(hr == DWRITE_E_FLOWDIRECTIONCONFLICTS, "got 0x%08x\n", hr);
1383 IDWriteTextFormat_Release(format);
1384 IDWriteFactory_Release(factory);
1387 static void test_fontweight(void)
1389 static const WCHAR strW[] = {'s','t','r','i','n','g',0};
1390 static const WCHAR ruW[] = {'r','u',0};
1391 IDWriteTextFormat *format, *fmt2;
1392 IDWriteTextLayout *layout;
1393 DWRITE_FONT_WEIGHT weight;
1394 DWRITE_TEXT_RANGE range;
1395 IDWriteFactory *factory;
1396 FLOAT size;
1397 HRESULT hr;
1399 factory = create_factory();
1401 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_BOLD, DWRITE_FONT_STYLE_NORMAL,
1402 DWRITE_FONT_STRETCH_NORMAL, 10.0, ruW, &format);
1403 ok(hr == S_OK, "got 0x%08x\n", hr);
1405 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, strW, 6, format, 100.0, 100.0, 1.0, NULL, FALSE, &layout);
1406 ok(hr == S_OK, "got 0x%08x\n", hr);
1408 hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextFormat, (void**)&fmt2);
1409 ok(hr == S_OK, "got 0x%08x\n", hr);
1411 weight = IDWriteTextFormat_GetFontWeight(fmt2);
1412 ok(weight == DWRITE_FONT_WEIGHT_BOLD, "got %u\n", weight);
1414 range.startPosition = range.length = 0;
1415 hr = IDWriteTextLayout_GetFontWeight(layout, 0, &weight, &range);
1416 ok(hr == S_OK, "got 0x%08x\n", hr);
1417 ok(range.startPosition == 0 && range.length == ~0u, "got %u, %u\n", range.startPosition, range.length);
1419 range.startPosition = 0;
1420 range.length = 6;
1421 hr = IDWriteTextLayout_SetFontWeight(layout, DWRITE_FONT_WEIGHT_NORMAL, range);
1422 ok(hr == S_OK, "got 0x%08x\n", hr);
1424 range.startPosition = range.length = 0;
1425 hr = IDWriteTextLayout_GetFontWeight(layout, 0, &weight, &range);
1426 ok(hr == S_OK, "got 0x%08x\n", hr);
1427 ok(range.startPosition == 0 && range.length == 6, "got %u, %u\n", range.startPosition, range.length);
1429 /* IDWriteTextFormat methods output doesn't reflect layout changes */
1430 weight = IDWriteTextFormat_GetFontWeight(fmt2);
1431 ok(weight == DWRITE_FONT_WEIGHT_BOLD, "got %u\n", weight);
1433 range.length = 0;
1434 weight = DWRITE_FONT_WEIGHT_BOLD;
1435 hr = IDWriteTextLayout_GetFontWeight(layout, 0, &weight, &range);
1436 ok(hr == S_OK, "got 0x%08x\n", hr);
1437 ok(weight == DWRITE_FONT_WEIGHT_NORMAL, "got %d\n", weight);
1438 ok(range.length == 6, "got %d\n", range.length);
1440 range.startPosition = 0;
1441 range.length = 6;
1442 hr = IDWriteTextLayout_SetFontWeight(layout, 1000, range);
1443 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1445 size = IDWriteTextLayout_GetMaxWidth(layout);
1446 ok(size == 100.0, "got %.2f\n", size);
1448 hr = IDWriteTextLayout_SetMaxWidth(layout, 0.0);
1449 ok(hr == S_OK, "got 0x%08x\n", hr);
1451 size = IDWriteTextLayout_GetMaxWidth(layout);
1452 ok(size == 0.0, "got %.2f\n", size);
1454 hr = IDWriteTextLayout_SetMaxWidth(layout, -1.0);
1455 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1457 size = IDWriteTextLayout_GetMaxWidth(layout);
1458 ok(size == 0.0, "got %.2f\n", size);
1460 hr = IDWriteTextLayout_SetMaxWidth(layout, 100.0);
1461 ok(hr == S_OK, "got 0x%08x\n", hr);
1463 size = IDWriteTextLayout_GetMaxWidth(layout);
1464 ok(size == 100.0, "got %.2f\n", size);
1466 size = IDWriteTextLayout_GetMaxHeight(layout);
1467 ok(size == 100.0, "got %.2f\n", size);
1469 hr = IDWriteTextLayout_SetMaxHeight(layout, 0.0);
1470 ok(hr == S_OK, "got 0x%08x\n", hr);
1472 size = IDWriteTextLayout_GetMaxHeight(layout);
1473 ok(size == 0.0, "got %.2f\n", size);
1475 hr = IDWriteTextLayout_SetMaxHeight(layout, -1.0);
1476 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1478 size = IDWriteTextLayout_GetMaxHeight(layout);
1479 ok(size == 0.0, "got %.2f\n", size);
1481 hr = IDWriteTextLayout_SetMaxHeight(layout, 100.0);
1482 ok(hr == S_OK, "got 0x%08x\n", hr);
1484 size = IDWriteTextLayout_GetMaxHeight(layout);
1485 ok(size == 100.0, "got %.2f\n", size);
1487 IDWriteTextLayout_Release(layout);
1488 IDWriteTextFormat_Release(fmt2);
1489 IDWriteTextFormat_Release(format);
1490 IDWriteFactory_Release(factory);
1493 static void test_SetInlineObject(void)
1495 static const WCHAR strW[] = {'s','t','r','i','n','g',0};
1496 static const WCHAR ruW[] = {'r','u',0};
1498 IDWriteInlineObject *inlineobj, *inlineobj2, *inlinetest;
1499 IDWriteTextFormat *format;
1500 IDWriteTextLayout *layout;
1501 DWRITE_TEXT_RANGE range, r2;
1502 IDWriteFactory *factory;
1503 HRESULT hr;
1505 factory = create_factory();
1507 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_BOLD, DWRITE_FONT_STYLE_NORMAL,
1508 DWRITE_FONT_STRETCH_NORMAL, 10.0, ruW, &format);
1509 ok(hr == S_OK, "got 0x%08x\n", hr);
1511 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, strW, 6, format, 100.0, 100.0, 1.0, NULL, FALSE, &layout);
1512 ok(hr == S_OK, "got 0x%08x\n", hr);
1514 hr = IDWriteFactory_CreateEllipsisTrimmingSign(factory, format, &inlineobj);
1515 ok(hr == S_OK, "got 0x%08x\n", hr);
1517 hr = IDWriteFactory_CreateEllipsisTrimmingSign(factory, format, &inlineobj2);
1518 ok(hr == S_OK, "got 0x%08x\n", hr);
1520 EXPECT_REF(inlineobj, 1);
1521 EXPECT_REF(inlineobj2, 1);
1523 inlinetest = (void*)0x1;
1524 hr = IDWriteTextLayout_GetInlineObject(layout, 0, &inlinetest, NULL);
1525 ok(hr == S_OK, "got 0x%08x\n", hr);
1526 ok(inlinetest == NULL, "got %p\n", inlinetest);
1528 range.startPosition = 0;
1529 range.length = 2;
1530 hr = IDWriteTextLayout_SetInlineObject(layout, inlineobj, range);
1531 ok(hr == S_OK, "got 0x%08x\n", hr);
1533 EXPECT_REF(inlineobj, 2);
1535 inlinetest = (void*)0x1;
1536 hr = IDWriteTextLayout_GetInlineObject(layout, 2, &inlinetest, NULL);
1537 ok(hr == S_OK, "got 0x%08x\n", hr);
1538 ok(inlinetest == NULL, "got %p\n", inlinetest);
1540 inlinetest = NULL;
1541 r2.startPosition = r2.length = 100;
1542 hr = IDWriteTextLayout_GetInlineObject(layout, 0, &inlinetest, &r2);
1543 ok(hr == S_OK, "got 0x%08x\n", hr);
1544 ok(inlinetest == inlineobj, "got %p\n", inlinetest);
1545 ok(r2.startPosition == 0 && r2.length == 2, "got %d, %d\n", r2.startPosition, r2.length);
1546 IDWriteInlineObject_Release(inlinetest);
1548 EXPECT_REF(inlineobj, 2);
1550 /* get from somewhere inside a range */
1551 inlinetest = NULL;
1552 r2.startPosition = r2.length = 100;
1553 hr = IDWriteTextLayout_GetInlineObject(layout, 1, &inlinetest, &r2);
1554 ok(hr == S_OK, "got 0x%08x\n", hr);
1555 ok(inlinetest == inlineobj, "got %p\n", inlinetest);
1556 ok(r2.startPosition == 0 && r2.length == 2, "got %d, %d\n", r2.startPosition, r2.length);
1557 IDWriteInlineObject_Release(inlinetest);
1559 EXPECT_REF(inlineobj, 2);
1561 range.startPosition = 1;
1562 range.length = 1;
1563 hr = IDWriteTextLayout_SetInlineObject(layout, inlineobj2, range);
1564 ok(hr == S_OK, "got 0x%08x\n", hr);
1566 inlinetest = NULL;
1567 r2.startPosition = r2.length = 100;
1568 hr = IDWriteTextLayout_GetInlineObject(layout, 1, &inlinetest, &r2);
1569 ok(hr == S_OK, "got 0x%08x\n", hr);
1570 ok(inlinetest == inlineobj2, "got %p\n", inlinetest);
1571 ok(r2.startPosition == 1 && r2.length == 1, "got %d, %d\n", r2.startPosition, r2.length);
1572 IDWriteInlineObject_Release(inlinetest);
1574 EXPECT_REF(inlineobj, 2);
1575 EXPECT_REF(inlineobj2, 2);
1577 inlinetest = NULL;
1578 r2.startPosition = r2.length = 100;
1579 hr = IDWriteTextLayout_GetInlineObject(layout, 0, &inlinetest, &r2);
1580 ok(hr == S_OK, "got 0x%08x\n", hr);
1581 ok(inlinetest == inlineobj, "got %p\n", inlinetest);
1582 ok(r2.startPosition == 0 && r2.length == 1, "got %d, %d\n", r2.startPosition, r2.length);
1583 IDWriteInlineObject_Release(inlinetest);
1585 EXPECT_REF(inlineobj, 2);
1587 range.startPosition = 1;
1588 range.length = 1;
1589 hr = IDWriteTextLayout_SetInlineObject(layout, inlineobj, range);
1590 ok(hr == S_OK, "got 0x%08x\n", hr);
1592 r2.startPosition = r2.length = 100;
1593 hr = IDWriteTextLayout_GetInlineObject(layout, 0, &inlinetest, &r2);
1594 ok(hr == S_OK, "got 0x%08x\n", hr);
1595 ok(inlinetest == inlineobj, "got %p\n", inlinetest);
1596 ok(r2.startPosition == 0 && r2.length == 2, "got %d, %d\n", r2.startPosition, r2.length);
1597 IDWriteInlineObject_Release(inlinetest);
1599 EXPECT_REF(inlineobj, 2);
1601 range.startPosition = 1;
1602 range.length = 2;
1603 hr = IDWriteTextLayout_SetInlineObject(layout, inlineobj, range);
1604 ok(hr == S_OK, "got 0x%08x\n", hr);
1606 EXPECT_REF(inlineobj, 2);
1608 r2.startPosition = r2.length = 100;
1609 hr = IDWriteTextLayout_GetInlineObject(layout, 0, &inlinetest, &r2);
1610 ok(hr == S_OK, "got 0x%08x\n", hr);
1611 ok(inlinetest == inlineobj, "got %p\n", inlinetest);
1612 ok(r2.startPosition == 0 && r2.length == 3, "got %d, %d\n", r2.startPosition, r2.length);
1613 IDWriteInlineObject_Release(inlinetest);
1615 EXPECT_REF(inlineobj, 2);
1616 EXPECT_REF(inlineobj2, 1);
1618 IDWriteTextLayout_Release(layout);
1620 EXPECT_REF(inlineobj, 1);
1622 IDWriteInlineObject_Release(inlineobj);
1623 IDWriteInlineObject_Release(inlineobj2);
1624 IDWriteTextFormat_Release(format);
1625 IDWriteFactory_Release(factory);
1628 /* drawing calls sequence doesn't depend on run order, instead all runs are
1629 drawn first, inline objects next and then underline/strikes */
1630 static const struct drawcall_entry draw_seq[] = {
1631 { DRAW_GLYPHRUN, {'s',0}, {'r','u',0}, 1 },
1632 { DRAW_GLYPHRUN, {'r','i',0}, {'r','u',0}, 2 },
1633 { DRAW_GLYPHRUN|DRAW_EFFECT, {'n',0}, {'r','u',0}, 1 },
1634 { DRAW_GLYPHRUN, {'g',0}, {'r','u',0}, 1 },
1635 { DRAW_INLINE },
1636 { DRAW_UNDERLINE, {0}, {'r','u',0} },
1637 { DRAW_STRIKETHROUGH },
1638 { DRAW_LAST_KIND }
1641 static const struct drawcall_entry draw_seq2[] = {
1642 { DRAW_GLYPHRUN, {'s',0}, {'r','u',0}, 1 },
1643 { DRAW_GLYPHRUN, {'t',0}, {'r','u',0}, 1 },
1644 { DRAW_GLYPHRUN, {'r',0}, {'r','u',0}, 1 },
1645 { DRAW_GLYPHRUN, {'i',0}, {'r','u',0}, 1 },
1646 { DRAW_GLYPHRUN, {'n',0}, {'r','u',0}, 1 },
1647 { DRAW_GLYPHRUN, {'g',0}, {'r','u',0}, 1 },
1648 { DRAW_LAST_KIND }
1651 static const struct drawcall_entry draw_seq3[] = {
1652 { DRAW_GLYPHRUN, {0x202a,0x202c,0}, {'r','u',0}, 0 },
1653 { DRAW_GLYPHRUN, {'a','b',0}, {'r','u',0}, 2 },
1654 { DRAW_LAST_KIND }
1657 static const struct drawcall_entry draw_seq4[] = {
1658 { DRAW_GLYPHRUN, {'s','t','r',0}, {'r','u',0}, 3 },
1659 { DRAW_GLYPHRUN, {'i','n','g',0}, {'r','u',0}, 3 },
1660 { DRAW_STRIKETHROUGH },
1661 { DRAW_LAST_KIND }
1664 static const struct drawcall_entry draw_seq5[] = {
1665 { DRAW_GLYPHRUN, {'s','t',0}, {'r','u',0}, 2 },
1666 { DRAW_GLYPHRUN, {'r','i',0}, {'r','u',0}, 2 },
1667 { DRAW_GLYPHRUN, {'n','g',0}, {'r','u',0}, 2 },
1668 { DRAW_STRIKETHROUGH },
1669 { DRAW_LAST_KIND }
1672 static const struct drawcall_entry empty_seq[] = {
1673 { DRAW_LAST_KIND }
1676 static const struct drawcall_entry draw_single_run_seq[] = {
1677 { DRAW_GLYPHRUN, {'s','t','r','i','n','g',0}, {'r','u',0}, 6 },
1678 { DRAW_LAST_KIND }
1681 static const struct drawcall_entry draw_reordered_run_seq[] = {
1682 { DRAW_GLYPHRUN, {'1','2','3','-','5','2',0}, {'r','u',0}, 6 },
1683 { DRAW_GLYPHRUN, {0x64a,0x64f,0x633,0x627,0x648,0x650,0x64a,0}, {'r','u',0}, 7 },
1684 { DRAW_GLYPHRUN, {'7','1',0}, {'r','u',0}, 2 },
1685 { DRAW_GLYPHRUN, {'.',0}, {'r','u',0}, 1 },
1686 { DRAW_LAST_KIND }
1689 static void test_Draw(void)
1691 static const WCHAR str3W[] = {'1','2','3','-','5','2',0x64a,0x64f,0x633,0x627,0x648,0x650,
1692 0x64a,'7','1','.',0};
1693 static const WCHAR strW[] = {'s','t','r','i','n','g',0};
1694 static const WCHAR str2W[] = {0x202a,0x202c,'a','b',0};
1695 static const WCHAR ruW[] = {'r','u',0};
1696 IDWriteInlineObject *inlineobj;
1697 struct renderer_context ctxt;
1698 IDWriteTextFormat *format;
1699 IDWriteTextLayout *layout;
1700 DWRITE_TEXT_RANGE range;
1701 IDWriteFactory *factory;
1702 DWRITE_TEXT_METRICS tm;
1703 DWRITE_MATRIX m;
1704 HRESULT hr;
1706 factory = create_factory();
1708 memset(&ctxt, 0, sizeof(ctxt));
1709 ctxt.snapping_disabled = TRUE;
1711 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_BOLD, DWRITE_FONT_STYLE_NORMAL,
1712 DWRITE_FONT_STRETCH_NORMAL, 10.0, ruW, &format);
1713 ok(hr == S_OK, "got 0x%08x\n", hr);
1715 hr = IDWriteFactory_CreateTextLayout(factory, strW, 6, format, 100.0, 100.0, &layout);
1716 ok(hr == S_OK, "got 0x%08x\n", hr);
1718 hr = IDWriteFactory_CreateEllipsisTrimmingSign(factory, format, &inlineobj);
1719 ok(hr == S_OK, "got 0x%08x\n", hr);
1721 range.startPosition = 5;
1722 range.length = 1;
1723 hr = IDWriteTextLayout_SetStrikethrough(layout, TRUE, range);
1724 ok(hr == S_OK, "got 0x%08x\n", hr);
1726 range.startPosition = 1;
1727 range.length = 1;
1728 hr = IDWriteTextLayout_SetInlineObject(layout, inlineobj, range);
1729 ok(hr == S_OK, "got 0x%08x\n", hr);
1731 range.startPosition = 4;
1732 range.length = 1;
1733 hr = IDWriteTextLayout_SetDrawingEffect(layout, (IUnknown*)inlineobj, range);
1734 ok(hr == S_OK, "got 0x%08x\n", hr);
1736 range.startPosition = 0;
1737 range.length = 1;
1738 hr = IDWriteTextLayout_SetUnderline(layout, TRUE, range);
1739 ok(hr == S_OK, "got 0x%08x\n", hr);
1741 flush_sequence(sequences, RENDERER_ID);
1742 hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0, 0.0);
1743 ok(hr == S_OK, "got 0x%08x\n", hr);
1744 ok_sequence(sequences, RENDERER_ID, draw_seq, "draw test", FALSE);
1745 IDWriteTextLayout_Release(layout);
1747 /* with reduced width DrawGlyphRun() is called for every line */
1748 hr = IDWriteFactory_CreateTextLayout(factory, strW, 6, format, 5.0, 100.0, &layout);
1749 ok(hr == S_OK, "got 0x%08x\n", hr);
1750 flush_sequence(sequences, RENDERER_ID);
1751 hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0, 0.0);
1752 ok(hr == S_OK, "got 0x%08x\n", hr);
1753 ok_sequence(sequences, RENDERER_ID, draw_seq2, "draw test 2", TRUE);
1754 hr = IDWriteTextLayout_GetMetrics(layout, &tm);
1755 ok(hr == S_OK, "got 0x%08x\n", hr);
1756 todo_wine
1757 ok(tm.lineCount == 6, "got %u\n", tm.lineCount);
1758 IDWriteTextLayout_Release(layout);
1760 /* string with control characters */
1761 hr = IDWriteFactory_CreateTextLayout(factory, str2W, 4, format, 500.0, 100.0, &layout);
1762 ok(hr == S_OK, "got 0x%08x\n", hr);
1763 flush_sequence(sequences, RENDERER_ID);
1764 hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0, 0.0);
1765 ok(hr == S_OK, "got 0x%08x\n", hr);
1766 ok_sequence(sequences, RENDERER_ID, draw_seq3, "draw test 3", FALSE);
1767 IDWriteTextLayout_Release(layout);
1769 /* strikethrough splits ranges from renderer point of view, but doesn't break
1770 shaping */
1771 hr = IDWriteFactory_CreateTextLayout(factory, strW, 6, format, 500.0, 100.0, &layout);
1772 ok(hr == S_OK, "got 0x%08x\n", hr);
1773 flush_sequence(sequences, RENDERER_ID);
1775 range.startPosition = 0;
1776 range.length = 3;
1777 hr = IDWriteTextLayout_SetStrikethrough(layout, TRUE, range);
1778 ok(hr == S_OK, "got 0x%08x\n", hr);
1780 hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0, 0.0);
1781 ok(hr == S_OK, "got 0x%08x\n", hr);
1782 ok_sequence(sequences, RENDERER_ID, draw_seq4, "draw test 4", FALSE);
1783 IDWriteTextLayout_Release(layout);
1785 /* strikethrough somewhere in the middle */
1786 hr = IDWriteFactory_CreateTextLayout(factory, strW, 6, format, 500.0, 100.0, &layout);
1787 ok(hr == S_OK, "got 0x%08x\n", hr);
1788 flush_sequence(sequences, RENDERER_ID);
1790 range.startPosition = 2;
1791 range.length = 2;
1792 hr = IDWriteTextLayout_SetStrikethrough(layout, TRUE, range);
1793 ok(hr == S_OK, "got 0x%08x\n", hr);
1795 hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0, 0.0);
1796 ok(hr == S_OK, "got 0x%08x\n", hr);
1797 ok_sequence(sequences, RENDERER_ID, draw_seq5, "draw test 5", FALSE);
1798 IDWriteTextLayout_Release(layout);
1800 /* empty string */
1801 hr = IDWriteFactory_CreateTextLayout(factory, strW, 0, format, 500.0, 100.0, &layout);
1802 ok(hr == S_OK, "got 0x%08x\n", hr);
1804 flush_sequence(sequences, RENDERER_ID);
1805 hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0, 0.0);
1806 ok(hr == S_OK, "got 0x%08x\n", hr);
1807 ok_sequence(sequences, RENDERER_ID, empty_seq, "draw test 6", FALSE);
1808 IDWriteTextLayout_Release(layout);
1810 ctxt.gdicompat = TRUE;
1811 ctxt.use_gdi_natural = TRUE;
1813 /* different parameter combinations with gdi-compatible layout */
1814 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, strW, 6, format, 100.0, 100.0, 1.0, NULL, TRUE, &layout);
1815 ok(hr == S_OK, "got 0x%08x\n", hr);
1816 flush_sequence(sequences, RENDERER_ID);
1817 hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0, 0.0);
1818 ok(hr == S_OK, "got 0x%08x\n", hr);
1819 ok_sequence(sequences, RENDERER_ID, draw_single_run_seq, "draw test 7", FALSE);
1821 /* text alignment keeps pixel-aligned origin */
1822 hr = IDWriteTextLayout_GetMetrics(layout, &tm);
1823 ok(hr == S_OK, "got 0x%08x\n", hr);
1824 ok(tm.width == floorf(tm.width), "got %f\n", tm.width);
1826 hr = IDWriteTextLayout_SetMaxWidth(layout, tm.width + 3.0);
1827 ok(hr == S_OK, "got 0x%08x\n", hr);
1828 hr = IDWriteTextLayout_SetTextAlignment(layout, DWRITE_TEXT_ALIGNMENT_CENTER);
1829 ok(hr == S_OK, "got 0x%08x\n", hr);
1831 ctxt.originX = ctxt.originY = 0.0;
1832 flush_sequence(sequences, RENDERER_ID);
1833 hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0, 0.0);
1834 ok(hr == S_OK, "got 0x%08x\n", hr);
1835 ok_sequence(sequences, RENDERER_ID, draw_single_run_seq, "draw test 7", FALSE);
1836 ok(ctxt.originX != 0.0 && ctxt.originX == floorf(ctxt.originX), "got %f\n", ctxt.originX);
1838 IDWriteTextLayout_Release(layout);
1840 ctxt.gdicompat = TRUE;
1841 ctxt.use_gdi_natural = FALSE;
1843 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, strW, 6, format, 100.0, 100.0, 1.0, NULL, FALSE, &layout);
1844 ok(hr == S_OK, "got 0x%08x\n", hr);
1845 flush_sequence(sequences, RENDERER_ID);
1846 hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0, 0.0);
1847 ok(hr == S_OK, "got 0x%08x\n", hr);
1848 ok_sequence(sequences, RENDERER_ID, draw_single_run_seq, "draw test 8", FALSE);
1849 IDWriteTextLayout_Release(layout);
1851 ctxt.gdicompat = TRUE;
1852 ctxt.use_gdi_natural = TRUE;
1854 m.m11 = m.m22 = 2.0;
1855 m.m12 = m.m21 = m.dx = m.dy = 0.0;
1856 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, strW, 6, format, 100.0, 100.0, 1.0, &m, TRUE, &layout);
1857 ok(hr == S_OK, "got 0x%08x\n", hr);
1858 flush_sequence(sequences, RENDERER_ID);
1859 hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0, 0.0);
1860 ok(hr == S_OK, "got 0x%08x\n", hr);
1861 ok_sequence(sequences, RENDERER_ID, draw_single_run_seq, "draw test 9", FALSE);
1862 IDWriteTextLayout_Release(layout);
1864 ctxt.gdicompat = TRUE;
1865 ctxt.use_gdi_natural = FALSE;
1867 m.m11 = m.m22 = 2.0;
1868 m.m12 = m.m21 = m.dx = m.dy = 0.0;
1869 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, strW, 6, format, 100.0, 100.0, 1.0, &m, FALSE, &layout);
1870 ok(hr == S_OK, "got 0x%08x\n", hr);
1871 flush_sequence(sequences, RENDERER_ID);
1872 hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0, 0.0);
1873 ok(hr == S_OK, "got 0x%08x\n", hr);
1874 ok_sequence(sequences, RENDERER_ID, draw_single_run_seq, "draw test 10", FALSE);
1875 IDWriteTextLayout_Release(layout);
1877 IDWriteInlineObject_Release(inlineobj);
1879 /* text that triggers bidi run reordering */
1880 hr = IDWriteFactory_CreateTextLayout(factory, str3W, lstrlenW(str3W), format, 1000.0f, 100.0f, &layout);
1881 ok(hr == S_OK, "got 0x%08x\n", hr);
1883 ctxt.gdicompat = FALSE;
1884 ctxt.use_gdi_natural = FALSE;
1885 ctxt.snapping_disabled = TRUE;
1887 flush_sequence(sequences, RENDERER_ID);
1888 hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0f, 0.0f);
1889 ok(hr == S_OK, "got 0x%08x\n", hr);
1890 ok_sequence(sequences, RENDERER_ID, draw_reordered_run_seq, "draw test 11", FALSE);
1892 IDWriteTextLayout_Release(layout);
1894 IDWriteTextFormat_Release(format);
1895 IDWriteFactory_Release(factory);
1898 static void test_typography(void)
1900 DWRITE_FONT_FEATURE feature;
1901 IDWriteTypography *typography;
1902 IDWriteFactory *factory;
1903 UINT32 count;
1904 HRESULT hr;
1906 factory = create_factory();
1908 hr = IDWriteFactory_CreateTypography(factory, &typography);
1909 ok(hr == S_OK, "got 0x%08x\n", hr);
1911 feature.nameTag = DWRITE_FONT_FEATURE_TAG_KERNING;
1912 feature.parameter = 1;
1913 hr = IDWriteTypography_AddFontFeature(typography, feature);
1914 ok(hr == S_OK, "got 0x%08x\n", hr);
1916 count = IDWriteTypography_GetFontFeatureCount(typography);
1917 ok(count == 1, "got %u\n", count);
1919 /* duplicated features work just fine */
1920 feature.nameTag = DWRITE_FONT_FEATURE_TAG_KERNING;
1921 feature.parameter = 0;
1922 hr = IDWriteTypography_AddFontFeature(typography, feature);
1923 ok(hr == S_OK, "got 0x%08x\n", hr);
1925 count = IDWriteTypography_GetFontFeatureCount(typography);
1926 ok(count == 2, "got %u\n", count);
1928 memset(&feature, 0xcc, sizeof(feature));
1929 hr = IDWriteTypography_GetFontFeature(typography, 0, &feature);
1930 ok(hr == S_OK, "got 0x%08x\n", hr);
1931 ok(feature.nameTag == DWRITE_FONT_FEATURE_TAG_KERNING, "got tag %x\n", feature.nameTag);
1932 ok(feature.parameter == 1, "got %u\n", feature.parameter);
1934 memset(&feature, 0xcc, sizeof(feature));
1935 hr = IDWriteTypography_GetFontFeature(typography, 1, &feature);
1936 ok(hr == S_OK, "got 0x%08x\n", hr);
1937 ok(feature.nameTag == DWRITE_FONT_FEATURE_TAG_KERNING, "got tag %x\n", feature.nameTag);
1938 ok(feature.parameter == 0, "got %u\n", feature.parameter);
1940 hr = IDWriteTypography_GetFontFeature(typography, 2, &feature);
1941 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1943 /* duplicated with same parameter value */
1944 feature.nameTag = DWRITE_FONT_FEATURE_TAG_KERNING;
1945 feature.parameter = 0;
1946 hr = IDWriteTypography_AddFontFeature(typography, feature);
1947 ok(hr == S_OK, "got 0x%08x\n", hr);
1949 count = IDWriteTypography_GetFontFeatureCount(typography);
1950 ok(count == 3, "got %u\n", count);
1952 memset(&feature, 0xcc, sizeof(feature));
1953 hr = IDWriteTypography_GetFontFeature(typography, 2, &feature);
1954 ok(hr == S_OK, "got 0x%08x\n", hr);
1955 ok(feature.nameTag == DWRITE_FONT_FEATURE_TAG_KERNING, "got tag %x\n", feature.nameTag);
1956 ok(feature.parameter == 0, "got %u\n", feature.parameter);
1958 IDWriteTypography_Release(typography);
1959 IDWriteFactory_Release(factory);
1962 static void test_GetClusterMetrics(void)
1964 static const WCHAR str_white_spaceW[] = {
1965 /* BK - FORM FEED, LINE TABULATION, LINE SEP, PARA SEP */ 0xc, 0xb, 0x2028, 0x2029,
1966 /* ZW - ZERO WIDTH SPACE */ 0x200b,
1967 /* SP - SPACE */ 0x20
1969 static const WCHAR str5W[] = {'a','\r','b','\n','c','\n','\r','d','\r','\n','e',0xb,'f',0xc,
1970 'g',0x0085,'h',0x2028,'i',0x2029,0xad,0xa,0};
1971 static const WCHAR str3W[] = {0x2066,')',')',0x661,'(',0x627,')',0};
1972 static const WCHAR str2W[] = {0x202a,0x202c,'a',0};
1973 static const WCHAR strW[] = {'a','b','c','d',0};
1974 static const WCHAR str4W[] = {'a',' ',0};
1975 static const WCHAR str6W[] = {'a',' ','b',0};
1976 DWRITE_INLINE_OBJECT_METRICS inline_metrics;
1977 DWRITE_CLUSTER_METRICS metrics[22];
1978 DWRITE_TEXT_METRICS text_metrics;
1979 DWRITE_TRIMMING trimming_options;
1980 IDWriteTextLayout1 *layout1;
1981 IDWriteInlineObject *trimm;
1982 IDWriteTextFormat *format;
1983 IDWriteTextLayout *layout;
1984 DWRITE_LINE_METRICS line;
1985 DWRITE_TEXT_RANGE range;
1986 IDWriteFactory *factory;
1987 UINT32 count, i;
1988 FLOAT width;
1989 HRESULT hr;
1991 factory = create_factory();
1993 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
1994 DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format);
1995 ok(hr == S_OK, "got 0x%08x\n", hr);
1997 hr = IDWriteFactory_CreateTextLayout(factory, str3W, 7, format, 1000.0, 1000.0, &layout);
1998 ok(hr == S_OK, "got 0x%08x\n", hr);
1999 hr = IDWriteTextLayout_GetClusterMetrics(layout, NULL, 0, &count);
2000 ok(hr == E_NOT_SUFFICIENT_BUFFER, "got 0x%08x\n", hr);
2001 ok(count == 7, "got %u\n", count);
2002 IDWriteTextLayout_Release(layout);
2004 hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 1000.0, 1000.0, &layout);
2005 ok(hr == S_OK, "got 0x%08x\n", hr);
2007 count = 0;
2008 hr = IDWriteTextLayout_GetClusterMetrics(layout, NULL, 0, &count);
2009 ok(hr == E_NOT_SUFFICIENT_BUFFER, "got 0x%08x\n", hr);
2010 ok(count == 4, "got %u\n", count);
2012 /* check every cluster width */
2013 count = 0;
2014 hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, ARRAY_SIZE(metrics), &count);
2015 ok(hr == S_OK, "got 0x%08x\n", hr);
2016 ok(count == 4, "got %u\n", count);
2017 for (i = 0; i < count; i++) {
2018 ok(metrics[i].width > 0.0, "%u: got width %.2f\n", i, metrics[i].width);
2019 ok(metrics[i].length == 1, "%u: got length %u\n", i, metrics[i].length);
2022 /* apply spacing and check widths again */
2023 if (IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextLayout1, (void**)&layout1) == S_OK) {
2024 DWRITE_CLUSTER_METRICS metrics2[4];
2025 FLOAT leading, trailing, min_advance;
2026 DWRITE_TEXT_RANGE r;
2028 leading = trailing = min_advance = 2.0;
2029 hr = IDWriteTextLayout1_GetCharacterSpacing(layout1, 500, &leading, &trailing,
2030 &min_advance, &r);
2031 ok(hr == S_OK, "got 0x%08x\n", hr);
2032 ok(leading == 0.0 && trailing == 0.0 && min_advance == 0.0,
2033 "got %.2f, %.2f, %.2f\n", leading, trailing, min_advance);
2034 ok(r.startPosition == 0 && r.length == ~0u, "got %u, %u\n", r.startPosition, r.length);
2036 leading = trailing = min_advance = 2.0;
2037 hr = IDWriteTextLayout1_GetCharacterSpacing(layout1, 0, &leading, &trailing,
2038 &min_advance, NULL);
2039 ok(hr == S_OK, "got 0x%08x\n", hr);
2040 ok(leading == 0.0 && trailing == 0.0 && min_advance == 0.0,
2041 "got %.2f, %.2f, %.2f\n", leading, trailing, min_advance);
2043 r.startPosition = 0;
2044 r.length = 4;
2045 hr = IDWriteTextLayout1_SetCharacterSpacing(layout1, 10.0, 15.0, 0.0, r);
2046 ok(hr == S_OK, "got 0x%08x\n", hr);
2048 count = 0;
2049 hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics2, ARRAY_SIZE(metrics2), &count);
2050 ok(hr == S_OK, "got 0x%08x\n", hr);
2051 ok(count == 4, "got %u\n", count);
2052 for (i = 0; i < count; i++) {
2053 todo_wine
2054 ok(metrics2[i].width > metrics[i].width, "%u: got width %.2f, was %.2f\n", i, metrics2[i].width,
2055 metrics[i].width);
2056 ok(metrics2[i].length == 1, "%u: got length %u\n", i, metrics2[i].length);
2059 /* back to defaults */
2060 r.startPosition = 0;
2061 r.length = 4;
2062 hr = IDWriteTextLayout1_SetCharacterSpacing(layout1, 0.0, 0.0, 0.0, r);
2063 ok(hr == S_OK, "got 0x%08x\n", hr);
2065 /* negative advance limit */
2066 r.startPosition = 0;
2067 r.length = 4;
2068 hr = IDWriteTextLayout1_SetCharacterSpacing(layout1, 0.0, 0.0, -10.0, r);
2069 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
2071 IDWriteTextLayout1_Release(layout1);
2073 else
2074 win_skip("IDWriteTextLayout1 is not supported, cluster spacing test skipped.\n");
2076 hr = IDWriteFactory_CreateEllipsisTrimmingSign(factory, format, &trimm);
2077 ok(hr == S_OK, "got 0x%08x\n", hr);
2079 range.startPosition = 0;
2080 range.length = 2;
2081 hr = IDWriteTextLayout_SetInlineObject(layout, trimm, range);
2082 ok(hr == S_OK, "got 0x%08x\n", hr);
2084 /* inline object takes a separate cluster, replaced codepoints number doesn't matter */
2085 count = 0;
2086 hr = IDWriteTextLayout_GetClusterMetrics(layout, NULL, 0, &count);
2087 ok(hr == E_NOT_SUFFICIENT_BUFFER, "got 0x%08x\n", hr);
2088 ok(count == 3, "got %u\n", count);
2090 count = 0;
2091 memset(&metrics, 0, sizeof(metrics));
2092 hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 1, &count);
2093 ok(hr == E_NOT_SUFFICIENT_BUFFER, "got 0x%08x\n", hr);
2094 ok(count == 3, "got %u\n", count);
2095 ok(metrics[0].length == 2, "got %u\n", metrics[0].length);
2097 hr = IDWriteInlineObject_GetMetrics(trimm, &inline_metrics);
2098 ok(hr == S_OK, "got 0x%08x\n", hr);
2099 ok(inline_metrics.width > 0.0 && inline_metrics.width == metrics[0].width, "got %.2f, expected %.2f\n",
2100 inline_metrics.width, metrics[0].width);
2102 IDWriteTextLayout_Release(layout);
2104 /* text with non-visual control codes */
2105 hr = IDWriteFactory_CreateTextLayout(factory, str2W, 3, format, 1000.0, 1000.0, &layout);
2106 ok(hr == S_OK, "got 0x%08x\n", hr);
2108 /* bidi control codes take a separate cluster */
2109 count = 0;
2110 memset(metrics, 0, sizeof(metrics));
2111 hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 3, &count);
2112 ok(hr == S_OK, "got 0x%08x\n", hr);
2113 ok(count == 3, "got %u\n", count);
2115 ok(metrics[0].width == 0.0, "got %.2f\n", metrics[0].width);
2116 ok(metrics[0].length == 1, "got %d\n", metrics[0].length);
2117 ok(metrics[0].canWrapLineAfter == 0, "got %d\n", metrics[0].canWrapLineAfter);
2118 ok(metrics[0].isWhitespace == 0, "got %d\n", metrics[0].isWhitespace);
2119 ok(metrics[0].isNewline == 0, "got %d\n", metrics[0].isNewline);
2120 ok(metrics[0].isSoftHyphen == 0, "got %d\n", metrics[0].isSoftHyphen);
2121 ok(metrics[0].isRightToLeft == 0, "got %d\n", metrics[0].isRightToLeft);
2123 ok(metrics[1].width == 0.0, "got %.2f\n", metrics[1].width);
2124 ok(metrics[1].length == 1, "got %d\n", metrics[1].length);
2125 ok(metrics[1].canWrapLineAfter == 0, "got %d\n", metrics[1].canWrapLineAfter);
2126 ok(metrics[1].isWhitespace == 0, "got %d\n", metrics[1].isWhitespace);
2127 ok(metrics[1].isNewline == 0, "got %d\n", metrics[1].isNewline);
2128 ok(metrics[1].isSoftHyphen == 0, "got %d\n", metrics[1].isSoftHyphen);
2129 ok(metrics[1].isRightToLeft == 0, "got %d\n", metrics[1].isRightToLeft);
2131 ok(metrics[2].width > 0.0, "got %.2f\n", metrics[2].width);
2132 ok(metrics[2].length == 1, "got %d\n", metrics[2].length);
2133 ok(metrics[2].canWrapLineAfter == 1, "got %d\n", metrics[2].canWrapLineAfter);
2134 ok(metrics[2].isWhitespace == 0, "got %d\n", metrics[2].isWhitespace);
2135 ok(metrics[2].isNewline == 0, "got %d\n", metrics[2].isNewline);
2136 ok(metrics[2].isSoftHyphen == 0, "got %d\n", metrics[2].isSoftHyphen);
2137 ok(metrics[2].isRightToLeft == 0, "got %d\n", metrics[2].isRightToLeft);
2139 IDWriteTextLayout_Release(layout);
2141 /* single inline object that fails to report its metrics */
2142 hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 1000.0, 1000.0, &layout);
2143 ok(hr == S_OK, "got 0x%08x\n", hr);
2145 range.startPosition = 0;
2146 range.length = 4;
2147 hr = IDWriteTextLayout_SetInlineObject(layout, &testinlineobj, range);
2148 ok(hr == S_OK, "got 0x%08x\n", hr);
2150 count = 0;
2151 memset(metrics, 0, sizeof(metrics));
2152 hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 3, &count);
2153 ok(hr == S_OK, "got 0x%08x\n", hr);
2154 ok(count == 1, "got %u\n", count);
2156 /* object sets a width to 123.0, but returns failure from GetMetrics() */
2157 ok(metrics[0].width == 0.0, "got %.2f\n", metrics[0].width);
2158 ok(metrics[0].length == 4, "got %d\n", metrics[0].length);
2159 ok(metrics[0].canWrapLineAfter == 1, "got %d\n", metrics[0].canWrapLineAfter);
2160 ok(metrics[0].isWhitespace == 0, "got %d\n", metrics[0].isWhitespace);
2161 ok(metrics[0].isNewline == 0, "got %d\n", metrics[0].isNewline);
2162 ok(metrics[0].isSoftHyphen == 0, "got %d\n", metrics[0].isSoftHyphen);
2163 ok(metrics[0].isRightToLeft == 0, "got %d\n", metrics[0].isRightToLeft);
2165 /* now set two inline object for [0,1] and [2,3], both fail to report break conditions */
2166 range.startPosition = 2;
2167 range.length = 2;
2168 hr = IDWriteTextLayout_SetInlineObject(layout, &testinlineobj2, range);
2169 ok(hr == S_OK, "got 0x%08x\n", hr);
2171 count = 0;
2172 memset(metrics, 0, sizeof(metrics));
2173 hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 3, &count);
2174 ok(hr == S_OK, "got 0x%08x\n", hr);
2175 ok(count == 2, "got %u\n", count);
2177 ok(metrics[0].width == 0.0, "got %.2f\n", metrics[0].width);
2178 ok(metrics[0].length == 2, "got %d\n", metrics[0].length);
2179 ok(metrics[0].canWrapLineAfter == 0, "got %d\n", metrics[0].canWrapLineAfter);
2180 ok(metrics[0].isWhitespace == 0, "got %d\n", metrics[0].isWhitespace);
2181 ok(metrics[0].isNewline == 0, "got %d\n", metrics[0].isNewline);
2182 ok(metrics[0].isSoftHyphen == 0, "got %d\n", metrics[0].isSoftHyphen);
2183 ok(metrics[0].isRightToLeft == 0, "got %d\n", metrics[0].isRightToLeft);
2185 ok(metrics[1].width == 0.0, "got %.2f\n", metrics[1].width);
2186 ok(metrics[1].length == 2, "got %d\n", metrics[1].length);
2187 ok(metrics[1].canWrapLineAfter == 1, "got %d\n", metrics[1].canWrapLineAfter);
2188 ok(metrics[1].isWhitespace == 0, "got %d\n", metrics[1].isWhitespace);
2189 ok(metrics[1].isNewline == 0, "got %d\n", metrics[1].isNewline);
2190 ok(metrics[1].isSoftHyphen == 0, "got %d\n", metrics[1].isSoftHyphen);
2191 ok(metrics[1].isRightToLeft == 0, "got %d\n", metrics[1].isRightToLeft);
2193 IDWriteTextLayout_Release(layout);
2195 /* zero length string */
2196 hr = IDWriteFactory_CreateTextLayout(factory, strW, 0, format, 1000.0, 1000.0, &layout);
2197 ok(hr == S_OK, "got 0x%08x\n", hr);
2199 count = 1;
2200 memset(metrics, 0, sizeof(metrics));
2201 hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 3, &count);
2202 ok(hr == S_OK, "got 0x%08x\n", hr);
2203 ok(count == 0, "got %u\n", count);
2204 IDWriteTextLayout_Release(layout);
2206 /* whitespace */
2207 hr = IDWriteFactory_CreateTextLayout(factory, str4W, 2, format, 1000.0, 1000.0, &layout);
2208 ok(hr == S_OK, "got 0x%08x\n", hr);
2210 count = 0;
2211 memset(metrics, 0, sizeof(metrics));
2212 hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 2, &count);
2213 ok(hr == S_OK, "got 0x%08x\n", hr);
2214 ok(count == 2, "got %u\n", count);
2215 ok(metrics[0].isWhitespace == 0, "got %d\n", metrics[0].isWhitespace);
2216 ok(metrics[0].canWrapLineAfter == 0, "got %d\n", metrics[0].canWrapLineAfter);
2217 ok(metrics[1].isWhitespace == 1, "got %d\n", metrics[1].isWhitespace);
2218 ok(metrics[1].canWrapLineAfter == 1, "got %d\n", metrics[1].canWrapLineAfter);
2219 IDWriteTextLayout_Release(layout);
2221 /* layout is fully covered by inline object with after condition DWRITE_BREAK_CONDITION_MAY_NOT_BREAK */
2222 hr = IDWriteFactory_CreateTextLayout(factory, str4W, 2, format, 1000.0, 1000.0, &layout);
2223 ok(hr == S_OK, "got 0x%08x\n", hr);
2225 range.startPosition = 0;
2226 range.length = ~0u;
2227 hr = IDWriteTextLayout_SetInlineObject(layout, &testinlineobj3, range);
2228 ok(hr == S_OK, "got 0x%08x\n", hr);
2230 count = 0;
2231 memset(metrics, 0, sizeof(metrics));
2232 hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 2, &count);
2233 ok(hr == S_OK, "got 0x%08x\n", hr);
2234 ok(count == 1, "got %u\n", count);
2235 ok(metrics[0].canWrapLineAfter == 1, "got %d\n", metrics[0].canWrapLineAfter);
2237 IDWriteTextLayout_Release(layout);
2239 /* compare natural cluster width with gdi layout */
2240 hr = IDWriteFactory_CreateTextLayout(factory, str4W, 1, format, 100.0, 100.0, &layout);
2241 ok(hr == S_OK, "got 0x%08x\n", hr);
2243 count = 0;
2244 memset(metrics, 0, sizeof(metrics));
2245 hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 1, &count);
2246 ok(hr == S_OK, "got 0x%08x\n", hr);
2247 ok(count == 1, "got %u\n", count);
2248 ok(metrics[0].width != floorf(metrics[0].width), "got %f\n", metrics[0].width);
2250 IDWriteTextLayout_Release(layout);
2252 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, str4W, 1, format, 100.0, 100.0, 1.0, NULL, FALSE, &layout);
2253 ok(hr == S_OK, "got 0x%08x\n", hr);
2255 count = 0;
2256 memset(metrics, 0, sizeof(metrics));
2257 hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 1, &count);
2258 ok(hr == S_OK, "got 0x%08x\n", hr);
2259 ok(count == 1, "got %u\n", count);
2260 ok(metrics[0].width == floorf(metrics[0].width), "got %f\n", metrics[0].width);
2262 IDWriteTextLayout_Release(layout);
2264 /* isNewline tests */
2265 hr = IDWriteFactory_CreateTextLayout(factory, str5W, lstrlenW(str5W), format, 100.0f, 200.0f, &layout);
2266 ok(hr == S_OK, "got 0x%08x\n", hr);
2268 count = 0;
2269 memset(metrics, 0, sizeof(metrics));
2270 hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, ARRAY_SIZE(metrics), &count);
2271 ok(hr == S_OK, "got 0x%08x\n", hr);
2272 ok(count == 22, "got %u\n", count);
2274 ok(metrics[1].isNewline == 1, "got %d\n", metrics[1].isNewline);
2275 ok(metrics[3].isNewline == 1, "got %d\n", metrics[3].isNewline);
2276 ok(metrics[5].isNewline == 1, "got %d\n", metrics[5].isNewline);
2277 ok(metrics[6].isNewline == 1, "got %d\n", metrics[6].isNewline);
2278 ok(metrics[9].isNewline == 1, "got %d\n", metrics[9].isNewline);
2279 ok(metrics[11].isNewline == 1, "got %d\n", metrics[11].isNewline);
2280 ok(metrics[13].isNewline == 1, "got %d\n", metrics[13].isNewline);
2281 ok(metrics[15].isNewline == 1, "got %d\n", metrics[15].isNewline);
2282 ok(metrics[17].isNewline == 1, "got %d\n", metrics[17].isNewline);
2283 ok(metrics[19].isNewline == 1, "got %d\n", metrics[19].isNewline);
2284 ok(metrics[21].isNewline == 1, "got %d\n", metrics[21].isNewline);
2286 ok(metrics[0].isNewline == 0, "got %d\n", metrics[0].isNewline);
2287 ok(metrics[2].isNewline == 0, "got %d\n", metrics[2].isNewline);
2288 ok(metrics[4].isNewline == 0, "got %d\n", metrics[4].isNewline);
2289 ok(metrics[7].isNewline == 0, "got %d\n", metrics[7].isNewline);
2290 ok(metrics[8].isNewline == 0, "got %d\n", metrics[8].isNewline);
2291 ok(metrics[10].isNewline == 0, "got %d\n", metrics[10].isNewline);
2292 ok(metrics[12].isNewline == 0, "got %d\n", metrics[12].isNewline);
2293 ok(metrics[14].isNewline == 0, "got %d\n", metrics[14].isNewline);
2294 ok(metrics[16].isNewline == 0, "got %d\n", metrics[16].isNewline);
2295 ok(metrics[18].isNewline == 0, "got %d\n", metrics[18].isNewline);
2296 ok(metrics[20].isNewline == 0, "got %d\n", metrics[20].isNewline);
2298 for (i = 0; i < count; i++) {
2299 ok(metrics[i].length == 1, "%d: got %d\n", i, metrics[i].length);
2300 ok(metrics[i].isSoftHyphen == (i == count - 2), "%d: got %d\n", i, metrics[i].isSoftHyphen);
2301 if (metrics[i].isSoftHyphen)
2302 ok(!metrics[i].isWhitespace, "%u: got %d\n", i, metrics[i].isWhitespace);
2303 if (metrics[i].isNewline) {
2304 ok(metrics[i].width == 0.0f, "%u: got width %f\n", i, metrics[i].width);
2305 ok(metrics[i].isWhitespace == 1, "%u: got %d\n", i, metrics[i].isWhitespace);
2306 ok(metrics[i].canWrapLineAfter == 1, "%u: got %d\n", i, metrics[i].canWrapLineAfter);
2310 IDWriteTextLayout_Release(layout);
2312 /* Test whitespace resolution from linebreaking classes BK, ZW, and SP */
2313 hr = IDWriteFactory_CreateTextLayout(factory, str_white_spaceW, ARRAY_SIZE(str_white_spaceW), format,
2314 100.0f, 200.0f, &layout);
2315 ok(hr == S_OK, "got 0x%08x\n", hr);
2317 count = 0;
2318 memset(metrics, 0, sizeof(metrics));
2319 hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 20, &count);
2320 ok(hr == S_OK, "got 0x%08x\n", hr);
2321 ok(count == 6, "got %u\n", count);
2323 ok(metrics[0].isWhitespace == 1, "got %d\n", metrics[0].isWhitespace);
2324 ok(metrics[1].isWhitespace == 1, "got %d\n", metrics[1].isWhitespace);
2325 ok(metrics[2].isWhitespace == 1, "got %d\n", metrics[2].isWhitespace);
2326 ok(metrics[3].isWhitespace == 1, "got %d\n", metrics[3].isWhitespace);
2327 ok(metrics[4].isWhitespace == 0, "got %d\n", metrics[4].isWhitespace);
2328 ok(metrics[5].isWhitespace == 1, "got %d\n", metrics[5].isWhitespace);
2330 IDWriteTextLayout_Release(layout);
2332 /* trigger line trimming */
2333 hr = IDWriteFactory_CreateTextLayout(factory, strW, lstrlenW(strW), format, 100.0f, 200.0f, &layout);
2334 ok(hr == S_OK, "got 0x%08x\n", hr);
2336 count = 0;
2337 memset(metrics, 0, sizeof(metrics));
2338 hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 4, &count);
2339 ok(hr == S_OK, "got 0x%08x\n", hr);
2340 ok(count == 4, "got %u\n", count);
2342 hr = IDWriteTextLayout_GetMetrics(layout, &text_metrics);
2343 ok(hr == S_OK, "got 0x%08x\n", hr);
2345 width = metrics[0].width + inline_metrics.width;
2346 ok(width < text_metrics.width, "unexpected trimming sign width\n");
2348 /* enable trimming, reduce layout width so only first cluster and trimming sign fits */
2349 trimming_options.granularity = DWRITE_TRIMMING_GRANULARITY_CHARACTER;
2350 trimming_options.delimiter = 0;
2351 trimming_options.delimiterCount = 0;
2352 hr = IDWriteTextLayout_SetTrimming(layout, &trimming_options, trimm);
2353 ok(hr == S_OK, "got 0x%08x\n", hr);
2355 hr = IDWriteTextLayout_SetMaxWidth(layout, width);
2356 ok(hr == S_OK, "got 0x%08x\n", hr);
2358 count = 0;
2359 memset(metrics, 0, sizeof(metrics));
2360 hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 4, &count);
2361 ok(hr == S_OK, "got 0x%08x\n", hr);
2362 ok(count == 4, "got %u\n", count);
2364 hr = IDWriteTextLayout_GetLineMetrics(layout, &line, 1, &count);
2365 ok(hr == S_OK, "got 0x%08x\n", hr);
2366 ok(count == 1, "got %u\n", count);
2367 ok(line.length == 4, "got %u\n", line.length);
2368 ok(line.isTrimmed, "got %d\n", line.isTrimmed);
2370 IDWriteTextLayout_Release(layout);
2372 /* NO_WRAP, check cluster wrapping attribute. */
2373 hr = IDWriteTextFormat_SetWordWrapping(format, DWRITE_WORD_WRAPPING_NO_WRAP);
2374 ok(hr == S_OK, "got 0x%08x\n", hr);
2376 hr = IDWriteFactory_CreateTextLayout(factory, str6W, lstrlenW(str6W), format, 1000.0f, 200.0f, &layout);
2377 ok(hr == S_OK, "got 0x%08x\n", hr);
2379 count = 0;
2380 memset(metrics, 0, sizeof(metrics));
2381 hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 3, &count);
2382 ok(hr == S_OK, "got 0x%08x\n", hr);
2383 ok(count == 3, "got %u\n", count);
2385 ok(metrics[0].canWrapLineAfter == 0, "got %d\n", metrics[0].canWrapLineAfter);
2386 ok(metrics[1].canWrapLineAfter == 1, "got %d\n", metrics[1].canWrapLineAfter);
2387 ok(metrics[2].canWrapLineAfter == 1, "got %d\n", metrics[2].canWrapLineAfter);
2389 IDWriteTextLayout_Release(layout);
2391 IDWriteInlineObject_Release(trimm);
2392 IDWriteTextFormat_Release(format);
2393 IDWriteFactory_Release(factory);
2396 static void test_SetLocaleName(void)
2398 static const WCHAR eNuSW[] = {'e','N','-','u','S',0};
2399 static const WCHAR strW[] = {'a','b','c','d',0};
2400 WCHAR buffW[LOCALE_NAME_MAX_LENGTH + ARRAY_SIZE(strW)];
2401 IDWriteTextFormat *format, *format2;
2402 IDWriteTextLayout *layout;
2403 DWRITE_TEXT_RANGE range;
2404 IDWriteFactory *factory;
2405 HRESULT hr;
2407 factory = create_factory();
2409 /* create format with mixed case locale name, get it back */
2410 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
2411 DWRITE_FONT_STRETCH_NORMAL, 10.0, eNuSW, &format);
2412 ok(hr == S_OK, "got 0x%08x\n", hr);
2414 hr = IDWriteTextFormat_GetLocaleName(format, buffW, ARRAY_SIZE(buffW));
2415 ok(hr == S_OK, "got 0x%08x\n", hr);
2416 ok(!lstrcmpW(buffW, enusW), "got %s\n", wine_dbgstr_w(buffW));
2418 hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 1000.0, 1000.0, &layout);
2419 ok(hr == S_OK, "got 0x%08x\n", hr);
2421 hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextFormat, (void**)&format2);
2422 ok(hr == S_OK, "got 0x%08x\n", hr);
2424 hr = IDWriteTextFormat_GetLocaleName(format2, buffW, ARRAY_SIZE(buffW));
2425 ok(hr == S_OK, "got 0x%08x\n", hr);
2426 ok(!lstrcmpW(buffW, enusW), "got %s\n", wine_dbgstr_w(buffW));
2428 hr = IDWriteTextLayout_GetLocaleName(layout, 0, buffW, ARRAY_SIZE(buffW), NULL);
2429 ok(hr == S_OK, "got 0x%08x\n", hr);
2430 ok(!lstrcmpW(buffW, enusW), "got %s\n", wine_dbgstr_w(buffW));
2432 IDWriteTextFormat_Release(format2);
2433 IDWriteTextLayout_Release(layout);
2434 IDWriteTextFormat_Release(format);
2436 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
2437 DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format);
2438 ok(hr == S_OK, "got 0x%08x\n", hr);
2440 hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 1000.0, 1000.0, &layout);
2441 ok(hr == S_OK, "got 0x%08x\n", hr);
2443 range.startPosition = 0;
2444 range.length = 1;
2445 hr = IDWriteTextLayout_SetLocaleName(layout, enusW, range);
2446 ok(hr == S_OK, "got 0x%08x\n", hr);
2448 hr = IDWriteTextLayout_SetLocaleName(layout, NULL, range);
2449 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
2451 /* invalid locale name is allowed */
2452 hr = IDWriteTextLayout_SetLocaleName(layout, strW, range);
2453 ok(hr == S_OK, "got 0x%08x\n", hr);
2455 hr = IDWriteTextLayout_GetLocaleName(layout, 0, NULL, 0, NULL);
2456 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
2458 if (0) /* crashes on native */
2459 hr = IDWriteTextLayout_GetLocaleName(layout, 0, NULL, 1, NULL);
2461 buffW[0] = 0;
2462 range.length = 0;
2463 hr = IDWriteTextLayout_GetLocaleName(layout, 0, buffW, ARRAY_SIZE(buffW), &range);
2464 ok(hr == S_OK, "got 0x%08x\n", hr);
2465 ok(!lstrcmpW(buffW, strW), "got %s\n", wine_dbgstr_w(buffW));
2466 ok(range.startPosition == 0 && range.length == 1, "got %u,%u\n", range.startPosition, range.length);
2468 /* get with a shorter buffer */
2469 buffW[0] = 0xa;
2470 hr = IDWriteTextLayout_GetLocaleName(layout, 0, buffW, 1, NULL);
2471 ok(hr == E_NOT_SUFFICIENT_BUFFER, "got 0x%08x\n", hr);
2472 ok(buffW[0] == 0, "got %x\n", buffW[0]);
2474 /* name is too long */
2475 lstrcpyW(buffW, strW);
2476 while (lstrlenW(buffW) <= LOCALE_NAME_MAX_LENGTH)
2477 lstrcatW(buffW, strW);
2479 range.startPosition = 0;
2480 range.length = 1;
2481 hr = IDWriteTextLayout_SetLocaleName(layout, buffW, range);
2482 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
2484 buffW[0] = 0;
2485 hr = IDWriteTextLayout_GetLocaleName(layout, 0, buffW, ARRAY_SIZE(buffW), NULL);
2486 ok(hr == S_OK, "got 0x%08x\n", hr);
2487 ok(!lstrcmpW(buffW, strW), "got %s\n", wine_dbgstr_w(buffW));
2489 /* set initial locale name for whole text, except with a different casing */
2490 range.startPosition = 0;
2491 range.length = 4;
2492 hr = IDWriteTextLayout_SetLocaleName(layout, eNuSW, range);
2493 ok(hr == S_OK, "got 0x%08x\n", hr);
2495 buffW[0] = 0;
2496 range.length = 0;
2497 hr = IDWriteTextLayout_GetLocaleName(layout, 0, buffW, ARRAY_SIZE(buffW), &range);
2498 ok(hr == S_OK, "got 0x%08x\n", hr);
2499 ok(!lstrcmpW(buffW, enusW), "got %s\n", wine_dbgstr_w(buffW));
2500 ok((range.startPosition == 0 && range.length == ~0u) ||
2501 broken(range.startPosition == 0 && range.length == 4) /* vista/win7 */, "got %u,%u\n", range.startPosition, range.length);
2503 /* check what's returned for positions after the text */
2504 buffW[0] = 0;
2505 range.length = 0;
2506 hr = IDWriteTextLayout_GetLocaleName(layout, 100, buffW, ARRAY_SIZE(buffW), &range);
2507 ok(hr == S_OK, "got 0x%08x\n", hr);
2508 ok(!lstrcmpW(buffW, enusW), "got %s\n", wine_dbgstr_w(buffW));
2509 ok((range.startPosition == 0 && range.length == ~0u) ||
2510 broken(range.startPosition == 4 && range.length == ~0u-4) /* vista/win7 */, "got %u,%u\n", range.startPosition, range.length);
2512 IDWriteTextLayout_Release(layout);
2513 IDWriteTextFormat_Release(format);
2514 IDWriteFactory_Release(factory);
2517 static void test_SetPairKerning(void)
2519 static const WCHAR strW[] = {'a','e',0x0300,'d',0}; /* accent grave */
2520 DWRITE_CLUSTER_METRICS clusters[4];
2521 IDWriteTextLayout1 *layout1;
2522 IDWriteTextFormat *format;
2523 IDWriteTextLayout *layout;
2524 DWRITE_TEXT_RANGE range;
2525 IDWriteFactory *factory;
2526 BOOL kerning;
2527 UINT32 count;
2528 HRESULT hr;
2530 factory = create_factory();
2532 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
2533 DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format);
2534 ok(hr == S_OK, "got 0x%08x\n", hr);
2536 hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 1000.0, 1000.0, &layout);
2537 ok(hr == S_OK, "got 0x%08x\n", hr);
2538 IDWriteTextFormat_Release(format);
2540 hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextLayout1, (void**)&layout1);
2541 IDWriteTextLayout_Release(layout);
2543 if (hr != S_OK) {
2544 win_skip("SetPairKerning() is not supported.\n");
2545 IDWriteFactory_Release(factory);
2546 return;
2549 if (0) { /* crashes on native */
2550 hr = IDWriteTextLayout1_GetPairKerning(layout1, 0, NULL, NULL);
2551 hr = IDWriteTextLayout1_GetPairKerning(layout1, 0, NULL, &range);
2554 hr = IDWriteTextLayout1_GetPairKerning(layout1, 0, &kerning, NULL);
2555 ok(hr == S_OK, "got 0x%08x\n", hr);
2557 range.startPosition = 0;
2558 range.length = 0;
2559 kerning = TRUE;
2560 hr = IDWriteTextLayout1_GetPairKerning(layout1, 0, &kerning, &range);
2561 ok(hr == S_OK, "got 0x%08x\n", hr);
2562 ok(!kerning, "got %d\n", kerning);
2563 ok(range.length == ~0u, "got %u\n", range.length);
2565 count = 0;
2566 hr = IDWriteTextLayout1_GetClusterMetrics(layout1, clusters, 4, &count);
2567 ok(hr == S_OK, "got 0x%08x\n", hr);
2568 todo_wine
2569 ok(count == 3, "got %u\n", count);
2570 if (count == 3) {
2571 ok(clusters[0].length == 1, "got %u\n", clusters[0].length);
2572 ok(clusters[1].length == 2, "got %u\n", clusters[1].length);
2573 ok(clusters[2].length == 1, "got %u\n", clusters[2].length);
2575 /* pair kerning flag participates in itemization - combining characters
2576 breaks */
2577 range.startPosition = 0;
2578 range.length = 2;
2579 hr = IDWriteTextLayout1_SetPairKerning(layout1, 2, range);
2580 ok(hr == S_OK, "got 0x%08x\n", hr);
2582 kerning = FALSE;
2583 hr = IDWriteTextLayout1_GetPairKerning(layout1, 0, &kerning, &range);
2584 ok(hr == S_OK, "got 0x%08x\n", hr);
2585 ok(kerning == TRUE, "got %d\n", kerning);
2587 count = 0;
2588 hr = IDWriteTextLayout1_GetClusterMetrics(layout1, clusters, 4, &count);
2589 ok(hr == S_OK, "got 0x%08x\n", hr);
2590 ok(count == 4, "got %u\n", count);
2591 ok(clusters[0].length == 1, "got %u\n", clusters[0].length);
2592 ok(clusters[1].length == 1, "got %u\n", clusters[1].length);
2593 ok(clusters[2].length == 1, "got %u\n", clusters[2].length);
2594 ok(clusters[3].length == 1, "got %u\n", clusters[3].length);
2596 IDWriteTextLayout1_Release(layout1);
2597 IDWriteFactory_Release(factory);
2600 static void test_SetVerticalGlyphOrientation(void)
2602 static const WCHAR strW[] = {'a','b','c','d',0};
2603 DWRITE_VERTICAL_GLYPH_ORIENTATION orientation;
2604 IDWriteTextLayout2 *layout2;
2605 IDWriteTextFormat *format;
2606 IDWriteTextLayout *layout;
2607 IDWriteFactory *factory;
2608 HRESULT hr;
2610 factory = create_factory();
2612 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
2613 DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format);
2614 ok(hr == S_OK, "got 0x%08x\n", hr);
2616 hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 1000.0, 1000.0, &layout);
2617 ok(hr == S_OK, "got 0x%08x\n", hr);
2618 IDWriteTextFormat_Release(format);
2620 hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextLayout2, (void**)&layout2);
2621 IDWriteTextLayout_Release(layout);
2623 if (hr != S_OK) {
2624 win_skip("SetVerticalGlyphOrientation() is not supported.\n");
2625 IDWriteFactory_Release(factory);
2626 return;
2629 orientation = IDWriteTextLayout2_GetVerticalGlyphOrientation(layout2);
2630 ok(orientation == DWRITE_VERTICAL_GLYPH_ORIENTATION_DEFAULT, "got %d\n", orientation);
2632 hr = IDWriteTextLayout2_SetVerticalGlyphOrientation(layout2, DWRITE_VERTICAL_GLYPH_ORIENTATION_STACKED+1);
2633 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
2635 IDWriteTextLayout2_Release(layout2);
2636 IDWriteFactory_Release(factory);
2639 static void test_fallback(void)
2641 static const WCHAR strW[] = {'a','b','c','d',0};
2642 IDWriteFontFallback *fallback, *fallback2;
2643 IDWriteTextLayout2 *layout2;
2644 IDWriteTextFormat1 *format1;
2645 IDWriteTextFormat *format;
2646 IDWriteTextLayout *layout;
2647 IDWriteFactory2 *factory2;
2648 IDWriteFactory *factory;
2649 HRESULT hr;
2651 factory = create_factory();
2653 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
2654 DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format);
2655 ok(hr == S_OK, "got 0x%08x\n", hr);
2657 hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 1000.0, 1000.0, &layout);
2658 ok(hr == S_OK, "got 0x%08x\n", hr);
2659 IDWriteTextFormat_Release(format);
2661 hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextLayout2, (void**)&layout2);
2662 IDWriteTextLayout_Release(layout);
2664 if (hr != S_OK) {
2665 win_skip("GetFontFallback() is not supported.\n");
2666 IDWriteFactory_Release(factory);
2667 return;
2670 if (0) /* crashes on native */
2671 hr = IDWriteTextLayout2_GetFontFallback(layout2, NULL);
2673 fallback = (void*)0xdeadbeef;
2674 hr = IDWriteTextLayout2_GetFontFallback(layout2, &fallback);
2675 ok(hr == S_OK, "got 0x%08x\n", hr);
2676 ok(fallback == NULL, "got %p\n", fallback);
2678 hr = IDWriteTextLayout2_QueryInterface(layout2, &IID_IDWriteTextFormat1, (void**)&format1);
2679 ok(hr == S_OK, "got 0x%08x\n", hr);
2681 fallback = (void*)0xdeadbeef;
2682 hr = IDWriteTextFormat1_GetFontFallback(format1, &fallback);
2683 ok(hr == S_OK, "got 0x%08x\n", hr);
2684 ok(fallback == NULL, "got %p\n", fallback);
2686 hr = IDWriteFactory_QueryInterface(factory, &IID_IDWriteFactory2, (void**)&factory2);
2687 ok(hr == S_OK, "got 0x%08x\n", hr);
2689 fallback = NULL;
2690 hr = IDWriteFactory2_GetSystemFontFallback(factory2, &fallback);
2691 ok(hr == S_OK, "got 0x%08x\n", hr);
2692 ok(fallback != NULL, "got %p\n", fallback);
2694 hr = IDWriteTextFormat1_SetFontFallback(format1, fallback);
2695 ok(hr == S_OK, "got 0x%08x\n", hr);
2697 fallback2 = (void*)0xdeadbeef;
2698 hr = IDWriteTextLayout2_GetFontFallback(layout2, &fallback2);
2699 ok(hr == S_OK, "got 0x%08x\n", hr);
2700 ok(fallback2 == fallback, "got %p\n", fallback2);
2702 hr = IDWriteTextLayout2_SetFontFallback(layout2, NULL);
2703 ok(hr == S_OK, "got 0x%08x\n", hr);
2705 fallback2 = (void*)0xdeadbeef;
2706 hr = IDWriteTextFormat1_GetFontFallback(format1, &fallback2);
2707 ok(hr == S_OK, "got 0x%08x\n", hr);
2708 ok(fallback2 == NULL, "got %p\n", fallback2);
2710 IDWriteFontFallback_Release(fallback);
2711 IDWriteTextFormat1_Release(format1);
2712 IDWriteTextLayout2_Release(layout2);
2713 IDWriteFactory_Release(factory);
2716 static void test_DetermineMinWidth(void)
2718 struct minwidth_test {
2719 const WCHAR text[10]; /* text to create a layout for */
2720 const WCHAR mintext[10]; /* text that represents sequence of minimal width */
2721 } minwidth_tests[] = {
2722 { {' ','a','b',' ',0}, {'a','b',0} },
2723 { {'a','\n',' ',' ',0}, {'a',0} },
2724 { {'a','\n',' ',' ','b',0}, {'b',0} },
2725 { {'a','b','c','\n',' ',' ','b',0}, {'a','b','c',0} },
2727 static const WCHAR strW[] = {'a','b','c','d',0};
2728 DWRITE_CLUSTER_METRICS metrics[10];
2729 IDWriteTextFormat *format;
2730 IDWriteTextLayout *layout;
2731 IDWriteFactory *factory;
2732 UINT32 count, i, j;
2733 FLOAT minwidth;
2734 HRESULT hr;
2736 factory = create_factory();
2738 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
2739 DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format);
2740 ok(hr == S_OK, "got 0x%08x\n", hr);
2742 hr = IDWriteFactory_CreateTextLayout(factory, strW, lstrlenW(strW), format, 1000.0, 1000.0, &layout);
2743 ok(hr == S_OK, "got 0x%08x\n", hr);
2745 hr = IDWriteTextLayout_DetermineMinWidth(layout, NULL);
2746 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
2747 IDWriteTextLayout_Release(layout);
2749 /* empty string */
2750 hr = IDWriteFactory_CreateTextLayout(factory, strW, 0, format, 100.0f, 100.0f, &layout);
2751 ok(hr == S_OK, "got 0x%08x\n", hr);
2753 minwidth = 1.0f;
2754 hr = IDWriteTextLayout_DetermineMinWidth(layout, &minwidth);
2755 ok(hr == S_OK, "got 0x%08x\n", hr);
2756 ok(minwidth == 0.0f, "got %f\n", minwidth);
2757 IDWriteTextLayout_Release(layout);
2759 for (i = 0; i < ARRAY_SIZE(minwidth_tests); i++) {
2760 FLOAT width = 0.0f;
2762 /* measure expected width */
2763 hr = IDWriteFactory_CreateTextLayout(factory, minwidth_tests[i].mintext, lstrlenW(minwidth_tests[i].mintext), format, 1000.0f, 1000.0f, &layout);
2764 ok(hr == S_OK, "got 0x%08x\n", hr);
2766 hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, ARRAY_SIZE(metrics), &count);
2767 ok(hr == S_OK, "got 0x%08x\n", hr);
2769 for (j = 0; j < count; j++)
2770 width += metrics[j].width;
2772 IDWriteTextLayout_Release(layout);
2774 hr = IDWriteFactory_CreateTextLayout(factory, minwidth_tests[i].text, lstrlenW(minwidth_tests[i].text), format, 1000.0f, 1000.0f, &layout);
2775 ok(hr == S_OK, "got 0x%08x\n", hr);
2777 minwidth = 0.0f;
2778 hr = IDWriteTextLayout_DetermineMinWidth(layout, &minwidth);
2779 ok(hr == S_OK, "got 0x%08x\n", hr);
2780 ok(minwidth == width, "test %u: expected width %f, got %f\n", i, width, minwidth);
2782 IDWriteTextLayout_Release(layout);
2785 IDWriteTextFormat_Release(format);
2786 IDWriteFactory_Release(factory);
2789 static void test_SetFontSize(void)
2791 static const WCHAR strW[] = {'a','b','c','d',0};
2792 IDWriteTextFormat *format;
2793 IDWriteTextLayout *layout;
2794 IDWriteFactory *factory;
2795 DWRITE_TEXT_RANGE r;
2796 FLOAT size;
2797 HRESULT hr;
2799 factory = create_factory();
2801 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
2802 DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format);
2803 ok(hr == S_OK, "got 0x%08x\n", hr);
2805 hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 1000.0, 1000.0, &layout);
2806 ok(hr == S_OK, "got 0x%08x\n", hr);
2808 /* negative/zero size */
2809 r.startPosition = 1;
2810 r.length = 1;
2811 hr = IDWriteTextLayout_SetFontSize(layout, -15.0, r);
2812 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
2814 hr = IDWriteTextLayout_SetFontSize(layout, 0.0, r);
2815 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
2817 r.startPosition = 1;
2818 r.length = 0;
2819 size = 0.0;
2820 hr = IDWriteTextLayout_GetFontSize(layout, 0, &size, &r);
2821 ok(hr == S_OK, "got 0x%08x\n", hr);
2822 ok(r.startPosition == 0 && r.length == ~0u, "got %u, %u\n", r.startPosition, r.length);
2823 ok(size == 10.0, "got %.2f\n", size);
2825 r.startPosition = 1;
2826 r.length = 1;
2827 hr = IDWriteTextLayout_SetFontSize(layout, 15.0, r);
2828 ok(hr == S_OK, "got 0x%08x\n", hr);
2830 /* zero length range */
2831 r.startPosition = 1;
2832 r.length = 0;
2833 hr = IDWriteTextLayout_SetFontSize(layout, 123.0, r);
2834 ok(hr == S_OK, "got 0x%08x\n", hr);
2836 size = 0.0;
2837 hr = IDWriteTextLayout_GetFontSize(layout, 1, &size, &r);
2838 ok(hr == S_OK, "got 0x%08x\n", hr);
2839 ok(size == 15.0, "got %.2f\n", size);
2841 r.startPosition = 0;
2842 r.length = 4;
2843 hr = IDWriteTextLayout_SetFontSize(layout, 15.0, r);
2844 ok(hr == S_OK, "got 0x%08x\n", hr);
2846 size = 0.0;
2847 hr = IDWriteTextLayout_GetFontSize(layout, 1, &size, &r);
2848 ok(hr == S_OK, "got 0x%08x\n", hr);
2849 ok(size == 15.0, "got %.2f\n", size);
2851 size = 0.0;
2852 hr = IDWriteTextLayout_GetFontSize(layout, 0, &size, &r);
2853 ok(hr == S_OK, "got 0x%08x\n", hr);
2854 ok(r.startPosition == 0 && r.length == 4, "got %u, %u\n", r.startPosition, r.length);
2855 ok(size == 15.0, "got %.2f\n", size);
2857 size = 15.0;
2858 r.startPosition = r.length = 0;
2859 hr = IDWriteTextLayout_GetFontSize(layout, 20, &size, &r);
2860 ok(hr == S_OK, "got 0x%08x\n", hr);
2861 ok(r.startPosition == 4 && r.length == ~0u-4, "got %u, %u\n", r.startPosition, r.length);
2862 ok(size == 10.0, "got %.2f\n", size);
2864 r.startPosition = 100;
2865 r.length = 4;
2866 hr = IDWriteTextLayout_SetFontSize(layout, 25.0, r);
2867 ok(hr == S_OK, "got 0x%08x\n", hr);
2869 size = 15.0;
2870 r.startPosition = r.length = 0;
2871 hr = IDWriteTextLayout_GetFontSize(layout, 100, &size, &r);
2872 ok(hr == S_OK, "got 0x%08x\n", hr);
2873 ok(r.startPosition == 100 && r.length == 4, "got %u, %u\n", r.startPosition, r.length);
2874 ok(size == 25.0, "got %.2f\n", size);
2876 IDWriteTextLayout_Release(layout);
2877 IDWriteTextFormat_Release(format);
2878 IDWriteFactory_Release(factory);
2881 static void test_SetFontFamilyName(void)
2883 static const WCHAR taHomaW[] = {'T','a','H','o','m','a',0};
2884 static const WCHAR arialW[] = {'A','r','i','a','l',0};
2885 static const WCHAR strW[] = {'a','b','c','d',0};
2886 IDWriteTextFormat *format;
2887 IDWriteTextLayout *layout;
2888 IDWriteFactory *factory;
2889 DWRITE_TEXT_RANGE r;
2890 WCHAR nameW[50];
2891 HRESULT hr;
2893 factory = create_factory();
2895 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
2896 DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format);
2897 ok(hr == S_OK, "got 0x%08x\n", hr);
2899 hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 1000.0, 1000.0, &layout);
2900 ok(hr == S_OK, "got 0x%08x\n", hr);
2902 /* NULL name */
2903 r.startPosition = 1;
2904 r.length = 1;
2905 hr = IDWriteTextLayout_SetFontFamilyName(layout, NULL, r);
2906 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
2908 r.startPosition = 1;
2909 r.length = 0;
2910 nameW[0] = 0;
2911 hr = IDWriteTextLayout_GetFontFamilyName(layout, 1, nameW, ARRAY_SIZE(nameW), &r);
2912 ok(hr == S_OK, "got 0x%08x\n", hr);
2913 ok(r.startPosition == 0 && r.length == ~0u, "got %u, %u\n", r.startPosition, r.length);
2915 /* set name only different in casing */
2916 r.startPosition = 1;
2917 r.length = 1;
2918 hr = IDWriteTextLayout_SetFontFamilyName(layout, taHomaW, r);
2919 ok(hr == S_OK, "got 0x%08x\n", hr);
2921 /* zero length range */
2922 r.startPosition = 1;
2923 r.length = 0;
2924 hr = IDWriteTextLayout_SetFontFamilyName(layout, arialW, r);
2925 ok(hr == S_OK, "got 0x%08x\n", hr);
2927 r.startPosition = 0;
2928 r.length = 0;
2929 nameW[0] = 0;
2930 hr = IDWriteTextLayout_GetFontFamilyName(layout, 1, nameW, ARRAY_SIZE(nameW), &r);
2931 ok(hr == S_OK, "got 0x%08x\n", hr);
2932 ok(!lstrcmpW(nameW, taHomaW), "got %s\n", wine_dbgstr_w(nameW));
2933 ok(r.startPosition == 1 && r.length == 1, "got %u, %u\n", r.startPosition, r.length);
2935 r.startPosition = 1;
2936 r.length = 1;
2937 hr = IDWriteTextLayout_SetFontFamilyName(layout, arialW, r);
2938 ok(hr == S_OK, "got 0x%08x\n", hr);
2940 r.startPosition = 1;
2941 r.length = 0;
2942 hr = IDWriteTextLayout_GetFontFamilyName(layout, 1, nameW, ARRAY_SIZE(nameW), &r);
2943 ok(hr == S_OK, "got 0x%08x\n", hr);
2944 ok(r.startPosition == 1 && r.length == 1, "got %u, %u\n", r.startPosition, r.length);
2946 r.startPosition = 0;
2947 r.length = 4;
2948 hr = IDWriteTextLayout_SetFontFamilyName(layout, arialW, r);
2949 ok(hr == S_OK, "got 0x%08x\n", hr);
2951 nameW[0] = 0;
2952 hr = IDWriteTextLayout_GetFontFamilyName(layout, 1, nameW, ARRAY_SIZE(nameW), &r);
2953 ok(hr == S_OK, "got 0x%08x\n", hr);
2954 ok(r.startPosition == 0 && r.length == 4, "got %u, %u\n", r.startPosition, r.length);
2955 ok(!lstrcmpW(nameW, arialW), "got name %s\n", wine_dbgstr_w(nameW));
2957 IDWriteTextLayout_Release(layout);
2958 IDWriteTextFormat_Release(format);
2959 IDWriteFactory_Release(factory);
2962 static void test_SetFontStyle(void)
2964 static const WCHAR strW[] = {'a','b','c','d',0};
2965 IDWriteTextFormat *format;
2966 IDWriteTextLayout *layout;
2967 IDWriteFactory *factory;
2968 DWRITE_FONT_STYLE style;
2969 DWRITE_TEXT_RANGE r;
2970 HRESULT hr;
2972 factory = create_factory();
2974 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
2975 DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format);
2976 ok(hr == S_OK, "got 0x%08x\n", hr);
2978 hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 1000.0, 1000.0, &layout);
2979 ok(hr == S_OK, "got 0x%08x\n", hr);
2981 /* invalid style value */
2982 r.startPosition = 1;
2983 r.length = 1;
2984 hr = IDWriteTextLayout_SetFontStyle(layout, DWRITE_FONT_STYLE_ITALIC+1, r);
2985 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
2987 r.startPosition = 1;
2988 r.length = 0;
2989 hr = IDWriteTextLayout_GetFontStyle(layout, 0, &style, &r);
2990 ok(hr == S_OK, "got 0x%08x\n", hr);
2991 ok(r.startPosition == 0 && r.length == ~0u, "got %u, %u\n", r.startPosition, r.length);
2992 ok(style == DWRITE_FONT_STYLE_NORMAL, "got %d\n", style);
2994 r.startPosition = 1;
2995 r.length = 1;
2996 hr = IDWriteTextLayout_SetFontStyle(layout, DWRITE_FONT_STYLE_ITALIC, r);
2997 ok(hr == S_OK, "got 0x%08x\n", hr);
2999 /* zero length range */
3000 r.startPosition = 1;
3001 r.length = 0;
3002 hr = IDWriteTextLayout_SetFontStyle(layout, DWRITE_FONT_STYLE_NORMAL, r);
3003 ok(hr == S_OK, "got 0x%08x\n", hr);
3005 style = DWRITE_FONT_STYLE_NORMAL;
3006 hr = IDWriteTextLayout_GetFontStyle(layout, 1, &style, &r);
3007 ok(hr == S_OK, "got 0x%08x\n", hr);
3008 ok(style == DWRITE_FONT_STYLE_ITALIC, "got %d\n", style);
3010 r.startPosition = 0;
3011 r.length = 4;
3012 hr = IDWriteTextLayout_SetFontStyle(layout, DWRITE_FONT_STYLE_OBLIQUE, r);
3013 ok(hr == S_OK, "got 0x%08x\n", hr);
3015 style = DWRITE_FONT_STYLE_ITALIC;
3016 hr = IDWriteTextLayout_GetFontStyle(layout, 1, &style, &r);
3017 ok(hr == S_OK, "got 0x%08x\n", hr);
3018 ok(style == DWRITE_FONT_STYLE_OBLIQUE, "got %d\n", style);
3020 style = DWRITE_FONT_STYLE_ITALIC;
3021 hr = IDWriteTextLayout_GetFontStyle(layout, 0, &style, &r);
3022 ok(hr == S_OK, "got 0x%08x\n", hr);
3023 ok(r.startPosition == 0 && r.length == 4, "got %u, %u\n", r.startPosition, r.length);
3024 ok(style == DWRITE_FONT_STYLE_OBLIQUE, "got %d\n", style);
3026 style = DWRITE_FONT_STYLE_ITALIC;
3027 r.startPosition = r.length = 0;
3028 hr = IDWriteTextLayout_GetFontStyle(layout, 20, &style, &r);
3029 ok(hr == S_OK, "got 0x%08x\n", hr);
3030 ok(r.startPosition == 4 && r.length == ~0u-4, "got %u, %u\n", r.startPosition, r.length);
3031 ok(style == DWRITE_FONT_STYLE_NORMAL, "got %d\n", style);
3033 r.startPosition = 100;
3034 r.length = 4;
3035 hr = IDWriteTextLayout_SetFontStyle(layout, DWRITE_FONT_STYLE_OBLIQUE, r);
3036 ok(hr == S_OK, "got 0x%08x\n", hr);
3038 style = DWRITE_FONT_STYLE_NORMAL;
3039 r.startPosition = r.length = 0;
3040 hr = IDWriteTextLayout_GetFontStyle(layout, 100, &style, &r);
3041 ok(hr == S_OK, "got 0x%08x\n", hr);
3042 ok(r.startPosition == 100 && r.length == 4, "got %u, %u\n", r.startPosition, r.length);
3043 ok(style == DWRITE_FONT_STYLE_OBLIQUE, "got %d\n", style);
3045 IDWriteTextLayout_Release(layout);
3046 IDWriteTextFormat_Release(format);
3047 IDWriteFactory_Release(factory);
3050 static void test_SetFontStretch(void)
3052 static const WCHAR strW[] = {'a','b','c','d',0};
3053 DWRITE_FONT_STRETCH stretch;
3054 IDWriteTextFormat *format;
3055 IDWriteTextLayout *layout;
3056 IDWriteFactory *factory;
3057 DWRITE_TEXT_RANGE r;
3058 HRESULT hr;
3060 factory = create_factory();
3062 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
3063 DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format);
3064 ok(hr == S_OK, "got 0x%08x\n", hr);
3066 hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 1000.0, 1000.0, &layout);
3067 ok(hr == S_OK, "got 0x%08x\n", hr);
3069 /* invalid stretch value */
3070 r.startPosition = 1;
3071 r.length = 1;
3072 hr = IDWriteTextLayout_SetFontStretch(layout, DWRITE_FONT_STRETCH_ULTRA_EXPANDED+1, r);
3073 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
3075 r.startPosition = 1;
3076 r.length = 0;
3077 stretch = DWRITE_FONT_STRETCH_UNDEFINED;
3078 hr = IDWriteTextLayout_GetFontStretch(layout, 0, &stretch, &r);
3079 ok(hr == S_OK, "got 0x%08x\n", hr);
3080 ok(r.startPosition == 0 && r.length == ~0u, "got %u, %u\n", r.startPosition, r.length);
3081 ok(stretch == DWRITE_FONT_STRETCH_NORMAL, "got %d\n", stretch);
3083 r.startPosition = 1;
3084 r.length = 1;
3085 hr = IDWriteTextLayout_SetFontStretch(layout, DWRITE_FONT_STRETCH_CONDENSED, r);
3086 ok(hr == S_OK, "got 0x%08x\n", hr);
3088 /* zero length range */
3089 r.startPosition = 1;
3090 r.length = 0;
3091 hr = IDWriteTextLayout_SetFontStretch(layout, DWRITE_FONT_STRETCH_NORMAL, r);
3092 ok(hr == S_OK, "got 0x%08x\n", hr);
3094 stretch = DWRITE_FONT_STRETCH_UNDEFINED;
3095 hr = IDWriteTextLayout_GetFontStretch(layout, 1, &stretch, &r);
3096 ok(hr == S_OK, "got 0x%08x\n", hr);
3097 ok(stretch == DWRITE_FONT_STRETCH_CONDENSED, "got %d\n", stretch);
3099 r.startPosition = 0;
3100 r.length = 4;
3101 hr = IDWriteTextLayout_SetFontStretch(layout, DWRITE_FONT_STRETCH_EXPANDED, r);
3102 ok(hr == S_OK, "got 0x%08x\n", hr);
3104 stretch = DWRITE_FONT_STRETCH_UNDEFINED;
3105 hr = IDWriteTextLayout_GetFontStretch(layout, 1, &stretch, &r);
3106 ok(hr == S_OK, "got 0x%08x\n", hr);
3107 ok(stretch == DWRITE_FONT_STRETCH_EXPANDED, "got %d\n", stretch);
3109 stretch = DWRITE_FONT_STRETCH_UNDEFINED;
3110 hr = IDWriteTextLayout_GetFontStretch(layout, 0, &stretch, &r);
3111 ok(hr == S_OK, "got 0x%08x\n", hr);
3112 ok(r.startPosition == 0 && r.length == 4, "got %u, %u\n", r.startPosition, r.length);
3113 ok(stretch == DWRITE_FONT_STRETCH_EXPANDED, "got %d\n", stretch);
3115 stretch = DWRITE_FONT_STRETCH_UNDEFINED;
3116 r.startPosition = r.length = 0;
3117 hr = IDWriteTextLayout_GetFontStretch(layout, 20, &stretch, &r);
3118 ok(hr == S_OK, "got 0x%08x\n", hr);
3119 ok(r.startPosition == 4 && r.length == ~0u-4, "got %u, %u\n", r.startPosition, r.length);
3120 ok(stretch == DWRITE_FONT_STRETCH_NORMAL, "got %d\n", stretch);
3122 r.startPosition = 100;
3123 r.length = 4;
3124 hr = IDWriteTextLayout_SetFontStretch(layout, DWRITE_FONT_STRETCH_EXPANDED, r);
3125 ok(hr == S_OK, "got 0x%08x\n", hr);
3127 stretch = DWRITE_FONT_STRETCH_UNDEFINED;
3128 r.startPosition = r.length = 0;
3129 hr = IDWriteTextLayout_GetFontStretch(layout, 100, &stretch, &r);
3130 ok(hr == S_OK, "got 0x%08x\n", hr);
3131 ok(r.startPosition == 100 && r.length == 4, "got %u, %u\n", r.startPosition, r.length);
3132 ok(stretch == DWRITE_FONT_STRETCH_EXPANDED, "got %d\n", stretch);
3134 /* trying to set undefined value */
3135 r.startPosition = 0;
3136 r.length = 2;
3137 hr = IDWriteTextLayout_SetFontStretch(layout, DWRITE_FONT_STRETCH_UNDEFINED, r);
3138 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
3140 IDWriteTextLayout_Release(layout);
3141 IDWriteTextFormat_Release(format);
3142 IDWriteFactory_Release(factory);
3145 static void test_SetStrikethrough(void)
3147 static const WCHAR strW[] = {'a','b','c','d',0};
3148 IDWriteTextFormat *format;
3149 IDWriteTextLayout *layout;
3150 IDWriteFactory *factory;
3151 DWRITE_TEXT_RANGE r;
3152 BOOL value;
3153 HRESULT hr;
3155 factory = create_factory();
3157 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
3158 DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format);
3159 ok(hr == S_OK, "got 0x%08x\n", hr);
3161 hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 1000.0, 1000.0, &layout);
3162 ok(hr == S_OK, "got 0x%08x\n", hr);
3164 r.startPosition = 1;
3165 r.length = 0;
3166 value = TRUE;
3167 hr = IDWriteTextLayout_GetStrikethrough(layout, 0, &value, &r);
3168 ok(hr == S_OK, "got 0x%08x\n", hr);
3169 ok(r.startPosition == 0 && r.length == ~0u, "got %u, %u\n", r.startPosition, r.length);
3170 ok(value == FALSE, "got %d\n", value);
3172 r.startPosition = 1;
3173 r.length = 1;
3174 hr = IDWriteTextLayout_SetStrikethrough(layout, TRUE, r);
3175 ok(hr == S_OK, "got 0x%08x\n", hr);
3177 value = FALSE;
3178 hr = IDWriteTextLayout_GetStrikethrough(layout, 1, &value, &r);
3179 ok(hr == S_OK, "got 0x%08x\n", hr);
3180 ok(value == TRUE, "got %d\n", value);
3181 ok(r.startPosition == 1 && r.length == 1, "got %u, %u\n", r.startPosition, r.length);
3183 value = TRUE;
3184 r.startPosition = r.length = 0;
3185 hr = IDWriteTextLayout_GetStrikethrough(layout, 20, &value, &r);
3186 ok(hr == S_OK, "got 0x%08x\n", hr);
3187 ok(r.startPosition == 2 && r.length == ~0u-2, "got %u, %u\n", r.startPosition, r.length);
3188 ok(value == FALSE, "got %d\n", value);
3190 r.startPosition = 100;
3191 r.length = 4;
3192 hr = IDWriteTextLayout_SetStrikethrough(layout, TRUE, r);
3193 ok(hr == S_OK, "got 0x%08x\n", hr);
3195 value = FALSE;
3196 r.startPosition = r.length = 0;
3197 hr = IDWriteTextLayout_GetStrikethrough(layout, 100, &value, &r);
3198 ok(hr == S_OK, "got 0x%08x\n", hr);
3199 ok(r.startPosition == 100 && r.length == 4, "got %u, %u\n", r.startPosition, r.length);
3200 ok(value == TRUE, "got %d\n", value);
3202 IDWriteTextLayout_Release(layout);
3203 IDWriteTextFormat_Release(format);
3204 IDWriteFactory_Release(factory);
3207 static void test_GetMetrics(void)
3209 static const WCHAR str2W[] = {0x2066,')',')',0x661,'(',0x627,')',0};
3210 static const WCHAR strW[] = {'a','b','c','d',0};
3211 static const WCHAR str3W[] = {'a',0};
3212 DWRITE_CLUSTER_METRICS clusters[4];
3213 DWRITE_TEXT_METRICS metrics;
3214 IDWriteTextFormat *format;
3215 IDWriteTextLayout *layout;
3216 IDWriteFactory *factory;
3217 UINT32 count, i;
3218 FLOAT width;
3219 HRESULT hr;
3221 factory = create_factory();
3223 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
3224 DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format);
3225 ok(hr == S_OK, "got 0x%08x\n", hr);
3227 hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 500.0, 1000.0, &layout);
3228 ok(hr == S_OK, "got 0x%08x\n", hr);
3230 count = 0;
3231 hr = IDWriteTextLayout_GetClusterMetrics(layout, clusters, 4, &count);
3232 ok(hr == S_OK, "got 0x%08x\n", hr);
3233 ok(count == 4, "got %u\n", count);
3234 for (i = 0, width = 0.0; i < count; i++)
3235 width += clusters[i].width;
3237 memset(&metrics, 0xcc, sizeof(metrics));
3238 hr = IDWriteTextLayout_GetMetrics(layout, &metrics);
3239 ok(hr == S_OK, "got 0x%08x\n", hr);
3240 ok(metrics.left == 0.0, "got %.2f\n", metrics.left);
3241 ok(metrics.top == 0.0, "got %.2f\n", metrics.top);
3242 ok(metrics.width == width, "got %.2f, expected %.2f\n", metrics.width, width);
3243 ok(metrics.widthIncludingTrailingWhitespace == width, "got %.2f, expected %.2f\n",
3244 metrics.widthIncludingTrailingWhitespace, width);
3245 ok(metrics.height > 0.0, "got %.2f\n", metrics.height);
3246 ok(metrics.layoutWidth == 500.0, "got %.2f\n", metrics.layoutWidth);
3247 ok(metrics.layoutHeight == 1000.0, "got %.2f\n", metrics.layoutHeight);
3248 ok(metrics.maxBidiReorderingDepth == 1, "got %u\n", metrics.maxBidiReorderingDepth);
3249 ok(metrics.lineCount == 1, "got %u\n", metrics.lineCount);
3251 IDWriteTextLayout_Release(layout);
3253 /* a string with more complex bidi sequence */
3254 hr = IDWriteFactory_CreateTextLayout(factory, str2W, 7, format, 500.0, 1000.0, &layout);
3255 ok(hr == S_OK, "got 0x%08x\n", hr);
3257 memset(&metrics, 0xcc, sizeof(metrics));
3258 metrics.maxBidiReorderingDepth = 0;
3259 hr = IDWriteTextLayout_GetMetrics(layout, &metrics);
3260 ok(hr == S_OK, "got 0x%08x\n", hr);
3261 ok(metrics.left == 0.0, "got %.2f\n", metrics.left);
3262 ok(metrics.top == 0.0, "got %.2f\n", metrics.top);
3263 ok(metrics.width > 0.0, "got %.2f\n", metrics.width);
3264 ok(metrics.widthIncludingTrailingWhitespace > 0.0, "got %.2f\n", metrics.widthIncludingTrailingWhitespace);
3265 ok(metrics.height > 0.0, "got %.2f\n", metrics.height);
3266 ok(metrics.layoutWidth == 500.0, "got %.2f\n", metrics.layoutWidth);
3267 ok(metrics.layoutHeight == 1000.0, "got %.2f\n", metrics.layoutHeight);
3268 todo_wine
3269 ok(metrics.maxBidiReorderingDepth > 1, "got %u\n", metrics.maxBidiReorderingDepth);
3270 ok(metrics.lineCount == 1, "got %u\n", metrics.lineCount);
3272 IDWriteTextLayout_Release(layout);
3274 /* single cluster layout */
3275 hr = IDWriteFactory_CreateTextLayout(factory, str3W, 1, format, 500.0, 1000.0, &layout);
3276 ok(hr == S_OK, "got 0x%08x\n", hr);
3278 count = 0;
3279 hr = IDWriteTextLayout_GetClusterMetrics(layout, clusters, 1, &count);
3280 ok(hr == S_OK, "got 0x%08x\n", hr);
3281 ok(count == 1, "got %u\n", count);
3283 memset(&metrics, 0xcc, sizeof(metrics));
3284 hr = IDWriteTextLayout_GetMetrics(layout, &metrics);
3285 ok(hr == S_OK, "got 0x%08x\n", hr);
3286 ok(metrics.left == 0.0, "got %.2f\n", metrics.left);
3287 ok(metrics.top == 0.0, "got %.2f\n", metrics.top);
3288 ok(metrics.width == clusters[0].width, "got %.2f, expected %.2f\n", metrics.width, clusters[0].width);
3289 ok(metrics.widthIncludingTrailingWhitespace == clusters[0].width, "got %.2f\n", metrics.widthIncludingTrailingWhitespace);
3290 ok(metrics.height > 0.0, "got %.2f\n", metrics.height);
3291 ok(metrics.layoutWidth == 500.0, "got %.2f\n", metrics.layoutWidth);
3292 ok(metrics.layoutHeight == 1000.0, "got %.2f\n", metrics.layoutHeight);
3293 ok(metrics.maxBidiReorderingDepth == 1, "got %u\n", metrics.maxBidiReorderingDepth);
3294 ok(metrics.lineCount == 1, "got %u\n", metrics.lineCount);
3295 IDWriteTextLayout_Release(layout);
3297 IDWriteTextFormat_Release(format);
3298 IDWriteFactory_Release(factory);
3301 static void test_SetFlowDirection(void)
3303 static const WCHAR strW[] = {'a','b','c','d',0};
3304 DWRITE_READING_DIRECTION reading;
3305 DWRITE_FLOW_DIRECTION flow;
3306 IDWriteTextFormat *format;
3307 IDWriteTextLayout *layout;
3308 IDWriteFactory *factory;
3309 HRESULT hr;
3311 factory = create_factory();
3313 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
3314 DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format);
3315 ok(hr == S_OK, "got 0x%08x\n", hr);
3317 flow = IDWriteTextFormat_GetFlowDirection(format);
3318 ok(flow == DWRITE_FLOW_DIRECTION_TOP_TO_BOTTOM, "got %d\n", flow);
3320 reading = IDWriteTextFormat_GetReadingDirection(format);
3321 ok(reading == DWRITE_READING_DIRECTION_LEFT_TO_RIGHT, "got %d\n", reading);
3323 hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 500.0, 1000.0, &layout);
3324 ok(hr == S_OK, "got 0x%08x\n", hr);
3325 IDWriteTextLayout_Release(layout);
3327 hr = IDWriteTextFormat_SetFlowDirection(format, DWRITE_FLOW_DIRECTION_LEFT_TO_RIGHT);
3328 ok(hr == S_OK || broken(hr == E_INVALIDARG) /* vista,win7 */, "got 0x%08x\n", hr);
3329 if (hr == S_OK) {
3330 hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 500.0, 1000.0, &layout);
3331 ok(hr == S_OK, "got 0x%08x\n", hr);
3332 IDWriteTextLayout_Release(layout);
3334 hr = IDWriteTextFormat_SetReadingDirection(format, DWRITE_READING_DIRECTION_TOP_TO_BOTTOM);
3335 ok(hr == S_OK, "got 0x%08x\n", hr);
3337 hr = IDWriteTextFormat_SetFlowDirection(format, DWRITE_FLOW_DIRECTION_TOP_TO_BOTTOM);
3338 ok(hr == S_OK, "got 0x%08x\n", hr);
3340 hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 500.0, 1000.0, &layout);
3341 ok(hr == S_OK, "got 0x%08x\n", hr);
3342 IDWriteTextLayout_Release(layout);
3344 else
3345 win_skip("DWRITE_FLOW_DIRECTION_LEFT_TO_RIGHT is not supported\n");
3347 IDWriteTextFormat_Release(format);
3348 IDWriteFactory_Release(factory);
3351 static const struct drawcall_entry draweffect_seq[] = {
3352 { DRAW_GLYPHRUN|DRAW_EFFECT, {'a','e',0x0300,0}, {'e','n','-','u','s',0}, 2 },
3353 { DRAW_GLYPHRUN, {'d',0}, {'e','n','-','u','s',0}, 1 },
3354 { DRAW_LAST_KIND }
3357 static const struct drawcall_entry draweffect2_seq[] = {
3358 { DRAW_GLYPHRUN|DRAW_EFFECT, {'a','e',0}, {'e','n','-','u','s',0}, 2 },
3359 { DRAW_GLYPHRUN, {'c','d',0}, {'e','n','-','u','s',0}, 2 },
3360 { DRAW_LAST_KIND }
3363 static const struct drawcall_entry draweffect3_seq[] = {
3364 { DRAW_INLINE|DRAW_EFFECT },
3365 { DRAW_LAST_KIND }
3368 static const struct drawcall_entry draweffect4_seq[] = {
3369 { DRAW_INLINE },
3370 { DRAW_LAST_KIND }
3373 static void test_SetDrawingEffect(void)
3375 static const WCHAR strW[] = {'a','e',0x0300,'d',0}; /* accent grave */
3376 static const WCHAR str2W[] = {'a','e','c','d',0};
3377 IDWriteInlineObject *sign;
3378 IDWriteTextFormat *format;
3379 IDWriteTextLayout *layout;
3380 IDWriteFactory *factory;
3381 IUnknown *unk, *effect;
3382 DWRITE_TEXT_RANGE r;
3383 HRESULT hr;
3384 LONG ref;
3386 factory = create_factory();
3388 effect = create_test_effect();
3390 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
3391 DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format);
3392 ok(hr == S_OK, "got 0x%08x\n", hr);
3394 /* string with combining mark */
3395 hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 500.0, 1000.0, &layout);
3396 ok(hr == S_OK, "got 0x%08x\n", hr);
3398 /* set effect past the end of text */
3399 r.startPosition = 100;
3400 r.length = 10;
3401 hr = IDWriteTextLayout_SetDrawingEffect(layout, effect, r);
3402 ok(hr == S_OK, "got 0x%08x\n", hr);
3404 r.startPosition = r.length = 0;
3405 hr = IDWriteTextLayout_GetDrawingEffect(layout, 101, &unk, &r);
3406 ok(hr == S_OK, "got 0x%08x\n", hr);
3407 ok(r.startPosition == 100 && r.length == 10, "got %u, %u\n", r.startPosition, r.length);
3408 IUnknown_Release(unk);
3410 r.startPosition = r.length = 0;
3411 unk = (void*)0xdeadbeef;
3412 hr = IDWriteTextLayout_GetDrawingEffect(layout, 1000, &unk, &r);
3413 ok(hr == S_OK, "got 0x%08x\n", hr);
3414 ok(r.startPosition == 110 && r.length == ~0u-110, "got %u, %u\n", r.startPosition, r.length);
3415 ok(unk == NULL, "got %p\n", unk);
3417 /* effect is applied to clusters, not individual text positions */
3418 r.startPosition = 0;
3419 r.length = 2;
3420 hr = IDWriteTextLayout_SetDrawingEffect(layout, effect, r);
3421 ok(hr == S_OK, "got 0x%08x\n", hr);
3423 flush_sequence(sequences, RENDERER_ID);
3424 hr = IDWriteTextLayout_Draw(layout, NULL, &testrenderer, 0.0, 0.0);
3425 ok(hr == S_OK, "got 0x%08x\n", hr);
3426 ok_sequence(sequences, RENDERER_ID, draweffect_seq, "effect draw test", TRUE);
3427 IDWriteTextLayout_Release(layout);
3429 /* simple string */
3430 hr = IDWriteFactory_CreateTextLayout(factory, str2W, 4, format, 500.0, 1000.0, &layout);
3431 ok(hr == S_OK, "got 0x%08x\n", hr);
3433 r.startPosition = 0;
3434 r.length = 2;
3435 hr = IDWriteTextLayout_SetDrawingEffect(layout, effect, r);
3436 ok(hr == S_OK, "got 0x%08x\n", hr);
3438 flush_sequence(sequences, RENDERER_ID);
3439 hr = IDWriteTextLayout_Draw(layout, NULL, &testrenderer, 0.0, 0.0);
3440 ok(hr == S_OK, "got 0x%08x\n", hr);
3441 ok_sequence(sequences, RENDERER_ID, draweffect2_seq, "effect draw test 2", FALSE);
3442 IDWriteTextLayout_Release(layout);
3444 /* Inline object - effect set for same range */
3445 hr = IDWriteFactory_CreateEllipsisTrimmingSign(factory, format, &sign);
3446 ok(hr == S_OK, "got 0x%08x\n", hr);
3448 hr = IDWriteFactory_CreateTextLayout(factory, str2W, 4, format, 500.0, 1000.0, &layout);
3449 ok(hr == S_OK, "got 0x%08x\n", hr);
3451 r.startPosition = 0;
3452 r.length = 4;
3453 hr = IDWriteTextLayout_SetInlineObject(layout, sign, r);
3454 ok(hr == S_OK, "got 0x%08x\n", hr);
3456 hr = IDWriteTextLayout_SetDrawingEffect(layout, effect, r);
3457 ok(hr == S_OK, "got 0x%08x\n", hr);
3459 flush_sequence(sequences, RENDERER_ID);
3460 hr = IDWriteTextLayout_Draw(layout, NULL, &testrenderer, 0.0, 0.0);
3461 ok(hr == S_OK, "got 0x%08x\n", hr);
3462 ok_sequence(sequences, RENDERER_ID, draweffect3_seq, "effect draw test 3", FALSE);
3464 /* now set effect somewhere inside a range replaced by inline object */
3465 hr = IDWriteTextLayout_SetDrawingEffect(layout, NULL, r);
3466 ok(hr == S_OK, "got 0x%08x\n", hr);
3468 r.startPosition = 1;
3469 r.length = 1;
3470 hr = IDWriteTextLayout_SetDrawingEffect(layout, effect, r);
3471 ok(hr == S_OK, "got 0x%08x\n", hr);
3473 /* no effect is reported in this case */
3474 flush_sequence(sequences, RENDERER_ID);
3475 hr = IDWriteTextLayout_Draw(layout, NULL, &testrenderer, 0.0, 0.0);
3476 ok(hr == S_OK, "got 0x%08x\n", hr);
3477 ok_sequence(sequences, RENDERER_ID, draweffect4_seq, "effect draw test 4", FALSE);
3479 r.startPosition = 0;
3480 r.length = 4;
3481 hr = IDWriteTextLayout_SetDrawingEffect(layout, NULL, r);
3482 ok(hr == S_OK, "got 0x%08x\n", hr);
3484 r.startPosition = 0;
3485 r.length = 1;
3486 hr = IDWriteTextLayout_SetDrawingEffect(layout, effect, r);
3487 ok(hr == S_OK, "got 0x%08x\n", hr);
3489 /* first range position is all that matters for inline ranges */
3490 flush_sequence(sequences, RENDERER_ID);
3491 hr = IDWriteTextLayout_Draw(layout, NULL, &testrenderer, 0.0, 0.0);
3492 ok(hr == S_OK, "got 0x%08x\n", hr);
3493 ok_sequence(sequences, RENDERER_ID, draweffect3_seq, "effect draw test 5", FALSE);
3495 IDWriteTextLayout_Release(layout);
3497 ref = IUnknown_Release(effect);
3498 ok(ref == 0, "Unexpected effect refcount %u\n", ref);
3499 IDWriteInlineObject_Release(sign);
3500 IDWriteTextFormat_Release(format);
3501 IDWriteFactory_Release(factory);
3504 static BOOL get_enus_string(IDWriteLocalizedStrings *strings, WCHAR *buff, UINT32 size)
3506 UINT32 index;
3507 BOOL exists = FALSE;
3508 HRESULT hr;
3510 hr = IDWriteLocalizedStrings_FindLocaleName(strings, enusW, &index, &exists);
3511 ok(hr == S_OK, "got 0x%08x\n", hr);
3513 if (exists) {
3514 hr = IDWriteLocalizedStrings_GetString(strings, index, buff, size);
3515 ok(hr == S_OK, "got 0x%08x\n", hr);
3517 else
3518 *buff = 0;
3520 return exists;
3523 static void test_GetLineMetrics(void)
3525 static const WCHAR str3W[] = {'a','\r','b','\n','c','\n','\r','d','\r','\n',0};
3526 static const WCHAR strW[] = {'a','b','c','d',' ',0};
3527 static const WCHAR str2W[] = {'a','b','\r','c','d',0};
3528 static const WCHAR str4W[] = {'a','\r',0};
3529 static const WCHAR emptyW[] = {0};
3530 IDWriteFontCollection *syscollection;
3531 DWRITE_FONT_METRICS fontmetrics;
3532 DWRITE_LINE_METRICS metrics[6];
3533 UINT32 count, i, familycount;
3534 IDWriteTextFormat *format;
3535 IDWriteTextLayout *layout;
3536 IDWriteFontFace *fontface;
3537 IDWriteFactory *factory;
3538 DWRITE_TEXT_RANGE range;
3539 WCHAR nameW[256];
3540 HRESULT hr;
3542 factory = create_factory();
3544 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
3545 DWRITE_FONT_STRETCH_NORMAL, 2048.0, enusW, &format);
3546 ok(hr == S_OK, "got 0x%08x\n", hr);
3548 hr = IDWriteFactory_CreateTextLayout(factory, strW, 5, format, 30000.0, 1000.0, &layout);
3549 ok(hr == S_OK, "got 0x%08x\n", hr);
3551 count = 0;
3552 hr = IDWriteTextLayout_GetLineMetrics(layout, metrics, 0, &count);
3553 ok(hr == E_NOT_SUFFICIENT_BUFFER, "got 0x%08x\n", hr);
3554 ok(count == 1, "got count %u\n", count);
3556 memset(metrics, 0, sizeof(metrics));
3557 hr = IDWriteTextLayout_GetLineMetrics(layout, metrics, 1, &count);
3558 ok(hr == S_OK, "got 0x%08x\n", hr);
3559 ok(metrics[0].length == 5, "got %u\n", metrics[0].length);
3560 ok(metrics[0].trailingWhitespaceLength == 1, "got %u\n", metrics[0].trailingWhitespaceLength);
3562 ok(metrics[0].newlineLength == 0, "got %u\n", metrics[0].newlineLength);
3563 ok(metrics[0].isTrimmed == FALSE, "got %d\n", metrics[0].isTrimmed);
3565 IDWriteTextLayout_Release(layout);
3566 IDWriteTextFormat_Release(format);
3568 /* Test line height and baseline calculation */
3569 hr = IDWriteFactory_GetSystemFontCollection(factory, &syscollection, FALSE);
3570 ok(hr == S_OK, "got 0x%08x\n", hr);
3571 familycount = IDWriteFontCollection_GetFontFamilyCount(syscollection);
3573 for (i = 0; i < familycount; i++) {
3574 IDWriteLocalizedStrings *names;
3575 IDWriteFontFamily *family;
3576 IDWriteFont *font;
3577 BOOL exists;
3579 format = NULL;
3580 layout = NULL;
3582 hr = IDWriteFontCollection_GetFontFamily(syscollection, i, &family);
3583 ok(hr == S_OK, "got 0x%08x\n", hr);
3585 hr = IDWriteFontFamily_GetFirstMatchingFont(family, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STRETCH_NORMAL,
3586 DWRITE_FONT_STYLE_NORMAL, &font);
3587 ok(hr == S_OK, "got 0x%08x\n", hr);
3589 hr = IDWriteFont_CreateFontFace(font, &fontface);
3590 ok(hr == S_OK, "got 0x%08x\n", hr);
3592 hr = IDWriteFontFamily_GetFamilyNames(family, &names);
3593 ok(hr == S_OK, "got 0x%08x\n", hr);
3595 if (!(exists = get_enus_string(names, nameW, ARRAY_SIZE(nameW)))) {
3596 IDWriteLocalFontFileLoader *localloader;
3597 IDWriteFontFileLoader *loader;
3598 IDWriteFontFile *file;
3599 const void *key;
3600 UINT32 keysize;
3601 UINT32 count;
3603 count = 1;
3604 hr = IDWriteFontFace_GetFiles(fontface, &count, &file);
3605 ok(hr == S_OK, "got 0x%08x\n", hr);
3607 hr = IDWriteFontFile_GetLoader(file, &loader);
3608 ok(hr == S_OK, "got 0x%08x\n", hr);
3610 hr = IDWriteFontFileLoader_QueryInterface(loader, &IID_IDWriteLocalFontFileLoader, (void**)&localloader);
3611 ok(hr == S_OK, "got 0x%08x\n", hr);
3612 IDWriteFontFileLoader_Release(loader);
3614 hr = IDWriteFontFile_GetReferenceKey(file, &key, &keysize);
3615 ok(hr == S_OK, "got 0x%08x\n", hr);
3617 hr = IDWriteLocalFontFileLoader_GetFilePathFromKey(localloader, key, keysize, nameW, ARRAY_SIZE(nameW));
3618 ok(hr == S_OK, "got 0x%08x\n", hr);
3620 skip("Failed to get English family name, font file %s\n", wine_dbgstr_w(nameW));
3622 IDWriteLocalFontFileLoader_Release(localloader);
3623 IDWriteFontFile_Release(file);
3626 IDWriteLocalizedStrings_Release(names);
3627 IDWriteFont_Release(font);
3629 if (!exists)
3630 goto cleanup;
3632 IDWriteFontFace_GetMetrics(fontface, &fontmetrics);
3633 hr = IDWriteFactory_CreateTextFormat(factory, nameW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
3634 DWRITE_FONT_STRETCH_NORMAL, fontmetrics.designUnitsPerEm, enusW, &format);
3635 ok(hr == S_OK, "got 0x%08x\n", hr);
3637 hr = IDWriteFactory_CreateTextLayout(factory, emptyW, 1, format, 30000.0f, 100.0f, &layout);
3638 ok(hr == S_OK, "got 0x%08x\n", hr);
3640 memset(metrics, 0, sizeof(metrics));
3641 count = 0;
3642 hr = IDWriteTextLayout_GetLineMetrics(layout, metrics, ARRAY_SIZE(metrics), &count);
3643 ok(hr == S_OK, "got 0x%08x\n", hr);
3644 ok(count == 1, "got %u\n", count);
3646 ok(metrics[0].baseline == fontmetrics.ascent + fontmetrics.lineGap, "%s: got %.2f, expected %d, "
3647 "linegap %d\n", wine_dbgstr_w(nameW), metrics[0].baseline, fontmetrics.ascent + fontmetrics.lineGap,
3648 fontmetrics.lineGap);
3649 ok(metrics[0].height == fontmetrics.ascent + fontmetrics.descent + fontmetrics.lineGap,
3650 "%s: got %.2f, expected %d, linegap %d\n", wine_dbgstr_w(nameW), metrics[0].height,
3651 fontmetrics.ascent + fontmetrics.descent + fontmetrics.lineGap, fontmetrics.lineGap);
3653 cleanup:
3654 if (layout)
3655 IDWriteTextLayout_Release(layout);
3656 if (format)
3657 IDWriteTextFormat_Release(format);
3658 IDWriteFontFace_Release(fontface);
3659 IDWriteFontFamily_Release(family);
3661 IDWriteFontCollection_Release(syscollection);
3663 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
3664 DWRITE_FONT_STRETCH_NORMAL, 2048.0f, enusW, &format);
3665 ok(hr == S_OK, "got 0x%08x\n", hr);
3667 fontface = get_fontface_from_format(format);
3668 ok(fontface != NULL, "got %p\n", fontface);
3670 /* force 2 lines */
3671 hr = IDWriteFactory_CreateTextLayout(factory, str2W, 5, format, 10000.0, 1000.0, &layout);
3672 ok(hr == S_OK, "got 0x%08x\n", hr);
3674 memset(metrics, 0, sizeof(metrics));
3675 count = 0;
3676 hr = IDWriteTextLayout_GetLineMetrics(layout, metrics, ARRAY_SIZE(metrics), &count);
3677 ok(hr == S_OK, "got 0x%08x\n", hr);
3678 ok(count == 2, "got %u\n", count);
3679 /* baseline is relative to a line, and is not accumulated */
3680 ok(metrics[0].baseline == metrics[1].baseline, "got %.2f, %.2f\n", metrics[0].baseline,
3681 metrics[1].baseline);
3683 IDWriteTextLayout_Release(layout);
3684 IDWriteTextFormat_Release(format);
3686 /* line breaks */
3687 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
3688 DWRITE_FONT_STRETCH_NORMAL, 12.0, enusW, &format);
3689 ok(hr == S_OK, "got 0x%08x\n", hr);
3691 hr = IDWriteFactory_CreateTextLayout(factory, str3W, 10, format, 100.0, 300.0, &layout);
3692 ok(hr == S_OK, "got 0x%08x\n", hr);
3694 memset(metrics, 0xcc, sizeof(metrics));
3695 count = 0;
3696 hr = IDWriteTextLayout_GetLineMetrics(layout, metrics, ARRAY_SIZE(metrics), &count);
3697 ok(hr == S_OK, "got 0x%08x\n", hr);
3698 ok(count == 6, "got %u\n", count);
3700 ok(metrics[0].length == 2, "got %u\n", metrics[0].length);
3701 ok(metrics[1].length == 2, "got %u\n", metrics[1].length);
3702 ok(metrics[2].length == 2, "got %u\n", metrics[2].length);
3703 ok(metrics[3].length == 1, "got %u\n", metrics[3].length);
3704 ok(metrics[4].length == 3, "got %u\n", metrics[4].length);
3705 ok(metrics[5].length == 0, "got %u\n", metrics[5].length);
3707 ok(metrics[0].newlineLength == 1, "got %u\n", metrics[0].newlineLength);
3708 ok(metrics[1].newlineLength == 1, "got %u\n", metrics[1].newlineLength);
3709 ok(metrics[2].newlineLength == 1, "got %u\n", metrics[2].newlineLength);
3710 ok(metrics[3].newlineLength == 1, "got %u\n", metrics[3].newlineLength);
3711 ok(metrics[4].newlineLength == 2, "got %u\n", metrics[4].newlineLength);
3712 ok(metrics[5].newlineLength == 0, "got %u\n", metrics[5].newlineLength);
3714 ok(metrics[0].trailingWhitespaceLength == 1, "got %u\n", metrics[0].newlineLength);
3715 ok(metrics[1].trailingWhitespaceLength == 1, "got %u\n", metrics[1].newlineLength);
3716 ok(metrics[2].trailingWhitespaceLength == 1, "got %u\n", metrics[2].newlineLength);
3717 ok(metrics[3].trailingWhitespaceLength == 1, "got %u\n", metrics[3].newlineLength);
3718 ok(metrics[4].trailingWhitespaceLength == 2, "got %u\n", metrics[4].newlineLength);
3719 ok(metrics[5].trailingWhitespaceLength == 0, "got %u\n", metrics[5].newlineLength);
3721 IDWriteTextLayout_Release(layout);
3723 /* empty text layout */
3724 hr = IDWriteFactory_CreateTextLayout(factory, strW, 0, format, 100.0f, 300.0f, &layout);
3725 ok(hr == S_OK, "got 0x%08x\n", hr);
3727 count = 0;
3728 memset(metrics, 0, sizeof(metrics));
3729 hr = IDWriteTextLayout_GetLineMetrics(layout, metrics, 1, &count);
3730 ok(hr == S_OK, "got 0x%08x\n", hr);
3731 ok(count == 1, "got %u\n", count);
3732 ok(metrics[0].length == 0, "got %u\n", metrics[0].length);
3733 ok(metrics[0].trailingWhitespaceLength == 0, "got %u\n", metrics[0].trailingWhitespaceLength);
3734 ok(metrics[0].newlineLength == 0, "got %u\n", metrics[0].newlineLength);
3735 ok(metrics[0].height > 0.0f, "got %f\n", metrics[0].height);
3736 ok(metrics[0].baseline > 0.0f, "got %f\n", metrics[0].baseline);
3737 ok(!metrics[0].isTrimmed, "got %d\n", metrics[0].isTrimmed);
3739 /* change font size at first position, see if metrics changed */
3740 range.startPosition = 0;
3741 range.length = 1;
3742 hr = IDWriteTextLayout_SetFontSize(layout, 80.0f, range);
3743 ok(hr == S_OK, "got 0x%08x\n", hr);
3745 count = 0;
3746 hr = IDWriteTextLayout_GetLineMetrics(layout, metrics + 1, 1, &count);
3747 ok(hr == S_OK, "got 0x%08x\n", hr);
3748 ok(count == 1, "got %u\n", count);
3749 ok(metrics[1].height > metrics[0].height, "got %f\n", metrics[1].height);
3750 ok(metrics[1].baseline > metrics[0].baseline, "got %f\n", metrics[1].baseline);
3752 /* revert font size back to format value, set different size for position 1 */
3753 hr = IDWriteTextLayout_SetFontSize(layout, 12.0f, range);
3754 ok(hr == S_OK, "got 0x%08x\n", hr);
3756 range.startPosition = 1;
3757 range.length = 1;
3758 hr = IDWriteTextLayout_SetFontSize(layout, 80.0f, range);
3759 ok(hr == S_OK, "got 0x%08x\n", hr);
3761 memset(metrics + 1, 0, sizeof(*metrics));
3762 count = 0;
3763 hr = IDWriteTextLayout_GetLineMetrics(layout, metrics + 1, 1, &count);
3764 ok(hr == S_OK, "got 0x%08x\n", hr);
3765 ok(count == 1, "got %u\n", count);
3766 ok(metrics[1].height == metrics[0].height, "got %f\n", metrics[1].height);
3767 ok(metrics[1].baseline == metrics[0].baseline, "got %f\n", metrics[1].baseline);
3769 IDWriteTextLayout_Release(layout);
3771 /* text is "a\r" */
3772 hr = IDWriteFactory_CreateTextLayout(factory, str4W, 2, format, 100.0f, 300.0f, &layout);
3773 ok(hr == S_OK, "got 0x%08x\n", hr);
3775 count = 0;
3776 memset(metrics, 0, sizeof(metrics));
3777 hr = IDWriteTextLayout_GetLineMetrics(layout, metrics, ARRAY_SIZE(metrics), &count);
3778 ok(hr == S_OK, "got 0x%08x\n", hr);
3779 ok(count == 2, "got %u\n", count);
3780 ok(metrics[0].length == 2, "got %u\n", metrics[0].length);
3781 ok(metrics[0].newlineLength == 1, "got %u\n", metrics[0].newlineLength);
3782 ok(metrics[0].height > 0.0f, "got %f\n", metrics[0].height);
3783 ok(metrics[0].baseline > 0.0f, "got %f\n", metrics[0].baseline);
3784 ok(metrics[1].length == 0, "got %u\n", metrics[1].length);
3785 ok(metrics[1].newlineLength == 0, "got %u\n", metrics[1].newlineLength);
3786 ok(metrics[1].height > 0.0f, "got %f\n", metrics[1].height);
3787 ok(metrics[1].baseline > 0.0f, "got %f\n", metrics[1].baseline);
3789 range.startPosition = 1;
3790 range.length = 1;
3791 hr = IDWriteTextLayout_SetFontSize(layout, 80.0f, range);
3792 ok(hr == S_OK, "got 0x%08x\n", hr);
3794 hr = IDWriteTextLayout_GetLineMetrics(layout, metrics + 2, 2, &count);
3795 ok(hr == S_OK, "got 0x%08x\n", hr);
3796 ok(count == 2, "got %u\n", count);
3797 ok(metrics[3].height > metrics[1].height, "got %f, old %f\n", metrics[3].height, metrics[1].height);
3798 ok(metrics[3].baseline > metrics[1].baseline, "got %f, old %f\n", metrics[3].baseline, metrics[1].baseline);
3800 /* revert to original format */
3801 hr = IDWriteTextLayout_SetFontSize(layout, 12.0f, range);
3802 ok(hr == S_OK, "got 0x%08x\n", hr);
3803 hr = IDWriteTextLayout_GetLineMetrics(layout, metrics + 2, 2, &count);
3804 ok(hr == S_OK, "got 0x%08x\n", hr);
3805 ok(count == 2, "got %u\n", count);
3806 ok(metrics[3].height == metrics[1].height, "got %f, old %f\n", metrics[3].height, metrics[1].height);
3807 ok(metrics[3].baseline == metrics[1].baseline, "got %f, old %f\n", metrics[3].baseline, metrics[1].baseline);
3809 /* Switch to uniform spacing */
3810 hr = IDWriteTextLayout_SetLineSpacing(layout, DWRITE_LINE_SPACING_METHOD_UNIFORM, 456.0f, 123.0f);
3811 ok(hr == S_OK, "got 0x%08x\n", hr);
3813 hr = IDWriteTextLayout_GetLineMetrics(layout, metrics, ARRAY_SIZE(metrics), &count);
3814 ok(hr == S_OK, "got 0x%08x\n", hr);
3815 ok(count == 2, "got %u\n", count);
3817 for (i = 0; i < count; i++) {
3818 ok(metrics[i].height == 456.0f, "%u: got line height %f\n", i, metrics[i].height);
3819 ok(metrics[i].baseline == 123.0f, "%u: got line baseline %f\n", i, metrics[i].baseline);
3822 IDWriteTextLayout_Release(layout);
3824 /* Switch to proportional */
3825 hr = IDWriteTextFormat_SetLineSpacing(format, DWRITE_LINE_SPACING_METHOD_PROPORTIONAL, 2.0f, 4.0f);
3826 if (hr == S_OK) {
3827 hr = IDWriteFactory_CreateTextLayout(factory, str4W, 1, format, 100.0f, 300.0f, &layout);
3828 ok(hr == S_OK, "Failed to create layout, hr %#x.\n", hr);
3830 hr = IDWriteTextLayout_GetLineMetrics(layout, metrics, ARRAY_SIZE(metrics), &count);
3831 ok(hr == S_OK, "Failed to get line metrics, hr %#x.\n", hr);
3832 ok(count == 1, "Unexpected line count %u\n", count);
3834 /* Back to default mode. */
3835 hr = IDWriteTextLayout_SetLineSpacing(layout, DWRITE_LINE_SPACING_METHOD_DEFAULT, 0.0f, 0.0f);
3836 ok(hr == S_OK, "Failed to set spacing method, hr %#x.\n", hr);
3838 hr = IDWriteTextLayout_GetLineMetrics(layout, metrics + 1, 1, &count);
3839 ok(hr == S_OK, "Failed to get line metrics, hr %#x.\n", hr);
3840 ok(count == 1, "Unexpected line count %u\n", count);
3842 /* Proportional spacing applies multipliers to default, content based spacing. */
3843 ok(metrics[0].height == 2.0f * metrics[1].height, "Unexpected line height %f.\n", metrics[0].height);
3844 ok(metrics[0].baseline == 4.0f * metrics[1].baseline, "Unexpected line baseline %f.\n", metrics[0].baseline);
3846 IDWriteTextLayout_Release(layout);
3848 else
3849 win_skip("Proportional spacing is not supported.\n");
3851 IDWriteTextFormat_Release(format);
3852 IDWriteFontFace_Release(fontface);
3853 IDWriteFactory_Release(factory);
3856 static void test_SetTextAlignment(void)
3858 static const WCHAR strW[] = {'a',0};
3860 static const WCHAR stringsW[][10] = {
3861 {'a',0},
3865 DWRITE_CLUSTER_METRICS clusters[10];
3866 DWRITE_TEXT_METRICS metrics;
3867 IDWriteTextFormat1 *format1;
3868 IDWriteTextFormat *format;
3869 IDWriteTextLayout *layout;
3870 IDWriteFactory *factory;
3871 DWRITE_TEXT_ALIGNMENT v;
3872 UINT32 count, i;
3873 HRESULT hr;
3875 factory = create_factory();
3877 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
3878 DWRITE_FONT_STRETCH_NORMAL, 12.0, enusW, &format);
3879 ok(hr == S_OK, "got 0x%08x\n", hr);
3881 v = IDWriteTextFormat_GetTextAlignment(format);
3882 ok(v == DWRITE_TEXT_ALIGNMENT_LEADING, "got %d\n", v);
3884 hr = IDWriteFactory_CreateTextLayout(factory, strW, 1, format, 500.0, 100.0, &layout);
3885 ok(hr == S_OK, "got 0x%08x\n", hr);
3887 v = IDWriteTextLayout_GetTextAlignment(layout);
3888 ok(v == DWRITE_TEXT_ALIGNMENT_LEADING, "got %d\n", v);
3890 hr = IDWriteTextLayout_SetTextAlignment(layout, DWRITE_TEXT_ALIGNMENT_TRAILING);
3891 ok(hr == S_OK, "got 0x%08x\n", hr);
3893 hr = IDWriteTextLayout_SetTextAlignment(layout, DWRITE_TEXT_ALIGNMENT_TRAILING);
3894 ok(hr == S_OK, "got 0x%08x\n", hr);
3896 v = IDWriteTextFormat_GetTextAlignment(format);
3897 ok(v == DWRITE_TEXT_ALIGNMENT_LEADING, "got %d\n", v);
3899 v = IDWriteTextLayout_GetTextAlignment(layout);
3900 ok(v == DWRITE_TEXT_ALIGNMENT_TRAILING, "got %d\n", v);
3902 hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextFormat1, (void**)&format1);
3903 if (hr == S_OK) {
3904 hr = IDWriteTextFormat1_SetTextAlignment(format1, DWRITE_TEXT_ALIGNMENT_CENTER);
3905 ok(hr == S_OK, "got 0x%08x\n", hr);
3907 v = IDWriteTextFormat_GetTextAlignment(format);
3908 ok(v == DWRITE_TEXT_ALIGNMENT_LEADING, "got %d\n", v);
3910 v = IDWriteTextLayout_GetTextAlignment(layout);
3911 ok(v == DWRITE_TEXT_ALIGNMENT_CENTER, "got %d\n", v);
3913 v = IDWriteTextFormat1_GetTextAlignment(format1);
3914 ok(v == DWRITE_TEXT_ALIGNMENT_CENTER, "got %d\n", v);
3916 IDWriteTextFormat1_Release(format1);
3918 else
3919 win_skip("IDWriteTextFormat1 is not supported\n");
3921 IDWriteTextLayout_Release(layout);
3923 for (i = 0; i < ARRAY_SIZE(stringsW); i++) {
3924 FLOAT text_width;
3926 hr = IDWriteTextFormat_SetTextAlignment(format, DWRITE_TEXT_ALIGNMENT_LEADING);
3927 ok(hr == S_OK, "got 0x%08x\n", hr);
3929 hr = IDWriteFactory_CreateTextLayout(factory, stringsW[i], lstrlenW(stringsW[i]), format, 500.0f, 100.0f, &layout);
3930 ok(hr == S_OK, "got 0x%08x\n", hr);
3932 hr = IDWriteTextLayout_SetWordWrapping(layout, DWRITE_WORD_WRAPPING_NO_WRAP);
3933 ok(hr == S_OK, "got 0x%08x\n", hr);
3935 count = 0;
3936 hr = IDWriteTextLayout_GetClusterMetrics(layout, clusters, ARRAY_SIZE(clusters), &count);
3937 ok(hr == S_OK, "got 0x%08x\n", hr);
3938 if (lstrlenW(stringsW[i]))
3939 ok(count > 0, "got %u\n", count);
3940 else
3941 ok(count == 0, "got %u\n", count);
3943 text_width = 0.0f;
3944 while (count)
3945 text_width += clusters[--count].width;
3947 /* maxwidth is 500, leading alignment */
3948 hr = IDWriteTextLayout_SetTextAlignment(layout, DWRITE_TEXT_ALIGNMENT_LEADING);
3949 ok(hr == S_OK, "got 0x%08x\n", hr);
3951 hr = IDWriteTextLayout_GetMetrics(layout, &metrics);
3952 ok(hr == S_OK, "got 0x%08x\n", hr);
3954 ok(metrics.left == 0.0f, "got %.2f\n", metrics.left);
3955 ok(metrics.width == text_width, "got %.2f\n", metrics.width);
3956 ok(metrics.layoutWidth == 500.0f, "got %.2f\n", metrics.layoutWidth);
3957 ok(metrics.lineCount == 1, "got %d\n", metrics.lineCount);
3959 /* maxwidth is 500, trailing alignment */
3960 hr = IDWriteTextLayout_SetTextAlignment(layout, DWRITE_TEXT_ALIGNMENT_TRAILING);
3961 ok(hr == S_OK, "got 0x%08x\n", hr);
3963 hr = IDWriteTextLayout_GetMetrics(layout, &metrics);
3964 ok(hr == S_OK, "got 0x%08x\n", hr);
3966 ok(metrics.left == metrics.layoutWidth - metrics.width, "got %.2f\n", metrics.left);
3967 ok(metrics.width == text_width, "got %.2f\n", metrics.width);
3968 ok(metrics.layoutWidth == 500.0f, "got %.2f\n", metrics.layoutWidth);
3969 ok(metrics.lineCount == 1, "got %d\n", metrics.lineCount);
3970 IDWriteTextLayout_Release(layout);
3972 /* initially created with trailing alignment */
3973 hr = IDWriteTextFormat_SetTextAlignment(format, DWRITE_TEXT_ALIGNMENT_TRAILING);
3974 ok(hr == S_OK, "got 0x%08x\n", hr);
3976 hr = IDWriteFactory_CreateTextLayout(factory, stringsW[i], lstrlenW(stringsW[i]), format, 500.0f, 100.0f, &layout);
3977 ok(hr == S_OK, "got 0x%08x\n", hr);
3979 hr = IDWriteTextLayout_GetMetrics(layout, &metrics);
3980 ok(hr == S_OK, "got 0x%08x\n", hr);
3982 ok(metrics.left == metrics.layoutWidth - metrics.width, "got %.2f\n", metrics.left);
3983 ok(metrics.width == text_width, "got %.2f\n", metrics.width);
3984 ok(metrics.layoutWidth == 500.0f, "got %.2f\n", metrics.layoutWidth);
3985 ok(metrics.lineCount == 1, "got %d\n", metrics.lineCount);
3986 IDWriteTextLayout_Release(layout);
3988 if (lstrlenW(stringsW[i]) > 0) {
3989 /* max width less than total run width, trailing alignment */
3990 hr = IDWriteTextFormat_SetWordWrapping(format, DWRITE_WORD_WRAPPING_NO_WRAP);
3991 ok(hr == S_OK, "got 0x%08x\n", hr);
3993 hr = IDWriteFactory_CreateTextLayout(factory, stringsW[i], lstrlenW(stringsW[i]), format, clusters[0].width, 100.0f, &layout);
3994 ok(hr == S_OK, "got 0x%08x\n", hr);
3995 hr = IDWriteTextLayout_GetMetrics(layout, &metrics);
3996 ok(hr == S_OK, "got 0x%08x\n", hr);
3997 ok(metrics.left == metrics.layoutWidth - metrics.width, "got %.2f\n", metrics.left);
3998 ok(metrics.width == text_width, "got %.2f\n", metrics.width);
3999 ok(metrics.lineCount == 1, "got %d\n", metrics.lineCount);
4000 IDWriteTextLayout_Release(layout);
4003 /* maxwidth is 500, centered */
4004 hr = IDWriteTextFormat_SetTextAlignment(format, DWRITE_TEXT_ALIGNMENT_CENTER);
4005 ok(hr == S_OK, "got 0x%08x\n", hr);
4007 hr = IDWriteFactory_CreateTextLayout(factory, stringsW[i], lstrlenW(stringsW[i]), format, 500.0f, 100.0f, &layout);
4008 ok(hr == S_OK, "got 0x%08x\n", hr);
4010 hr = IDWriteTextLayout_GetMetrics(layout, &metrics);
4011 ok(hr == S_OK, "got 0x%08x\n", hr);
4012 ok(metrics.left == (metrics.layoutWidth - metrics.width) / 2.0f, "got %.2f\n", metrics.left);
4013 ok(metrics.width == text_width, "got %.2f\n", metrics.width);
4014 ok(metrics.lineCount == 1, "got %d\n", metrics.lineCount);
4016 IDWriteTextLayout_Release(layout);
4019 IDWriteTextFormat_Release(format);
4020 IDWriteFactory_Release(factory);
4023 static void test_SetParagraphAlignment(void)
4025 static const WCHAR strW[] = {'a',0};
4026 DWRITE_TEXT_METRICS metrics;
4027 IDWriteTextFormat *format;
4028 IDWriteTextLayout *layout;
4029 IDWriteFactory *factory;
4030 DWRITE_PARAGRAPH_ALIGNMENT v;
4031 DWRITE_LINE_METRICS lines[1];
4032 UINT32 count;
4033 HRESULT hr;
4035 factory = create_factory();
4037 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
4038 DWRITE_FONT_STRETCH_NORMAL, 12.0, enusW, &format);
4039 ok(hr == S_OK, "got 0x%08x\n", hr);
4041 v = IDWriteTextFormat_GetParagraphAlignment(format);
4042 ok(v == DWRITE_PARAGRAPH_ALIGNMENT_NEAR, "got %d\n", v);
4044 hr = IDWriteFactory_CreateTextLayout(factory, strW, 1, format, 500.0, 100.0, &layout);
4045 ok(hr == S_OK, "got 0x%08x\n", hr);
4047 v = IDWriteTextLayout_GetParagraphAlignment(layout);
4048 ok(v == DWRITE_PARAGRAPH_ALIGNMENT_NEAR, "got %d\n", v);
4050 hr = IDWriteTextLayout_SetParagraphAlignment(layout, DWRITE_PARAGRAPH_ALIGNMENT_FAR);
4051 ok(hr == S_OK, "got 0x%08x\n", hr);
4053 hr = IDWriteTextLayout_SetParagraphAlignment(layout, DWRITE_PARAGRAPH_ALIGNMENT_FAR);
4054 ok(hr == S_OK, "got 0x%08x\n", hr);
4056 v = IDWriteTextFormat_GetParagraphAlignment(format);
4057 ok(v == DWRITE_PARAGRAPH_ALIGNMENT_NEAR, "got %d\n", v);
4059 v = IDWriteTextLayout_GetParagraphAlignment(layout);
4060 ok(v == DWRITE_PARAGRAPH_ALIGNMENT_FAR, "got %d\n", v);
4062 hr = IDWriteTextLayout_SetParagraphAlignment(layout, DWRITE_PARAGRAPH_ALIGNMENT_CENTER);
4063 ok(hr == S_OK, "got 0x%08x\n", hr);
4065 v = IDWriteTextLayout_GetParagraphAlignment(layout);
4066 ok(v == DWRITE_PARAGRAPH_ALIGNMENT_CENTER, "got %d\n", v);
4068 count = 0;
4069 hr = IDWriteTextLayout_GetLineMetrics(layout, lines, 1, &count);
4070 ok(hr == S_OK, "got 0x%08x\n", hr);
4071 ok(count == 1, "got %u\n", count);
4073 /* maxheight is 100, near alignment */
4074 hr = IDWriteTextLayout_SetParagraphAlignment(layout, DWRITE_PARAGRAPH_ALIGNMENT_NEAR);
4075 ok(hr == S_OK, "got 0x%08x\n", hr);
4077 hr = IDWriteTextLayout_GetMetrics(layout, &metrics);
4078 ok(hr == S_OK, "got 0x%08x\n", hr);
4080 ok(metrics.top == 0.0, "got %.2f\n", metrics.top);
4081 ok(metrics.height == lines[0].height, "got %.2f\n", metrics.height);
4082 ok(metrics.layoutHeight == 100.0, "got %.2f\n", metrics.layoutHeight);
4083 ok(metrics.lineCount == 1, "got %d\n", metrics.lineCount);
4085 /* maxwidth is 100, far alignment */
4086 hr = IDWriteTextLayout_SetParagraphAlignment(layout, DWRITE_PARAGRAPH_ALIGNMENT_FAR);
4087 ok(hr == S_OK, "got 0x%08x\n", hr);
4089 hr = IDWriteTextLayout_GetMetrics(layout, &metrics);
4090 ok(hr == S_OK, "got 0x%08x\n", hr);
4092 ok(metrics.top == metrics.layoutHeight - metrics.height, "got %.2f\n", metrics.top);
4093 ok(metrics.height == lines[0].height, "got %.2f\n", metrics.height);
4094 ok(metrics.layoutHeight == 100.0, "got %.2f\n", metrics.layoutHeight);
4095 ok(metrics.lineCount == 1, "got %d\n", metrics.lineCount);
4096 IDWriteTextLayout_Release(layout);
4098 /* initially created with centered alignment */
4099 hr = IDWriteTextFormat_SetParagraphAlignment(format, DWRITE_PARAGRAPH_ALIGNMENT_CENTER);
4100 ok(hr == S_OK, "got 0x%08x\n", hr);
4102 hr = IDWriteFactory_CreateTextLayout(factory, strW, 1, format, 500.0, 100.0, &layout);
4103 ok(hr == S_OK, "got 0x%08x\n", hr);
4105 hr = IDWriteTextLayout_GetMetrics(layout, &metrics);
4106 ok(hr == S_OK, "got 0x%08x\n", hr);
4108 ok(metrics.top == (metrics.layoutHeight - lines[0].height) / 2, "got %.2f\n", metrics.top);
4109 ok(metrics.height == lines[0].height, "got %.2f\n", metrics.height);
4110 ok(metrics.lineCount == 1, "got %d\n", metrics.lineCount);
4111 IDWriteTextLayout_Release(layout);
4113 IDWriteTextFormat_Release(format);
4114 IDWriteFactory_Release(factory);
4117 static void test_SetReadingDirection(void)
4119 static const WCHAR strW[] = {'a',0};
4120 DWRITE_CLUSTER_METRICS clusters[1];
4121 DWRITE_TEXT_METRICS metrics;
4122 IDWriteTextFormat *format;
4123 IDWriteTextLayout *layout;
4124 IDWriteFactory *factory;
4125 DWRITE_READING_DIRECTION v;
4126 DWRITE_LINE_METRICS lines[1];
4127 UINT32 count;
4128 HRESULT hr;
4130 factory = create_factory();
4132 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
4133 DWRITE_FONT_STRETCH_NORMAL, 12.0, enusW, &format);
4134 ok(hr == S_OK, "got 0x%08x\n", hr);
4136 v = IDWriteTextFormat_GetReadingDirection(format);
4137 ok(v == DWRITE_READING_DIRECTION_LEFT_TO_RIGHT, "got %d\n", v);
4139 hr = IDWriteFactory_CreateTextLayout(factory, strW, 1, format, 500.0, 100.0, &layout);
4140 ok(hr == S_OK, "got 0x%08x\n", hr);
4142 v = IDWriteTextLayout_GetReadingDirection(layout);
4143 ok(v == DWRITE_READING_DIRECTION_LEFT_TO_RIGHT, "got %d\n", v);
4145 v = IDWriteTextFormat_GetReadingDirection(format);
4146 ok(v == DWRITE_READING_DIRECTION_LEFT_TO_RIGHT, "got %d\n", v);
4148 hr = IDWriteTextLayout_SetReadingDirection(layout, DWRITE_READING_DIRECTION_RIGHT_TO_LEFT);
4149 ok(hr == S_OK, "got 0x%08x\n", hr);
4151 count = 0;
4152 hr = IDWriteTextLayout_GetLineMetrics(layout, lines, 1, &count);
4153 ok(hr == S_OK, "got 0x%08x\n", hr);
4154 ok(count == 1, "got %u\n", count);
4156 count = 0;
4157 hr = IDWriteTextLayout_GetClusterMetrics(layout, clusters, 1, &count);
4158 ok(hr == S_OK, "got 0x%08x\n", hr);
4159 ok(count == 1, "got %u\n", count);
4161 /* leading alignment, RTL */
4162 hr = IDWriteTextLayout_GetMetrics(layout, &metrics);
4163 ok(hr == S_OK, "got 0x%08x\n", hr);
4165 ok(metrics.left == metrics.layoutWidth - clusters[0].width, "got %.2f\n", metrics.left);
4166 ok(metrics.top == 0.0, "got %.2f\n", metrics.top);
4167 ok(metrics.width == clusters[0].width, "got %.2f\n", metrics.width);
4168 ok(metrics.height == lines[0].height, "got %.2f\n", metrics.height);
4169 ok(metrics.layoutWidth == 500.0, "got %.2f\n", metrics.layoutWidth);
4170 ok(metrics.layoutHeight == 100.0, "got %.2f\n", metrics.layoutHeight);
4171 ok(metrics.lineCount == 1, "got %d\n", metrics.lineCount);
4173 /* trailing alignment, RTL */
4174 hr = IDWriteTextLayout_SetTextAlignment(layout, DWRITE_TEXT_ALIGNMENT_TRAILING);
4175 ok(hr == S_OK, "got 0x%08x\n", hr);
4177 hr = IDWriteTextLayout_GetMetrics(layout, &metrics);
4178 ok(hr == S_OK, "got 0x%08x\n", hr);
4180 ok(metrics.left == 0.0, "got %.2f\n", metrics.left);
4181 ok(metrics.top == 0.0, "got %.2f\n", metrics.top);
4182 ok(metrics.width == clusters[0].width, "got %.2f\n", metrics.width);
4183 ok(metrics.height == lines[0].height, "got %.2f\n", metrics.height);
4184 ok(metrics.layoutWidth == 500.0, "got %.2f\n", metrics.layoutWidth);
4185 ok(metrics.layoutHeight == 100.0, "got %.2f\n", metrics.layoutHeight);
4186 ok(metrics.lineCount == 1, "got %d\n", metrics.lineCount);
4188 /* centered alignment, RTL */
4189 hr = IDWriteTextLayout_SetTextAlignment(layout, DWRITE_TEXT_ALIGNMENT_CENTER);
4190 ok(hr == S_OK, "got 0x%08x\n", hr);
4192 hr = IDWriteTextLayout_GetMetrics(layout, &metrics);
4193 ok(hr == S_OK, "got 0x%08x\n", hr);
4195 ok(metrics.left == (metrics.layoutWidth - clusters[0].width) / 2.0, "got %.2f\n", metrics.left);
4196 ok(metrics.top == 0.0, "got %.2f\n", metrics.top);
4197 ok(metrics.width == clusters[0].width, "got %.2f\n", metrics.width);
4198 ok(metrics.height == lines[0].height, "got %.2f\n", metrics.height);
4199 ok(metrics.layoutWidth == 500.0, "got %.2f\n", metrics.layoutWidth);
4200 ok(metrics.layoutHeight == 100.0, "got %.2f\n", metrics.layoutHeight);
4201 ok(metrics.lineCount == 1, "got %d\n", metrics.lineCount);
4203 IDWriteTextLayout_Release(layout);
4205 IDWriteTextFormat_Release(format);
4206 IDWriteFactory_Release(factory);
4209 static inline FLOAT get_scaled_font_metric(UINT32 metric, FLOAT emSize, const DWRITE_FONT_METRICS *metrics)
4211 return (FLOAT)metric * emSize / (FLOAT)metrics->designUnitsPerEm;
4214 static FLOAT snap_coord(const DWRITE_MATRIX *m, FLOAT ppdip, FLOAT coord)
4216 FLOAT vec[2], det, vec2[2];
4217 BOOL transform;
4219 /* has to be a diagonal matrix */
4220 if ((ppdip <= 0.0) ||
4221 (m->m11 * m->m22 != 0.0 && (m->m12 != 0.0 || m->m21 != 0.0)) ||
4222 (m->m12 * m->m21 != 0.0 && (m->m11 != 0.0 || m->m22 != 0.0)))
4223 return coord;
4225 det = m->m11 * m->m22 - m->m12 * m->m21;
4226 transform = fabsf(det) > 1e-10;
4228 if (transform) {
4229 /* apply transform */
4230 vec[0] = 0.0;
4231 vec[1] = coord * ppdip;
4233 vec2[0] = m->m11 * vec[0] + m->m21 * vec[1] + m->dx;
4234 vec2[1] = m->m12 * vec[0] + m->m22 * vec[1] + m->dy;
4236 /* snap */
4237 vec2[0] = floorf(vec2[0] + 0.5f);
4238 vec2[1] = floorf(vec2[1] + 0.5f);
4240 /* apply inverted transform */
4241 vec[1] = (-m->m12 * vec2[0] + m->m11 * vec2[1] - (m->m11 * m->dy - m->m12 * m->dx)) / det;
4242 vec[1] /= ppdip;
4244 else
4245 vec[1] = floorf(coord * ppdip + 0.5f) / ppdip;
4246 return vec[1];
4249 static inline BOOL float_eq(FLOAT left, FLOAT right)
4251 int x = *(int *)&left;
4252 int y = *(int *)&right;
4254 if (x < 0)
4255 x = INT_MIN - x;
4256 if (y < 0)
4257 y = INT_MIN - y;
4259 return abs(x - y) <= 16;
4262 struct snapping_test {
4263 DWRITE_MATRIX m;
4264 FLOAT ppdip;
4267 static struct snapping_test snapping_tests[] = {
4268 { { 0.0, 1.0, 2.0, 0.0, 0.2, 0.3 }, 1.0 },
4269 { { 0.0, 1.0, 2.0, 0.0, 0.0, 0.0 }, 1.0 },
4270 { { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }, 1.0 }, /* identity transform */
4271 { { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }, 0.9 },
4272 { { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }, -1.0 },
4273 { { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }, 0.0 },
4274 { { 1.0, 0.0, 0.0, 1.0, 0.0, 0.3 }, 1.0 }, /* simple Y shift */
4275 { { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }, 10.0 }, /* identity, 10 ppdip */
4276 { { 1.0, 0.0, 0.0, 10.0, 0.0, 0.0 }, 10.0 },
4277 { { 0.0, 1.0, 1.0, 0.0, 0.2, 0.6 }, 1.0 },
4278 { { 0.0, 2.0, 2.0, 0.0, 0.2, 0.6 }, 1.0 },
4279 { { 0.0, 0.5, -0.5, 0.0, 0.2, 0.6 }, 1.0 },
4280 { { 1.0, 2.0, 0.0, 1.0, 0.2, 0.6 }, 1.0 },
4281 { { 1.0, 1.0, 0.0, 1.0, 0.2, 0.6 }, 1.0 },
4282 { { 0.5, 0.5, -0.5, 0.5, 0.2, 0.6 }, 1.0 }, /* 45 degrees rotation */
4283 { { 0.5, 0.5, -0.5, 0.5, 0.0, 0.0 }, 100.0 }, /* 45 degrees rotation */
4284 { { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }, 100.0 },
4285 { { 0.0, 1.0, -1.0, 0.0, 0.2, 0.6 }, 1.0 }, /* 90 degrees rotation */
4286 { { -1.0, 0.0, 0.0, -1.0, 0.2, 0.6 }, 1.0 }, /* 180 degrees rotation */
4287 { { 0.0, -1.0, 1.0, 0.0, 0.2, 0.6 }, 1.0 }, /* 270 degrees rotation */
4288 { { 1.0, 0.0, 0.0, 1.0,-0.1, 0.2 }, 1.0 },
4289 { { 0.0, 1.0, -1.0, 0.0,-0.2,-0.3 }, 1.0 }, /* 90 degrees rotation */
4290 { { -1.0, 0.0, 0.0, -1.0,-0.3,-1.6 }, 1.0 }, /* 180 degrees rotation */
4291 { { 0.0, -1.0, 1.0, 0.0,-0.7, 0.6 }, 10.0 }, /* 270 degrees rotation */
4292 { { 0.0, 2.0, 1.0, 0.0, 0.2, 0.6 }, 1.0 },
4293 { { 0.0, 0.0, 1.0, 0.0, 0.0, 0.0 }, 1.0 },
4294 { { 3.0, 0.0, 0.0, 5.0, 0.2,-0.3 }, 10.0 },
4295 { { 0.0, -3.0, 5.0, 0.0,-0.1, 0.7 }, 10.0 },
4298 static DWRITE_MATRIX compattransforms[] = {
4299 { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 },
4300 { 1.0, 0.0, 0.0, 1.0, 0.2, 0.3 },
4301 { 2.0, 0.0, 0.0, 2.0, 0.2, 0.3 },
4302 { 2.0, 1.0, 2.0, 2.0, 0.2, 0.3 },
4305 static void test_pixelsnapping(void)
4307 static const WCHAR strW[] = {'a',0};
4308 IDWriteTextLayout *layout, *layout2;
4309 struct renderer_context ctxt;
4310 DWRITE_FONT_METRICS metrics;
4311 IDWriteTextFormat *format;
4312 IDWriteFontFace *fontface;
4313 IDWriteFactory *factory;
4314 FLOAT baseline, originX;
4315 HRESULT hr;
4316 int i, j;
4318 factory = create_factory();
4320 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
4321 DWRITE_FONT_STRETCH_NORMAL, 12.0, enusW, &format);
4322 ok(hr == S_OK, "got 0x%08x\n", hr);
4324 fontface = get_fontface_from_format(format);
4325 IDWriteFontFace_GetMetrics(fontface, &metrics);
4327 hr = IDWriteFactory_CreateTextLayout(factory, strW, 1, format, 500.0, 100.0, &layout);
4328 ok(hr == S_OK, "got 0x%08x\n", hr);
4330 /* disabled snapping */
4331 ctxt.snapping_disabled = TRUE;
4332 ctxt.gdicompat = FALSE;
4333 ctxt.use_gdi_natural = FALSE;
4334 ctxt.ppdip = 1.0f;
4335 memset(&ctxt.m, 0, sizeof(ctxt.m));
4336 ctxt.m.m11 = ctxt.m.m22 = 1.0;
4337 originX = 0.1;
4339 hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, originX, 0.0);
4340 ok(hr == S_OK, "got 0x%08x\n", hr);
4342 baseline = get_scaled_font_metric(metrics.ascent, 12.0, &metrics);
4343 ok(ctxt.originX == originX, "got %f, originX %f\n", ctxt.originX, originX);
4344 ok(ctxt.originY == baseline, "got %f, baseline %f\n", ctxt.originY, baseline);
4345 ok(floor(baseline) != baseline, "got %f\n", baseline);
4347 ctxt.snapping_disabled = FALSE;
4349 for (i = 0; i < ARRAY_SIZE(snapping_tests); i++) {
4350 struct snapping_test *ptr = &snapping_tests[i];
4351 FLOAT expectedY;
4353 ctxt.m = ptr->m;
4354 ctxt.ppdip = ptr->ppdip;
4355 ctxt.originX = 678.9;
4356 ctxt.originY = 678.9;
4358 expectedY = snap_coord(&ctxt.m, ctxt.ppdip, baseline);
4359 hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, originX, 0.0);
4360 ok(hr == S_OK, "%d: got 0x%08x\n", i, hr);
4361 ok(ctxt.originX == originX, "%d: got %f, originX %f\n", i, ctxt.originX, originX);
4362 ok(float_eq(ctxt.originY, expectedY), "%d: got %f, expected %f, baseline %f\n",
4363 i, ctxt.originY, expectedY, baseline);
4365 /* gdicompat layout transform doesn't affect snapping */
4366 for (j = 0; j < ARRAY_SIZE(compattransforms); j++) {
4367 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, strW, 1, format, 500.0, 100.0,
4368 1.0, &compattransforms[j], FALSE, &layout2);
4369 ok(hr == S_OK, "%d: got 0x%08x\n", i, hr);
4371 expectedY = snap_coord(&ctxt.m, ctxt.ppdip, baseline);
4372 hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, originX, 0.0);
4373 ok(hr == S_OK, "%d: got 0x%08x\n", i, hr);
4374 ok(ctxt.originX == originX, "%d: got %f, originX %f\n", i, ctxt.originX, originX);
4375 ok(float_eq(ctxt.originY, expectedY), "%d: got %f, expected %f, baseline %f\n",
4376 i, ctxt.originY, expectedY, baseline);
4378 IDWriteTextLayout_Release(layout2);
4382 IDWriteTextLayout_Release(layout);
4383 IDWriteTextFormat_Release(format);
4384 IDWriteFontFace_Release(fontface);
4385 IDWriteFactory_Release(factory);
4388 static void test_SetWordWrapping(void)
4390 static const WCHAR strW[] = {'a',' ','s','o','m','e',' ','t','e','x','t',' ','a','n','d',
4391 ' ','a',' ','b','i','t',' ','m','o','r','e','\n','b'};
4392 IDWriteTextFormat *format;
4393 IDWriteTextLayout *layout;
4394 IDWriteFactory *factory;
4395 DWRITE_WORD_WRAPPING v;
4396 UINT32 count;
4397 HRESULT hr;
4399 factory = create_factory();
4401 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
4402 DWRITE_FONT_STRETCH_NORMAL, 12.0, enusW, &format);
4403 ok(hr == S_OK, "got 0x%08x\n", hr);
4405 v = IDWriteTextFormat_GetWordWrapping(format);
4406 ok(v == DWRITE_WORD_WRAPPING_WRAP, "got %d\n", v);
4408 hr = IDWriteFactory_CreateTextLayout(factory, strW, ARRAY_SIZE(strW), format, 10.0f, 100.0f, &layout);
4409 ok(hr == S_OK, "got 0x%08x\n", hr);
4411 v = IDWriteTextLayout_GetWordWrapping(layout);
4412 ok(v == DWRITE_WORD_WRAPPING_WRAP, "got %d\n", v);
4414 hr = IDWriteTextLayout_SetWordWrapping(layout, DWRITE_WORD_WRAPPING_NO_WRAP);
4415 ok(hr == S_OK, "got 0x%08x\n", hr);
4417 hr = IDWriteTextLayout_SetWordWrapping(layout, DWRITE_WORD_WRAPPING_NO_WRAP);
4418 ok(hr == S_OK, "got 0x%08x\n", hr);
4420 v = IDWriteTextFormat_GetWordWrapping(format);
4421 ok(v == DWRITE_WORD_WRAPPING_WRAP, "got %d\n", v);
4423 /* disable wrapping, text has explicit newline */
4424 hr = IDWriteTextLayout_SetWordWrapping(layout, DWRITE_WORD_WRAPPING_NO_WRAP);
4425 ok(hr == S_OK, "got 0x%08x\n", hr);
4427 count = 0;
4428 hr = IDWriteTextLayout_GetLineMetrics(layout, NULL, 0, &count);
4429 ok(hr == E_NOT_SUFFICIENT_BUFFER, "got 0x%08x\n", hr);
4430 ok(count == 2, "got %u\n", count);
4432 hr = IDWriteTextLayout_SetWordWrapping(layout, DWRITE_WORD_WRAPPING_WRAP);
4433 ok(hr == S_OK, "got 0x%08x\n", hr);
4435 count = 0;
4436 hr = IDWriteTextLayout_GetLineMetrics(layout, NULL, 0, &count);
4437 ok(hr == E_NOT_SUFFICIENT_BUFFER, "got 0x%08x\n", hr);
4438 ok(count > 2, "got %u\n", count);
4440 IDWriteTextLayout_Release(layout);
4441 IDWriteTextFormat_Release(format);
4442 IDWriteFactory_Release(factory);
4445 /* Collection dedicated to fallback testing */
4447 static const WCHAR g_blahfontW[] = {'B','l','a','h',0};
4448 static HRESULT WINAPI fontcollection_QI(IDWriteFontCollection *iface, REFIID riid, void **obj)
4450 if (IsEqualIID(riid, &IID_IDWriteFontCollection) || IsEqualIID(riid, &IID_IUnknown)) {
4451 *obj = iface;
4452 IDWriteFontCollection_AddRef(iface);
4453 return S_OK;
4456 *obj = NULL;
4457 return E_NOINTERFACE;
4460 static ULONG WINAPI fontcollection_AddRef(IDWriteFontCollection *iface)
4462 return 2;
4465 static ULONG WINAPI fontcollection_Release(IDWriteFontCollection *iface)
4467 return 1;
4470 static UINT32 WINAPI fontcollection_GetFontFamilyCount(IDWriteFontCollection *iface)
4472 ok(0, "unexpected call\n");
4473 return 0;
4476 static HRESULT WINAPI fontcollection_GetFontFamily(IDWriteFontCollection *iface, UINT32 index, IDWriteFontFamily **family)
4478 if (index == 123456) {
4479 IDWriteFactory *factory = create_factory();
4480 IDWriteFontCollection *syscollection;
4481 BOOL exists;
4482 HRESULT hr;
4484 hr = IDWriteFactory_GetSystemFontCollection(factory, &syscollection, FALSE);
4485 ok(hr == S_OK, "got 0x%08x\n", hr);
4487 hr = IDWriteFontCollection_FindFamilyName(syscollection, tahomaW, &index, &exists);
4488 ok(hr == S_OK, "got 0x%08x\n", hr);
4490 hr = IDWriteFontCollection_GetFontFamily(syscollection, index, family);
4491 ok(hr == S_OK, "got 0x%08x\n", hr);
4493 IDWriteFontCollection_Release(syscollection);
4494 IDWriteFactory_Release(factory);
4495 return S_OK;
4497 ok(0, "unexpected call\n");
4498 return E_NOTIMPL;
4501 static HRESULT WINAPI fontcollection_FindFamilyName(IDWriteFontCollection *iface, WCHAR const *name, UINT32 *index, BOOL *exists)
4503 if (!lstrcmpW(name, g_blahfontW)) {
4504 *index = 123456;
4505 *exists = TRUE;
4506 return S_OK;
4508 ok(0, "unexpected call, name %s\n", wine_dbgstr_w(name));
4509 return E_NOTIMPL;
4512 static HRESULT WINAPI fontcollection_GetFontFromFontFace(IDWriteFontCollection *iface, IDWriteFontFace *face, IDWriteFont **font)
4514 ok(0, "unexpected call\n");
4515 return E_NOTIMPL;
4518 static const IDWriteFontCollectionVtbl fallbackcollectionvtbl = {
4519 fontcollection_QI,
4520 fontcollection_AddRef,
4521 fontcollection_Release,
4522 fontcollection_GetFontFamilyCount,
4523 fontcollection_GetFontFamily,
4524 fontcollection_FindFamilyName,
4525 fontcollection_GetFontFromFontFace
4528 static IDWriteFontCollection fallbackcollection = { &fallbackcollectionvtbl };
4530 static void test_MapCharacters(void)
4532 static const WCHAR strW[] = {'a','b','c',0};
4533 static const WCHAR str2W[] = {'a',0x3058,'b',0};
4534 IDWriteLocalizedStrings *strings;
4535 IDWriteFontFallback *fallback;
4536 IDWriteFactory2 *factory2;
4537 IDWriteFactory *factory;
4538 UINT32 mappedlength;
4539 IDWriteFont *font;
4540 WCHAR buffW[50];
4541 BOOL exists;
4542 FLOAT scale;
4543 HRESULT hr;
4545 factory = create_factory();
4547 hr = IDWriteFactory_QueryInterface(factory, &IID_IDWriteFactory2, (void**)&factory2);
4548 IDWriteFactory_Release(factory);
4549 if (hr != S_OK) {
4550 win_skip("MapCharacters() is not supported\n");
4551 return;
4554 fallback = NULL;
4555 hr = IDWriteFactory2_GetSystemFontFallback(factory2, &fallback);
4556 ok(hr == S_OK, "got 0x%08x\n", hr);
4557 ok(fallback != NULL, "got %p\n", fallback);
4559 mappedlength = 1;
4560 scale = 0.0f;
4561 font = (void*)0xdeadbeef;
4562 hr = IDWriteFontFallback_MapCharacters(fallback, NULL, 0, 0, NULL, NULL, DWRITE_FONT_WEIGHT_NORMAL,
4563 DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, &mappedlength, &font, &scale);
4564 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
4565 ok(mappedlength == 0, "got %u\n", mappedlength);
4566 ok(scale == 1.0f, "got %f\n", scale);
4567 ok(font == NULL, "got %p\n", font);
4569 /* zero length source */
4570 g_source = strW;
4571 mappedlength = 1;
4572 scale = 0.0f;
4573 font = (void*)0xdeadbeef;
4574 hr = IDWriteFontFallback_MapCharacters(fallback, &analysissource, 0, 0, NULL, NULL, DWRITE_FONT_WEIGHT_NORMAL,
4575 DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, &mappedlength, &font, &scale);
4576 ok(hr == S_OK, "got 0x%08x\n", hr);
4577 ok(mappedlength == 0, "got %u\n", mappedlength);
4578 ok(scale == 1.0f, "got %f\n", scale);
4579 ok(font == NULL, "got %p\n", font);
4581 g_source = strW;
4582 mappedlength = 0;
4583 scale = 0.0f;
4584 font = NULL;
4585 hr = IDWriteFontFallback_MapCharacters(fallback, &analysissource, 0, 1, NULL, NULL, DWRITE_FONT_WEIGHT_NORMAL,
4586 DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, &mappedlength, &font, &scale);
4587 todo_wine {
4588 ok(hr == S_OK, "got 0x%08x\n", hr);
4589 ok(mappedlength == 1, "got %u\n", mappedlength);
4591 ok(scale == 1.0f, "got %f\n", scale);
4592 todo_wine
4593 ok(font != NULL, "got %p\n", font);
4594 if (font) {
4595 IDWriteFont_Release(font);
4597 /* same latin text, full length */
4598 g_source = strW;
4599 mappedlength = 0;
4600 scale = 0.0f;
4601 font = NULL;
4602 hr = IDWriteFontFallback_MapCharacters(fallback, &analysissource, 0, 3, NULL, NULL, DWRITE_FONT_WEIGHT_NORMAL,
4603 DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, &mappedlength, &font, &scale);
4604 todo_wine {
4605 ok(hr == S_OK, "got 0x%08x\n", hr);
4606 ok(mappedlength == 3, "got %u\n", mappedlength);
4608 ok(scale == 1.0f, "got %f\n", scale);
4609 todo_wine
4610 ok(font != NULL, "got %p\n", font);
4611 if (font) {
4612 IDWriteFont_Release(font);
4614 /* string 'a\x3058b' */
4615 g_source = str2W;
4616 mappedlength = 0;
4617 scale = 0.0f;
4618 font = NULL;
4619 hr = IDWriteFontFallback_MapCharacters(fallback, &analysissource, 0, 3, NULL, NULL, DWRITE_FONT_WEIGHT_NORMAL,
4620 DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, &mappedlength, &font, &scale);
4621 todo_wine {
4622 ok(hr == S_OK, "got 0x%08x\n", hr);
4623 ok(mappedlength == 1, "got %u\n", mappedlength);
4625 ok(scale == 1.0f, "got %f\n", scale);
4626 todo_wine
4627 ok(font != NULL, "got %p\n", font);
4628 if (font) {
4629 IDWriteFont_Release(font);
4631 g_source = str2W;
4632 mappedlength = 0;
4633 scale = 0.0f;
4634 font = NULL;
4635 hr = IDWriteFontFallback_MapCharacters(fallback, &analysissource, 1, 2, NULL, NULL, DWRITE_FONT_WEIGHT_NORMAL,
4636 DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, &mappedlength, &font, &scale);
4637 todo_wine {
4638 ok(hr == S_OK, "got 0x%08x\n", hr);
4639 ok(mappedlength == 1, "got %u\n", mappedlength);
4641 ok(scale == 1.0f, "got %f\n", scale);
4642 todo_wine
4643 ok(font != NULL, "got %p\n", font);
4644 if (font) {
4645 /* font returned for Hiragana character, check if it supports Latin too */
4646 exists = FALSE;
4647 hr = IDWriteFont_HasCharacter(font, 'b', &exists);
4648 ok(hr == S_OK, "got 0x%08x\n", hr);
4649 ok(exists, "got %d\n", exists);
4651 IDWriteFont_Release(font);
4653 /* Try with explicit collection, Tahoma will be forced. */
4654 /* 1. Latin part */
4655 g_source = str2W;
4656 mappedlength = 0;
4657 scale = 0.0f;
4658 font = NULL;
4659 hr = IDWriteFontFallback_MapCharacters(fallback, &analysissource, 0, 3, &fallbackcollection, g_blahfontW, DWRITE_FONT_WEIGHT_NORMAL,
4660 DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, &mappedlength, &font, &scale);
4661 ok(hr == S_OK, "got 0x%08x\n", hr);
4662 ok(mappedlength == 1, "got %u\n", mappedlength);
4663 ok(scale == 1.0f, "got %f\n", scale);
4664 ok(font != NULL, "got %p\n", font);
4666 exists = FALSE;
4667 hr = IDWriteFont_GetInformationalStrings(font, DWRITE_INFORMATIONAL_STRING_WIN32_FAMILY_NAMES, &strings, &exists);
4668 ok(hr == S_OK && exists, "got 0x%08x, exists %d\n", hr, exists);
4669 hr = IDWriteLocalizedStrings_GetString(strings, 0, buffW, ARRAY_SIZE(buffW));
4670 ok(hr == S_OK, "got 0x%08x\n", hr);
4671 ok(!lstrcmpW(buffW, tahomaW), "%s\n", wine_dbgstr_w(buffW));
4672 IDWriteLocalizedStrings_Release(strings);
4673 IDWriteFont_Release(font);
4675 /* 2. Hiragana character, force Tahoma font does not support Japanese */
4676 g_source = str2W;
4677 mappedlength = 0;
4678 scale = 0.0f;
4679 font = NULL;
4680 hr = IDWriteFontFallback_MapCharacters(fallback, &analysissource, 1, 1, &fallbackcollection, g_blahfontW, DWRITE_FONT_WEIGHT_NORMAL,
4681 DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, &mappedlength, &font, &scale);
4682 ok(hr == S_OK, "got 0x%08x\n", hr);
4683 ok(mappedlength == 1, "got %u\n", mappedlength);
4684 ok(scale == 1.0f, "got %f\n", scale);
4685 ok(font != NULL, "got %p\n", font);
4687 exists = FALSE;
4688 hr = IDWriteFont_GetInformationalStrings(font, DWRITE_INFORMATIONAL_STRING_WIN32_FAMILY_NAMES, &strings, &exists);
4689 ok(hr == S_OK && exists, "got 0x%08x, exists %d\n", hr, exists);
4690 hr = IDWriteLocalizedStrings_GetString(strings, 0, buffW, ARRAY_SIZE(buffW));
4691 ok(hr == S_OK, "got 0x%08x\n", hr);
4692 todo_wine
4693 ok(lstrcmpW(buffW, tahomaW), "%s\n", wine_dbgstr_w(buffW));
4694 IDWriteLocalizedStrings_Release(strings);
4695 IDWriteFont_Release(font);
4697 IDWriteFontFallback_Release(fallback);
4698 IDWriteFactory2_Release(factory2);
4701 static void test_FontFallbackBuilder(void)
4703 static const WCHAR localeW[] = {'l','o','c','a','l','e',0};
4704 static const WCHAR strW[] = {'A',0};
4705 IDWriteFontFallback *fallback, *fallback2;
4706 IDWriteFontFallbackBuilder *builder;
4707 DWRITE_UNICODE_RANGE range;
4708 IDWriteFactory2 *factory2;
4709 IDWriteFactory *factory;
4710 const WCHAR *familyW;
4711 UINT32 mappedlength;
4712 IDWriteFont *font;
4713 FLOAT scale;
4714 HRESULT hr;
4715 ULONG ref;
4717 factory = create_factory();
4719 hr = IDWriteFactory_QueryInterface(factory, &IID_IDWriteFactory2, (void**)&factory2);
4720 IDWriteFactory_Release(factory);
4722 if (hr != S_OK) {
4723 win_skip("IDWriteFontFallbackBuilder is not supported\n");
4724 return;
4727 EXPECT_REF(factory2, 1);
4728 hr = IDWriteFactory2_CreateFontFallbackBuilder(factory2, &builder);
4729 EXPECT_REF(factory2, 2);
4731 fallback = NULL;
4732 EXPECT_REF(factory2, 2);
4733 EXPECT_REF(builder, 1);
4734 hr = IDWriteFontFallbackBuilder_CreateFontFallback(builder, &fallback);
4735 ok(hr == S_OK, "got 0x%08x\n", hr);
4736 EXPECT_REF(factory2, 3);
4737 EXPECT_REF(fallback, 1);
4738 EXPECT_REF(builder, 1);
4740 IDWriteFontFallback_AddRef(fallback);
4741 EXPECT_REF(builder, 1);
4742 EXPECT_REF(fallback, 2);
4743 EXPECT_REF(factory2, 3);
4744 IDWriteFontFallback_Release(fallback);
4746 /* New instance is created every time, even if mappings have not changed. */
4747 hr = IDWriteFontFallbackBuilder_CreateFontFallback(builder, &fallback2);
4748 ok(hr == S_OK, "Failed to create fallback object, hr %#x.\n", hr);
4749 ok(fallback != fallback2, "Unexpected fallback instance.\n");
4750 IDWriteFontFallback_Release(fallback2);
4752 hr = IDWriteFontFallbackBuilder_AddMapping(builder, NULL, 0, NULL, 0, NULL, NULL, NULL, 0.0f);
4753 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
4755 range.first = 'A';
4756 range.last = 'B';
4757 hr = IDWriteFontFallbackBuilder_AddMapping(builder, &range, 0, NULL, 0, NULL, NULL, NULL, 0.0f);
4758 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
4760 hr = IDWriteFontFallbackBuilder_AddMapping(builder, &range, 0, NULL, 0, NULL, NULL, NULL, 1.0f);
4761 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
4763 hr = IDWriteFontFallbackBuilder_AddMapping(builder, &range, 0, &familyW, 1, NULL, NULL, NULL, 1.0f);
4764 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
4766 hr = IDWriteFontFallbackBuilder_AddMapping(builder, NULL, 0, &familyW, 1, NULL, NULL, NULL, 1.0f);
4767 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
4769 /* negative scaling factor */
4770 range.first = range.last = 0;
4771 familyW = g_blahfontW;
4772 hr = IDWriteFontFallbackBuilder_AddMapping(builder, &range, 1, &familyW, 1, NULL, NULL, NULL, -1.0f);
4773 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
4775 hr = IDWriteFontFallbackBuilder_AddMapping(builder, &range, 1, &familyW, 1, NULL, NULL, NULL, 0.0f);
4776 ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
4778 /* empty range */
4779 range.first = range.last = 0;
4780 familyW = g_blahfontW;
4781 hr = IDWriteFontFallbackBuilder_AddMapping(builder, &range, 1, &familyW, 1, NULL, NULL, NULL, 1.0f);
4782 ok(hr == S_OK, "got 0x%08x\n", hr);
4784 range.first = range.last = 0;
4785 hr = IDWriteFontFallbackBuilder_AddMapping(builder, &range, 1, &familyW, 1, NULL, NULL, NULL, 2.0f);
4786 ok(hr == S_OK, "got 0x%08x\n", hr);
4788 range.first = range.last = 'A';
4789 hr = IDWriteFontFallbackBuilder_AddMapping(builder, &range, 1, &familyW, 1, NULL, NULL, NULL, 3.0f);
4790 ok(hr == S_OK, "got 0x%08x\n", hr);
4792 range.first = 'B';
4793 range.last = 'A';
4794 hr = IDWriteFontFallbackBuilder_AddMapping(builder, &range, 1, &familyW, 1, NULL, NULL, NULL, 4.0f);
4795 ok(hr == S_OK, "got 0x%08x\n", hr);
4797 IDWriteFontFallback_Release(fallback);
4799 if (0) /* crashes on native */
4800 hr = IDWriteFontFallbackBuilder_CreateFontFallback(builder, NULL);
4802 hr = IDWriteFontFallbackBuilder_CreateFontFallback(builder, &fallback);
4803 ok(hr == S_OK, "got 0x%08x\n", hr);
4805 /* fallback font missing from system collection */
4806 g_source = strW;
4807 mappedlength = 0;
4808 scale = 0.0f;
4809 font = (void*)0xdeadbeef;
4810 hr = IDWriteFontFallback_MapCharacters(fallback, &analysissource, 0, 1, NULL, NULL, DWRITE_FONT_WEIGHT_NORMAL,
4811 DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, &mappedlength, &font, &scale);
4812 todo_wine {
4813 ok(hr == S_OK, "got 0x%08x\n", hr);
4814 ok(mappedlength == 1, "got %u\n", mappedlength);
4815 ok(scale == 1.0f, "got %f\n", scale);
4816 ok(font == NULL, "got %p\n", font);
4818 IDWriteFontFallback_Release(fallback);
4820 /* remap with custom collection */
4821 range.first = range.last = 'A';
4822 hr = IDWriteFontFallbackBuilder_AddMapping(builder, &range, 1, &familyW, 1, &fallbackcollection, NULL, NULL, 5.0f);
4823 ok(hr == S_OK, "got 0x%08x\n", hr);
4825 hr = IDWriteFontFallbackBuilder_CreateFontFallback(builder, &fallback);
4826 ok(hr == S_OK, "got 0x%08x\n", hr);
4828 g_source = strW;
4829 mappedlength = 0;
4830 scale = 0.0f;
4831 font = NULL;
4832 hr = IDWriteFontFallback_MapCharacters(fallback, &analysissource, 0, 1, NULL, NULL, DWRITE_FONT_WEIGHT_NORMAL,
4833 DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, &mappedlength, &font, &scale);
4834 todo_wine {
4835 ok(hr == S_OK, "got 0x%08x\n", hr);
4836 ok(mappedlength == 1, "got %u\n", mappedlength);
4837 ok(scale == 5.0f, "got %f\n", scale);
4838 ok(font != NULL, "got %p\n", font);
4840 if (font)
4841 IDWriteFont_Release(font);
4843 IDWriteFontFallback_Release(fallback);
4845 range.first = 'B';
4846 range.last = 'A';
4847 hr = IDWriteFontFallbackBuilder_AddMapping(builder, &range, 1, &familyW, 1, &fallbackcollection, NULL, NULL, 6.0f);
4848 ok(hr == S_OK, "got 0x%08x\n", hr);
4850 hr = IDWriteFontFallbackBuilder_CreateFontFallback(builder, &fallback);
4851 ok(hr == S_OK, "got 0x%08x\n", hr);
4853 g_source = strW;
4854 mappedlength = 0;
4855 scale = 0.0f;
4856 font = NULL;
4857 hr = IDWriteFontFallback_MapCharacters(fallback, &analysissource, 0, 1, NULL, NULL, DWRITE_FONT_WEIGHT_NORMAL,
4858 DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, &mappedlength, &font, &scale);
4859 todo_wine {
4860 ok(hr == S_OK, "got 0x%08x\n", hr);
4861 ok(mappedlength == 1, "got %u\n", mappedlength);
4862 ok(scale == 5.0f, "got %f\n", scale);
4863 ok(font != NULL, "got %p\n", font);
4865 if (font)
4866 IDWriteFont_Release(font);
4868 IDWriteFontFallback_Release(fallback);
4870 /* explicit locale */
4871 range.first = 'A';
4872 range.last = 'B';
4873 hr = IDWriteFontFallbackBuilder_AddMapping(builder, &range, 1, &familyW, 1, &fallbackcollection, localeW, NULL, 6.0f);
4874 ok(hr == S_OK, "got 0x%08x\n", hr);
4876 hr = IDWriteFontFallbackBuilder_CreateFontFallback(builder, &fallback);
4877 ok(hr == S_OK, "got 0x%08x\n", hr);
4879 g_source = strW;
4880 mappedlength = 0;
4881 scale = 0.0f;
4882 font = NULL;
4883 hr = IDWriteFontFallback_MapCharacters(fallback, &analysissource, 0, 1, NULL, NULL, DWRITE_FONT_WEIGHT_NORMAL,
4884 DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, &mappedlength, &font, &scale);
4885 todo_wine {
4886 ok(hr == S_OK, "got 0x%08x\n", hr);
4887 ok(mappedlength == 1, "got %u\n", mappedlength);
4888 ok(scale == 5.0f, "got %f\n", scale);
4889 ok(font != NULL, "got %p\n", font);
4891 if (font)
4892 IDWriteFont_Release(font);
4894 IDWriteFontFallback_Release(fallback);
4896 IDWriteFontFallbackBuilder_Release(builder);
4897 ref = IDWriteFactory2_Release(factory2);
4898 ok(ref == 0, "Factory is not released, ref %u.\n", ref);
4901 static void test_SetTypography(void)
4903 static const WCHAR strW[] = {'a','f','i','b',0};
4904 IDWriteTypography *typography, *typography2;
4905 IDWriteTextFormat *format;
4906 IDWriteTextLayout *layout;
4907 DWRITE_TEXT_RANGE range;
4908 IDWriteFactory *factory;
4909 HRESULT hr;
4911 factory = create_factory();
4913 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
4914 DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format);
4915 ok(hr == S_OK, "got 0x%08x\n", hr);
4917 hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 1000.0, 1000.0, &layout);
4918 ok(hr == S_OK, "got 0x%08x\n", hr);
4919 IDWriteTextFormat_Release(format);
4921 hr = IDWriteFactory_CreateTypography(factory, &typography);
4922 ok(hr == S_OK, "got 0x%08x\n", hr);
4924 EXPECT_REF(typography, 1);
4925 range.startPosition = 0;
4926 range.length = 2;
4927 hr = IDWriteTextLayout_SetTypography(layout, typography, range);
4928 ok(hr == S_OK, "got 0x%08x\n", hr);
4929 EXPECT_REF(typography, 2);
4931 hr = IDWriteTextLayout_GetTypography(layout, 0, &typography2, NULL);
4932 ok(hr == S_OK, "got 0x%08x\n", hr);
4933 ok(typography2 == typography, "got %p, expected %p\n", typography2, typography);
4934 IDWriteTypography_Release(typography2);
4935 IDWriteTypography_Release(typography);
4937 hr = IDWriteFactory_CreateTypography(factory, &typography2);
4938 ok(hr == S_OK, "got 0x%08x\n", hr);
4940 range.startPosition = 0;
4941 range.length = 1;
4942 hr = IDWriteTextLayout_SetTypography(layout, typography2, range);
4943 ok(hr == S_OK, "got 0x%08x\n", hr);
4944 EXPECT_REF(typography2, 2);
4945 IDWriteTypography_Release(typography2);
4947 hr = IDWriteTextLayout_GetTypography(layout, 0, &typography, &range);
4948 ok(hr == S_OK, "got 0x%08x\n", hr);
4949 ok(range.length == 1, "got %u\n", range.length);
4951 IDWriteTypography_Release(typography);
4953 IDWriteTextLayout_Release(layout);
4954 IDWriteFactory_Release(factory);
4957 static void test_SetLastLineWrapping(void)
4959 static const WCHAR strW[] = {'a',0};
4960 IDWriteTextLayout2 *layout2;
4961 IDWriteTextFormat1 *format1;
4962 IDWriteTextLayout *layout;
4963 IDWriteTextFormat *format;
4964 IDWriteFactory *factory;
4965 HRESULT hr;
4966 BOOL ret;
4968 factory = create_factory();
4970 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
4971 DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format);
4972 ok(hr == S_OK, "got 0x%08x\n", hr);
4974 hr = IDWriteTextFormat_QueryInterface(format, &IID_IDWriteTextFormat1, (void**)&format1);
4975 IDWriteTextFormat_Release(format);
4976 if (hr != S_OK) {
4977 win_skip("SetLastLineWrapping() is not supported\n");
4978 IDWriteFactory_Release(factory);
4979 return;
4982 ret = IDWriteTextFormat1_GetLastLineWrapping(format1);
4983 ok(ret, "got %d\n", ret);
4985 hr = IDWriteTextFormat1_SetLastLineWrapping(format1, FALSE);
4986 ok(hr == S_OK, "got 0x%08x\n", hr);
4988 hr = IDWriteFactory_CreateTextLayout(factory, strW, 1, (IDWriteTextFormat*)format1, 1000.0, 1000.0, &layout);
4989 ok(hr == S_OK, "got 0x%08x\n", hr);
4991 hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextLayout2, (void**)&layout2);
4992 ok(hr == S_OK, "got 0x%08x\n", hr);
4993 IDWriteTextLayout_Release(layout);
4995 ret = IDWriteTextLayout2_GetLastLineWrapping(layout2);
4996 ok(!ret, "got %d\n", ret);
4998 hr = IDWriteTextLayout2_SetLastLineWrapping(layout2, TRUE);
4999 ok(hr == S_OK, "got 0x%08x\n", hr);
5001 IDWriteTextLayout2_Release(layout2);
5002 IDWriteTextFormat1_Release(format1);
5003 IDWriteFactory_Release(factory);
5006 static void test_SetOpticalAlignment(void)
5008 static const WCHAR strW[] = {'a',0};
5009 DWRITE_OPTICAL_ALIGNMENT alignment;
5010 IDWriteTextLayout2 *layout2;
5011 IDWriteTextFormat1 *format1;
5012 IDWriteTextLayout *layout;
5013 IDWriteTextFormat *format;
5014 IDWriteFactory *factory;
5015 HRESULT hr;
5017 factory = create_factory();
5019 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
5020 DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format);
5021 ok(hr == S_OK, "got 0x%08x\n", hr);
5023 hr = IDWriteTextFormat_QueryInterface(format, &IID_IDWriteTextFormat1, (void**)&format1);
5024 IDWriteTextFormat_Release(format);
5025 if (hr != S_OK) {
5026 win_skip("SetOpticalAlignment() is not supported\n");
5027 IDWriteFactory_Release(factory);
5028 return;
5031 alignment = IDWriteTextFormat1_GetOpticalAlignment(format1);
5032 ok(alignment == DWRITE_OPTICAL_ALIGNMENT_NONE, "got %d\n", alignment);
5034 hr = IDWriteFactory_CreateTextLayout(factory, strW, 1, (IDWriteTextFormat*)format1, 1000.0, 1000.0, &layout);
5035 ok(hr == S_OK, "got 0x%08x\n", hr);
5037 hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextLayout2, (void**)&layout2);
5038 ok(hr == S_OK, "got 0x%08x\n", hr);
5039 IDWriteTextLayout_Release(layout);
5040 IDWriteTextFormat1_Release(format1);
5042 alignment = IDWriteTextLayout2_GetOpticalAlignment(layout2);
5043 ok(alignment == DWRITE_OPTICAL_ALIGNMENT_NONE, "got %d\n", alignment);
5045 hr = IDWriteTextLayout2_QueryInterface(layout2, &IID_IDWriteTextFormat1, (void**)&format1);
5046 ok(hr == S_OK, "got 0x%08x\n", hr);
5048 alignment = IDWriteTextFormat1_GetOpticalAlignment(format1);
5049 ok(alignment == DWRITE_OPTICAL_ALIGNMENT_NONE, "got %d\n", alignment);
5051 hr = IDWriteTextLayout2_SetOpticalAlignment(layout2, DWRITE_OPTICAL_ALIGNMENT_NO_SIDE_BEARINGS);
5052 ok(hr == S_OK, "got 0x%08x\n", hr);
5054 hr = IDWriteTextLayout2_SetOpticalAlignment(layout2, DWRITE_OPTICAL_ALIGNMENT_NO_SIDE_BEARINGS+1);
5055 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
5057 alignment = IDWriteTextFormat1_GetOpticalAlignment(format1);
5058 ok(alignment == DWRITE_OPTICAL_ALIGNMENT_NO_SIDE_BEARINGS, "got %d\n", alignment);
5060 hr = IDWriteTextFormat1_SetOpticalAlignment(format1, DWRITE_OPTICAL_ALIGNMENT_NONE);
5061 ok(hr == S_OK, "got 0x%08x\n", hr);
5063 hr = IDWriteTextFormat1_SetOpticalAlignment(format1, DWRITE_OPTICAL_ALIGNMENT_NO_SIDE_BEARINGS+1);
5064 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
5066 alignment = IDWriteTextLayout2_GetOpticalAlignment(layout2);
5067 ok(alignment == DWRITE_OPTICAL_ALIGNMENT_NONE, "got %d\n", alignment);
5069 IDWriteTextLayout2_Release(layout2);
5070 IDWriteTextFormat1_Release(format1);
5071 IDWriteFactory_Release(factory);
5074 static const struct drawcall_entry drawunderline_seq[] = {
5075 { DRAW_GLYPHRUN, {'a','e',0x0300,0}, {'e','n','-','u','s',0}, 2 }, /* reported runs can't mix different underline values */
5076 { DRAW_GLYPHRUN, {'d',0}, {'e','n','-','u','s',0}, 1 },
5077 { DRAW_UNDERLINE, {0}, {'e','n','-','u','s',0} },
5078 { DRAW_LAST_KIND }
5081 static const struct drawcall_entry drawunderline2_seq[] = {
5082 { DRAW_GLYPHRUN, {'a',0}, {'e','n','-','u','s',0}, 1 },
5083 { DRAW_GLYPHRUN, {'e',0}, {'e','n','-','u','s',0}, 1 },
5084 { DRAW_UNDERLINE, {0}, {'e','n','-','u','s',0} },
5085 { DRAW_LAST_KIND }
5088 static const struct drawcall_entry drawunderline3_seq[] = {
5089 { DRAW_GLYPHRUN, {'a',0}, {'e','n','-','c','a',0}, 1 },
5090 { DRAW_GLYPHRUN, {'e',0}, {'e','n','-','u','s',0}, 1 },
5091 { DRAW_UNDERLINE, {0}, {'e','n','-','c','a',0} },
5092 { DRAW_UNDERLINE, {0}, {'e','n','-','u','s',0} },
5093 { DRAW_LAST_KIND }
5096 static const struct drawcall_entry drawunderline4_seq[] = {
5097 { DRAW_GLYPHRUN, {'a',0}, {'e','n','-','u','s',0}, 1 },
5098 { DRAW_GLYPHRUN, {'e',0}, {'e','n','-','u','s',0}, 1 },
5099 { DRAW_UNDERLINE, {0}, {'e','n','-','u','s',0} },
5100 { DRAW_STRIKETHROUGH },
5101 { DRAW_LAST_KIND }
5104 static void test_SetUnderline(void)
5106 static const WCHAR encaW[] = {'e','n','-','C','A',0};
5107 static const WCHAR strW[] = {'a','e',0x0300,'d',0}; /* accent grave */
5108 IDWriteFontCollection *syscollection;
5109 DWRITE_CLUSTER_METRICS clusters[4];
5110 IDWriteTextFormat *format;
5111 IDWriteTextLayout *layout;
5112 DWRITE_TEXT_RANGE range;
5113 IDWriteFactory *factory;
5114 UINT32 count, i;
5115 HRESULT hr;
5117 factory = create_factory();
5119 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
5120 DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format);
5121 ok(hr == S_OK, "got 0x%08x\n", hr);
5123 hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 1000.0, 1000.0, &layout);
5124 ok(hr == S_OK, "got 0x%08x\n", hr);
5126 count = 0;
5127 hr = IDWriteTextLayout_GetClusterMetrics(layout, clusters, ARRAY_SIZE(clusters), &count);
5128 ok(hr == S_OK, "got 0x%08x\n", hr);
5129 todo_wine
5130 ok(count == 3, "got %u\n", count);
5132 range.startPosition = 0;
5133 range.length = 2;
5134 hr = IDWriteTextLayout_SetUnderline(layout, TRUE, range);
5135 ok(hr == S_OK, "got 0x%08x\n", hr);
5137 count = 0;
5138 hr = IDWriteTextLayout_GetClusterMetrics(layout, clusters, ARRAY_SIZE(clusters), &count);
5139 ok(hr == S_OK, "got 0x%08x\n", hr);
5140 todo_wine
5141 ok(count == 3, "got %u\n", count);
5143 flush_sequence(sequences, RENDERER_ID);
5144 hr = IDWriteTextLayout_Draw(layout, NULL, &testrenderer, 0.0, 0.0);
5145 ok(hr == S_OK, "got 0x%08x\n", hr);
5146 ok_sequence(sequences, RENDERER_ID, drawunderline_seq, "draw underline test", TRUE);
5148 IDWriteTextLayout_Release(layout);
5150 /* 2 characters, same font, significantly different font size. Set underline for both, see how many
5151 underline drawing calls is there. */
5152 hr = IDWriteFactory_CreateTextLayout(factory, strW, 2, format, 1000.0f, 1000.0f, &layout);
5153 ok(hr == S_OK, "got 0x%08x\n", hr);
5155 range.startPosition = 0;
5156 range.length = 2;
5157 hr = IDWriteTextLayout_SetUnderline(layout, TRUE, range);
5158 ok(hr == S_OK, "got 0x%08x\n", hr);
5160 range.startPosition = 0;
5161 range.length = 1;
5162 hr = IDWriteTextLayout_SetFontSize(layout, 100.0f, range);
5163 ok(hr == S_OK, "got 0x%08x\n", hr);
5165 flush_sequence(sequences, RENDERER_ID);
5166 hr = IDWriteTextLayout_Draw(layout, NULL, &testrenderer, 0.0f, 0.0f);
5167 ok(hr == S_OK, "got 0x%08x\n", hr);
5168 ok_sequence(sequences, RENDERER_ID, drawunderline2_seq, "draw underline test 2", FALSE);
5170 /* now set different locale for second char, draw again */
5171 range.startPosition = 0;
5172 range.length = 1;
5173 hr = IDWriteTextLayout_SetLocaleName(layout, encaW, range);
5174 ok(hr == S_OK, "got 0x%08x\n", hr);
5176 flush_sequence(sequences, RENDERER_ID);
5177 hr = IDWriteTextLayout_Draw(layout, NULL, &testrenderer, 0.0f, 0.0f);
5178 ok(hr == S_OK, "got 0x%08x\n", hr);
5179 ok_sequence(sequences, RENDERER_ID, drawunderline3_seq, "draw underline test 2", FALSE);
5181 IDWriteTextLayout_Release(layout);
5183 /* 2 characters, same font properties, first with strikethrough, both underlined */
5184 hr = IDWriteFactory_CreateTextLayout(factory, strW, 2, format, 1000.0f, 1000.0f, &layout);
5185 ok(hr == S_OK, "got 0x%08x\n", hr);
5187 range.startPosition = 0;
5188 range.length = 1;
5189 hr = IDWriteTextLayout_SetStrikethrough(layout, TRUE, range);
5190 ok(hr == S_OK, "got 0x%08x\n", hr);
5192 range.startPosition = 0;
5193 range.length = 2;
5194 hr = IDWriteTextLayout_SetUnderline(layout, TRUE, range);
5195 ok(hr == S_OK, "got 0x%08x\n", hr);
5197 flush_sequence(sequences, RENDERER_ID);
5198 hr = IDWriteTextLayout_Draw(layout, NULL, &testrenderer, 0.0f, 0.0f);
5199 ok(hr == S_OK, "got 0x%08x\n", hr);
5200 ok_sequence(sequences, RENDERER_ID, drawunderline4_seq, "draw underline test 4", FALSE);
5202 IDWriteTextLayout_Release(layout);
5204 IDWriteTextFormat_Release(format);
5206 /* Test runHeight value with all available fonts */
5207 hr = IDWriteFactory_GetSystemFontCollection(factory, &syscollection, FALSE);
5208 ok(hr == S_OK, "got 0x%08x\n", hr);
5209 count = IDWriteFontCollection_GetFontFamilyCount(syscollection);
5211 for (i = 0; i < count; i++) {
5212 DWRITE_FONT_METRICS fontmetrics;
5213 IDWriteLocalizedStrings *names;
5214 struct renderer_context ctxt;
5215 IDWriteFontFamily *family;
5216 IDWriteFontFace *fontface;
5217 IDWriteFont *font;
5218 WCHAR nameW[256];
5219 BOOL exists;
5221 format = NULL;
5222 layout = NULL;
5224 hr = IDWriteFontCollection_GetFontFamily(syscollection, i, &family);
5225 ok(hr == S_OK, "got 0x%08x\n", hr);
5227 hr = IDWriteFontFamily_GetFirstMatchingFont(family, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STRETCH_NORMAL,
5228 DWRITE_FONT_STYLE_NORMAL, &font);
5229 ok(hr == S_OK, "got 0x%08x\n", hr);
5231 hr = IDWriteFont_CreateFontFace(font, &fontface);
5232 ok(hr == S_OK, "got 0x%08x\n", hr);
5234 hr = IDWriteFontFamily_GetFamilyNames(family, &names);
5235 ok(hr == S_OK, "got 0x%08x\n", hr);
5237 if (!(exists = get_enus_string(names, nameW, ARRAY_SIZE(nameW)))) {
5238 IDWriteLocalFontFileLoader *localloader;
5239 IDWriteFontFileLoader *loader;
5240 IDWriteFontFile *file;
5241 const void *key;
5242 UINT32 keysize;
5243 UINT32 count;
5245 count = 1;
5246 hr = IDWriteFontFace_GetFiles(fontface, &count, &file);
5247 ok(hr == S_OK, "got 0x%08x\n", hr);
5249 hr = IDWriteFontFile_GetLoader(file, &loader);
5250 ok(hr == S_OK, "got 0x%08x\n", hr);
5252 hr = IDWriteFontFileLoader_QueryInterface(loader, &IID_IDWriteLocalFontFileLoader, (void**)&localloader);
5253 ok(hr == S_OK, "got 0x%08x\n", hr);
5254 IDWriteFontFileLoader_Release(loader);
5256 hr = IDWriteFontFile_GetReferenceKey(file, &key, &keysize);
5257 ok(hr == S_OK, "got 0x%08x\n", hr);
5259 hr = IDWriteLocalFontFileLoader_GetFilePathFromKey(localloader, key, keysize, nameW, ARRAY_SIZE(nameW));
5260 ok(hr == S_OK, "got 0x%08x\n", hr);
5262 skip("Failed to get English family name, font file %s\n", wine_dbgstr_w(nameW));
5264 IDWriteLocalFontFileLoader_Release(localloader);
5265 IDWriteFontFile_Release(file);
5268 IDWriteLocalizedStrings_Release(names);
5269 IDWriteFont_Release(font);
5271 if (!exists)
5272 goto cleanup;
5274 IDWriteFontFace_GetMetrics(fontface, &fontmetrics);
5275 hr = IDWriteFactory_CreateTextFormat(factory, nameW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
5276 DWRITE_FONT_STRETCH_NORMAL, fontmetrics.designUnitsPerEm, enusW, &format);
5277 ok(hr == S_OK, "got 0x%08x\n", hr);
5279 hr = IDWriteFactory_CreateTextLayout(factory, strW, 2, format, 30000.0f, 100.0f, &layout);
5280 ok(hr == S_OK, "got 0x%08x\n", hr);
5282 range.startPosition = 0;
5283 range.length = 2;
5284 hr = IDWriteTextLayout_SetUnderline(layout, TRUE, range);
5285 ok(hr == S_OK, "got 0x%08x\n", hr);
5287 memset(&ctxt, 0, sizeof(ctxt));
5288 ctxt.format = format;
5289 ctxt.familyW = nameW;
5290 hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0f, 0.0f);
5291 ok(hr == S_OK, "got 0x%08x\n", hr);
5293 cleanup:
5294 if (layout)
5295 IDWriteTextLayout_Release(layout);
5296 if (format)
5297 IDWriteTextFormat_Release(format);
5298 IDWriteFontFace_Release(fontface);
5299 IDWriteFontFamily_Release(family);
5301 IDWriteFontCollection_Release(syscollection);
5303 IDWriteFactory_Release(factory);
5306 static void test_InvalidateLayout(void)
5308 static const WCHAR strW[] = {'a',0};
5309 IDWriteTextLayout3 *layout3;
5310 IDWriteTextLayout *layout;
5311 IDWriteTextFormat *format;
5312 IDWriteFactory *factory;
5313 HRESULT hr;
5315 factory = create_factory();
5317 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
5318 DWRITE_FONT_STRETCH_NORMAL, 10.0f, enusW, &format);
5319 ok(hr == S_OK, "got 0x%08x\n", hr);
5321 hr = IDWriteFactory_CreateTextLayout(factory, strW, 1, format, 1000.0f, 1000.0f, &layout);
5322 ok(hr == S_OK, "got 0x%08x\n", hr);
5324 hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextLayout3, (void**)&layout3);
5325 if (hr == S_OK) {
5326 IDWriteTextFormat1 *format1;
5327 IDWriteTextFormat2 *format2;
5329 hr = IDWriteTextFormat_QueryInterface(format, &IID_IDWriteTextFormat2, (void**)&format2);
5330 ok(hr == S_OK, "got 0x%08x\n", hr);
5331 IDWriteTextFormat2_Release(format2);
5333 hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextFormat2, (void**)&format2);
5334 ok(hr == S_OK || broken(hr == E_NOINTERFACE), "Unexpected hr %#x.\n", hr);
5335 if (hr == S_OK) {
5336 ok(format != (IDWriteTextFormat *)format2, "Unexpected interface pointer.\n");
5337 IDWriteTextFormat2_Release(format2);
5340 hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextFormat1, (void**)&format1);
5341 ok(hr == S_OK, "got 0x%08x\n", hr);
5343 hr = IDWriteTextFormat1_QueryInterface(format1, &IID_IDWriteTextFormat2, (void**)&format2);
5344 ok(hr == S_OK || broken(hr == E_NOINTERFACE), "Unexpected hr %#x.\n", hr);
5345 if (hr == S_OK)
5346 IDWriteTextFormat2_Release(format2);
5347 IDWriteTextFormat1_Release(format1);
5349 hr = IDWriteTextLayout3_QueryInterface(layout3, &IID_IDWriteTextFormat2, (void**)&format2);
5350 ok(hr == S_OK || broken(hr == E_NOINTERFACE), "got 0x%08x\n", hr);
5351 if (hr == S_OK)
5352 IDWriteTextFormat2_Release(format2);
5354 hr = IDWriteTextLayout3_InvalidateLayout(layout3);
5355 ok(hr == S_OK, "got 0x%08x\n", hr);
5356 IDWriteTextLayout3_Release(layout3);
5358 else
5359 win_skip("IDWriteTextLayout3::InvalidateLayout() is not supported.\n");
5361 IDWriteTextLayout_Release(layout);
5362 IDWriteTextFormat_Release(format);
5363 IDWriteFactory_Release(factory);
5366 static void test_line_spacing(void)
5368 static const WCHAR strW[] = {'a',0};
5369 IDWriteTextFormat2 *format2;
5370 IDWriteTextLayout *layout;
5371 IDWriteTextFormat *format;
5372 IDWriteFactory *factory;
5373 HRESULT hr;
5375 factory = create_factory();
5377 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
5378 DWRITE_FONT_STRETCH_NORMAL, 10.0f, enusW, &format);
5379 ok(hr == S_OK, "got 0x%08x\n", hr);
5381 hr = IDWriteTextFormat_SetLineSpacing(format, DWRITE_LINE_SPACING_METHOD_DEFAULT, 0.0f, 0.0f);
5382 ok(hr == S_OK, "got 0x%08x\n", hr);
5384 hr = IDWriteTextFormat_SetLineSpacing(format, DWRITE_LINE_SPACING_METHOD_DEFAULT, 0.0f, -10.0f);
5385 ok(hr == S_OK, "got 0x%08x\n", hr);
5387 hr = IDWriteTextFormat_SetLineSpacing(format, DWRITE_LINE_SPACING_METHOD_DEFAULT, -10.0f, 0.0f);
5388 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
5390 hr = IDWriteTextFormat_SetLineSpacing(format, DWRITE_LINE_SPACING_METHOD_PROPORTIONAL+1, 0.0f, 0.0f);
5391 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
5393 hr = IDWriteFactory_CreateTextLayout(factory, strW, 1, format, 1000.0f, 1000.0f, &layout);
5394 ok(hr == S_OK, "got 0x%08x\n", hr);
5396 hr = IDWriteTextLayout_SetLineSpacing(layout, DWRITE_LINE_SPACING_METHOD_DEFAULT, 0.0f, 0.0f);
5397 ok(hr == S_OK, "got 0x%08x\n", hr);
5399 hr = IDWriteTextLayout_SetLineSpacing(layout, DWRITE_LINE_SPACING_METHOD_DEFAULT, 0.0f, -10.0f);
5400 ok(hr == S_OK, "got 0x%08x\n", hr);
5402 hr = IDWriteTextLayout_SetLineSpacing(layout, DWRITE_LINE_SPACING_METHOD_DEFAULT, -10.0f, 0.0f);
5403 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
5405 hr = IDWriteTextLayout_SetLineSpacing(layout, DWRITE_LINE_SPACING_METHOD_PROPORTIONAL+1, 0.0f, 0.0f);
5406 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
5408 if (IDWriteTextFormat_QueryInterface(format, &IID_IDWriteTextFormat2, (void**)&format2) == S_OK) {
5409 DWRITE_LINE_SPACING spacing;
5411 hr = IDWriteTextFormat2_GetLineSpacing(format2, &spacing);
5412 ok(hr == S_OK, "got 0x%08x\n", hr);
5414 ok(spacing.method == DWRITE_LINE_SPACING_METHOD_DEFAULT, "got method %d\n", spacing.method);
5415 ok(spacing.height == 0.0f, "got %f\n", spacing.height);
5416 ok(spacing.baseline == -10.0f, "got %f\n", spacing.baseline);
5417 ok(spacing.leadingBefore == 0.0f, "got %f\n", spacing.leadingBefore);
5418 ok(spacing.fontLineGapUsage == DWRITE_FONT_LINE_GAP_USAGE_DEFAULT, "got %f\n", spacing.leadingBefore);
5420 spacing.leadingBefore = -1.0f;
5422 hr = IDWriteTextFormat2_SetLineSpacing(format2, &spacing);
5423 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
5425 spacing.leadingBefore = 1.1f;
5427 hr = IDWriteTextFormat2_SetLineSpacing(format2, &spacing);
5428 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
5430 spacing.leadingBefore = 1.0f;
5432 hr = IDWriteTextFormat2_SetLineSpacing(format2, &spacing);
5433 ok(hr == S_OK, "got 0x%08x\n", hr);
5435 spacing.method = DWRITE_LINE_SPACING_METHOD_PROPORTIONAL + 1;
5436 hr = IDWriteTextFormat2_SetLineSpacing(format2, &spacing);
5437 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
5439 spacing.method = DWRITE_LINE_SPACING_METHOD_PROPORTIONAL;
5440 spacing.fontLineGapUsage = DWRITE_FONT_LINE_GAP_USAGE_ENABLED + 1;
5441 hr = IDWriteTextFormat2_SetLineSpacing(format2, &spacing);
5442 ok(hr == S_OK, "got 0x%08x\n", hr);
5444 hr = IDWriteTextFormat2_GetLineSpacing(format2, &spacing);
5445 ok(hr == S_OK, "got 0x%08x\n", hr);
5446 ok(spacing.fontLineGapUsage == DWRITE_FONT_LINE_GAP_USAGE_ENABLED + 1, "got %d\n", spacing.fontLineGapUsage);
5448 IDWriteTextFormat2_Release(format2);
5451 IDWriteTextLayout_Release(layout);
5452 IDWriteTextFormat_Release(format);
5453 IDWriteFactory_Release(factory);
5456 static void test_GetOverhangMetrics(void)
5458 static const struct overhangs_test
5460 FLOAT uniform_baseline;
5461 DWRITE_INLINE_OBJECT_METRICS metrics;
5462 DWRITE_OVERHANG_METRICS overhang_metrics;
5463 DWRITE_OVERHANG_METRICS expected;
5464 } overhangs_tests[] = {
5465 { 16.0f, { 10.0f, 50.0f, 20.0f }, { 1.0f, 2.0f, 3.0f, 4.0f }, { 1.0f, 6.0f, 3.0f, 0.0f } },
5466 { 15.0f, { 10.0f, 50.0f, 20.0f }, { 1.0f, 2.0f, 3.0f, 4.0f }, { 1.0f, 7.0f, 3.0f, -1.0f } },
5467 { 16.0f, { 10.0f, 50.0f, 20.0f }, { -1.0f, 0.0f, -3.0f, 4.0f }, { -1.0f, 4.0f, -3.0f, 0.0f } },
5468 { 15.0f, { 10.0f, 50.0f, 20.0f }, { -1.0f, 10.0f, 3.0f, -4.0f }, { -1.0f, 15.0f, 3.0f, -9.0f } },
5470 static const WCHAR strW[] = {'A',0};
5471 IDWriteFactory *factory;
5472 IDWriteTextFormat *format;
5473 IDWriteTextLayout *layout;
5474 HRESULT hr;
5475 UINT32 i;
5477 factory = create_factory();
5479 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
5480 DWRITE_FONT_STRETCH_NORMAL, 100.0f, enusW, &format);
5481 ok(hr == S_OK, "Failed to create text format, hr %#x.\n", hr);
5483 hr = IDWriteFactory_CreateTextLayout(factory, strW, 1, format, 1000.0f, 1000.0f, &layout);
5484 ok(hr == S_OK, "Failed to create text layout, hr %x.\n", hr);
5486 for (i = 0; i < ARRAY_SIZE(overhangs_tests); i++) {
5487 const struct overhangs_test *test = &overhangs_tests[i];
5488 DWRITE_OVERHANG_METRICS overhang_metrics;
5489 DWRITE_TEXT_RANGE range = { 0, 1 };
5490 DWRITE_TEXT_METRICS metrics;
5491 struct test_inline_obj obj;
5493 test_inline_obj_init(&obj, &test->metrics, &test->overhang_metrics);
5495 hr = IDWriteTextLayout_SetLineSpacing(layout, DWRITE_LINE_SPACING_METHOD_UNIFORM, test->metrics.height * 2.0f,
5496 test->uniform_baseline);
5497 ok(hr == S_OK, "Failed to set line spacing, hr %#x.\n", hr);
5499 hr = IDWriteTextLayout_SetInlineObject(layout, NULL, range);
5500 ok(hr == S_OK, "Failed to reset inline object, hr %#x.\n", hr);
5502 hr = IDWriteTextLayout_SetInlineObject(layout, &obj.IDWriteInlineObject_iface, range);
5503 ok(hr == S_OK, "Failed to set inline object, hr %#x.\n", hr);
5505 hr = IDWriteTextLayout_GetMetrics(layout, &metrics);
5506 ok(hr == S_OK, "Failed to get layout metrics, hr %#x.\n", hr);
5508 ok(metrics.width == test->metrics.width, "%u: unexpected formatted width.\n", i);
5509 ok(metrics.height == test->metrics.height * 2.0f, "%u: unexpected formatted height.\n", i);
5511 hr = IDWriteTextLayout_SetMaxWidth(layout, metrics.width);
5512 hr = IDWriteTextLayout_SetMaxHeight(layout, test->metrics.height);
5514 hr = IDWriteTextLayout_GetOverhangMetrics(layout, &overhang_metrics);
5515 ok(hr == S_OK, "Failed to get overhang metrics, hr %#x.\n", hr);
5517 ok(!memcmp(&overhang_metrics, &test->expected, sizeof(overhang_metrics)),
5518 "%u: unexpected overhang metrics (%f, %f, %f, %f).\n", i, overhang_metrics.left, overhang_metrics.top,
5519 overhang_metrics.right, overhang_metrics.bottom);
5522 IDWriteTextLayout_Release(layout);
5523 IDWriteTextFormat_Release(format);
5524 IDWriteFactory_Release(factory);
5527 START_TEST(layout)
5529 IDWriteFactory *factory;
5531 if (!(factory = create_factory())) {
5532 win_skip("failed to create factory\n");
5533 return;
5536 init_call_sequences(sequences, NUM_CALL_SEQUENCES);
5537 init_call_sequences(expected_seq, 1);
5539 test_CreateTextLayout();
5540 test_CreateGdiCompatibleTextLayout();
5541 test_CreateTextFormat();
5542 test_GetLocaleName();
5543 test_CreateEllipsisTrimmingSign();
5544 test_fontweight();
5545 test_SetInlineObject();
5546 test_Draw();
5547 test_typography();
5548 test_GetClusterMetrics();
5549 test_SetLocaleName();
5550 test_SetPairKerning();
5551 test_SetVerticalGlyphOrientation();
5552 test_fallback();
5553 test_DetermineMinWidth();
5554 test_SetFontSize();
5555 test_SetFontFamilyName();
5556 test_SetFontStyle();
5557 test_SetFontStretch();
5558 test_SetStrikethrough();
5559 test_GetMetrics();
5560 test_SetFlowDirection();
5561 test_SetDrawingEffect();
5562 test_GetLineMetrics();
5563 test_SetTextAlignment();
5564 test_SetParagraphAlignment();
5565 test_SetReadingDirection();
5566 test_pixelsnapping();
5567 test_SetWordWrapping();
5568 test_MapCharacters();
5569 test_FontFallbackBuilder();
5570 test_SetTypography();
5571 test_SetLastLineWrapping();
5572 test_SetOpticalAlignment();
5573 test_SetUnderline();
5574 test_InvalidateLayout();
5575 test_line_spacing();
5576 test_GetOverhangMetrics();
5578 IDWriteFactory_Release(factory);