dwrite: Implement InvalidateLayout().
[wine.git] / dlls / dwrite / tests / layout.c
blobc8fe6d2782f03a43282731430b193dcd1461b4d3
1 /*
2 * Text layout/format tests
4 * Copyright 2012, 2014-2016 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 static const WCHAR tahomaW[] = {'T','a','h','o','m','a',0};
33 static const WCHAR enusW[] = {'e','n','-','u','s',0};
35 static DWRITE_SCRIPT_ANALYSIS g_sa;
37 /* test IDWriteTextAnalysisSink */
38 static HRESULT WINAPI analysissink_QueryInterface(IDWriteTextAnalysisSink *iface, REFIID riid, void **obj)
40 if (IsEqualIID(riid, &IID_IDWriteTextAnalysisSink) || IsEqualIID(riid, &IID_IUnknown))
42 *obj = iface;
43 return S_OK;
46 *obj = NULL;
47 return E_NOINTERFACE;
50 static ULONG WINAPI analysissink_AddRef(IDWriteTextAnalysisSink *iface)
52 return 2;
55 static ULONG WINAPI analysissink_Release(IDWriteTextAnalysisSink *iface)
57 return 1;
60 static HRESULT WINAPI analysissink_SetScriptAnalysis(IDWriteTextAnalysisSink *iface,
61 UINT32 position, UINT32 length, DWRITE_SCRIPT_ANALYSIS const* sa)
63 g_sa = *sa;
64 return S_OK;
67 static HRESULT WINAPI analysissink_SetLineBreakpoints(IDWriteTextAnalysisSink *iface,
68 UINT32 position, UINT32 length, DWRITE_LINE_BREAKPOINT const* breakpoints)
70 ok(0, "unexpected call\n");
71 return E_NOTIMPL;
74 static HRESULT WINAPI analysissink_SetBidiLevel(IDWriteTextAnalysisSink *iface,
75 UINT32 position, UINT32 length, UINT8 explicitLevel, UINT8 resolvedLevel)
77 ok(0, "unexpected\n");
78 return E_NOTIMPL;
81 static HRESULT WINAPI analysissink_SetNumberSubstitution(IDWriteTextAnalysisSink *iface,
82 UINT32 position, UINT32 length, IDWriteNumberSubstitution* substitution)
84 ok(0, "unexpected\n");
85 return E_NOTIMPL;
88 static IDWriteTextAnalysisSinkVtbl analysissinkvtbl = {
89 analysissink_QueryInterface,
90 analysissink_AddRef,
91 analysissink_Release,
92 analysissink_SetScriptAnalysis,
93 analysissink_SetLineBreakpoints,
94 analysissink_SetBidiLevel,
95 analysissink_SetNumberSubstitution
98 static IDWriteTextAnalysisSink analysissink = { &analysissinkvtbl };
100 /* test IDWriteTextAnalysisSource */
101 static HRESULT WINAPI analysissource_QueryInterface(IDWriteTextAnalysisSource *iface,
102 REFIID riid, void **obj)
104 if (IsEqualIID(riid, &IID_IDWriteTextAnalysisSource) || IsEqualIID(riid, &IID_IUnknown)) {
105 *obj = iface;
106 IDWriteTextAnalysisSource_AddRef(iface);
107 return S_OK;
109 return E_NOINTERFACE;
112 static ULONG WINAPI analysissource_AddRef(IDWriteTextAnalysisSource *iface)
114 return 2;
117 static ULONG WINAPI analysissource_Release(IDWriteTextAnalysisSource *iface)
119 return 1;
122 static const WCHAR *g_source;
124 static HRESULT WINAPI analysissource_GetTextAtPosition(IDWriteTextAnalysisSource *iface,
125 UINT32 position, WCHAR const** text, UINT32* text_len)
127 if (position >= lstrlenW(g_source))
129 *text = NULL;
130 *text_len = 0;
132 else
134 *text = &g_source[position];
135 *text_len = lstrlenW(g_source) - position;
138 return S_OK;
141 static HRESULT WINAPI analysissource_GetTextBeforePosition(IDWriteTextAnalysisSource *iface,
142 UINT32 position, WCHAR const** text, UINT32* text_len)
144 ok(0, "unexpected\n");
145 return E_NOTIMPL;
148 static DWRITE_READING_DIRECTION WINAPI analysissource_GetParagraphReadingDirection(
149 IDWriteTextAnalysisSource *iface)
151 ok(0, "unexpected\n");
152 return DWRITE_READING_DIRECTION_RIGHT_TO_LEFT;
155 static HRESULT WINAPI analysissource_GetLocaleName(IDWriteTextAnalysisSource *iface,
156 UINT32 position, UINT32* text_len, WCHAR const** locale)
158 *locale = NULL;
159 *text_len = 0;
160 return S_OK;
163 static HRESULT WINAPI analysissource_GetNumberSubstitution(IDWriteTextAnalysisSource *iface,
164 UINT32 position, UINT32* text_len, IDWriteNumberSubstitution **substitution)
166 ok(0, "unexpected\n");
167 return E_NOTIMPL;
170 static IDWriteTextAnalysisSourceVtbl analysissourcevtbl = {
171 analysissource_QueryInterface,
172 analysissource_AddRef,
173 analysissource_Release,
174 analysissource_GetTextAtPosition,
175 analysissource_GetTextBeforePosition,
176 analysissource_GetParagraphReadingDirection,
177 analysissource_GetLocaleName,
178 analysissource_GetNumberSubstitution
181 static IDWriteTextAnalysisSource analysissource = { &analysissourcevtbl };
183 static IDWriteFactory *create_factory(void)
185 IDWriteFactory *factory;
186 HRESULT hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_ISOLATED, &IID_IDWriteFactory, (IUnknown**)&factory);
187 ok(hr == S_OK, "got 0x%08x\n", hr);
188 return factory;
191 /* obvious limitation is that only last script data is returned, so this
192 helper is suitable for single script strings only */
193 static void get_script_analysis(const WCHAR *str, UINT32 len, DWRITE_SCRIPT_ANALYSIS *sa)
195 IDWriteTextAnalyzer *analyzer;
196 IDWriteFactory *factory;
197 HRESULT hr;
199 g_source = str;
201 factory = create_factory();
202 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
203 ok(hr == S_OK, "got 0x%08x\n", hr);
205 hr = IDWriteTextAnalyzer_AnalyzeScript(analyzer, &analysissource, 0, len, &analysissink);
206 ok(hr == S_OK, "got 0x%08x\n", hr);
208 *sa = g_sa;
209 IDWriteFactory_Release(factory);
212 #define EXPECT_REF(obj,ref) _expect_ref((IUnknown*)obj, ref, __LINE__)
213 static void _expect_ref(IUnknown* obj, ULONG ref, int line)
215 ULONG rc = IUnknown_AddRef(obj);
216 IUnknown_Release(obj);
217 ok_(__FILE__,line)(rc-1 == ref, "expected refcount %d, got %d\n", ref, rc-1);
220 enum drawcall_modifiers_kind {
221 DRAW_EFFECT = 0x1000
224 enum drawcall_kind {
225 DRAW_GLYPHRUN = 0,
226 DRAW_UNDERLINE = 1,
227 DRAW_STRIKETHROUGH = 2,
228 DRAW_INLINE = 3,
229 DRAW_LAST_KIND = 4,
230 DRAW_TOTAL_KINDS = 5,
231 DRAW_KINDS_MASK = 0xff
234 static const char *get_draw_kind_name(unsigned short kind)
236 static const char *kind_names[] = {
237 "GLYPH_RUN",
238 "UNDERLINE",
239 "STRIKETHROUGH",
240 "INLINE",
241 "END_OF_SEQ",
243 "GLYPH_RUN|EFFECT",
244 "UNDERLINE|EFFECT",
245 "STRIKETHROUGH|EFFECT",
246 "INLINE|EFFECT",
247 "END_OF_SEQ"
249 if ((kind & DRAW_KINDS_MASK) > DRAW_LAST_KIND)
250 return "unknown";
251 return (kind & DRAW_EFFECT) ? kind_names[(kind & DRAW_KINDS_MASK) + DRAW_TOTAL_KINDS] :
252 kind_names[kind];
255 struct drawcall_entry {
256 enum drawcall_kind kind;
257 WCHAR string[10]; /* only meaningful for DrawGlyphRun() */
258 WCHAR locale[LOCALE_NAME_MAX_LENGTH];
261 struct drawcall_sequence
263 int count;
264 int size;
265 struct drawcall_entry *sequence;
268 struct drawtestcontext {
269 unsigned short kind;
270 BOOL todo;
271 int *failcount;
272 const char *file;
273 int line;
276 #define NUM_CALL_SEQUENCES 1
277 #define RENDERER_ID 0
278 static struct drawcall_sequence *sequences[NUM_CALL_SEQUENCES];
279 static struct drawcall_sequence *expected_seq[1];
281 static void add_call(struct drawcall_sequence **seq, int sequence_index, const struct drawcall_entry *call)
283 struct drawcall_sequence *call_seq = seq[sequence_index];
285 if (!call_seq->sequence) {
286 call_seq->size = 10;
287 call_seq->sequence = HeapAlloc(GetProcessHeap(), 0, call_seq->size * sizeof (struct drawcall_entry));
290 if (call_seq->count == call_seq->size) {
291 call_seq->size *= 2;
292 call_seq->sequence = HeapReAlloc(GetProcessHeap(), 0,
293 call_seq->sequence,
294 call_seq->size * sizeof (struct drawcall_entry));
297 assert(call_seq->sequence);
298 call_seq->sequence[call_seq->count++] = *call;
301 static inline void flush_sequence(struct drawcall_sequence **seg, int sequence_index)
303 struct drawcall_sequence *call_seq = seg[sequence_index];
305 HeapFree(GetProcessHeap(), 0, call_seq->sequence);
306 call_seq->sequence = NULL;
307 call_seq->count = call_seq->size = 0;
310 static inline void flush_sequences(struct drawcall_sequence **seq, int n)
312 int i;
313 for (i = 0; i < n; i++)
314 flush_sequence(seq, i);
317 static void init_call_sequences(struct drawcall_sequence **seq, int n)
319 int i;
321 for (i = 0; i < n; i++)
322 seq[i] = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct drawcall_sequence));
325 static void ok_sequence_(struct drawcall_sequence **seq, int sequence_index,
326 const struct drawcall_entry *expected, const char *context, BOOL todo,
327 const char *file, int line)
329 static const struct drawcall_entry end_of_sequence = { DRAW_LAST_KIND };
330 struct drawcall_sequence *call_seq = seq[sequence_index];
331 const struct drawcall_entry *actual, *sequence;
332 int failcount = 0;
334 add_call(seq, sequence_index, &end_of_sequence);
336 sequence = call_seq->sequence;
337 actual = sequence;
339 while (expected->kind != DRAW_LAST_KIND && actual->kind != DRAW_LAST_KIND) {
340 if (expected->kind != actual->kind) {
341 if (todo) {
342 failcount++;
343 todo_wine
344 ok_(file, line) (0, "%s: call %s was expected, but got call %s instead\n",
345 context, get_draw_kind_name(expected->kind), get_draw_kind_name(actual->kind));
347 flush_sequence(seq, sequence_index);
348 return;
350 else
351 ok_(file, line) (0, "%s: call %s was expected, but got call %s instead\n",
352 context, get_draw_kind_name(expected->kind), get_draw_kind_name(actual->kind));
354 else if ((expected->kind & DRAW_KINDS_MASK) == DRAW_GLYPHRUN) {
355 int cmp = lstrcmpW(expected->string, actual->string);
356 if (cmp != 0 && todo) {
357 failcount++;
358 todo_wine
359 ok_(file, line) (0, "%s: glyphrun string %s was expected, but got %s instead\n",
360 context, wine_dbgstr_w(expected->string), wine_dbgstr_w(actual->string));
362 else
363 ok_(file, line) (cmp == 0, "%s: glyphrun string %s was expected, but got %s instead\n",
364 context, wine_dbgstr_w(expected->string), wine_dbgstr_w(actual->string));
366 else if ((expected->kind & DRAW_KINDS_MASK) == DRAW_UNDERLINE) {
367 int cmp = lstrcmpW(expected->locale, actual->locale);
368 if (cmp != 0 && todo) {
369 failcount++;
370 todo_wine
371 ok_(file, line) (0, "%s: underline locale %s was expected, but got %s instead\n",
372 context, wine_dbgstr_w(expected->locale), wine_dbgstr_w(actual->locale));
374 else
375 ok_(file, line) (cmp == 0, "%s: underline locale %s was expected, but got %s instead\n",
376 context, wine_dbgstr_w(expected->locale), wine_dbgstr_w(actual->locale));
378 expected++;
379 actual++;
382 if (todo) {
383 todo_wine {
384 if (expected->kind != DRAW_LAST_KIND || actual->kind != DRAW_LAST_KIND) {
385 failcount++;
386 ok_(file, line) (0, "%s: the call sequence is not complete: expected %s - actual %s\n",
387 context, get_draw_kind_name(expected->kind), get_draw_kind_name(actual->kind));
391 else if (expected->kind != DRAW_LAST_KIND || actual->kind != DRAW_LAST_KIND)
392 ok_(file, line) (0, "%s: the call sequence is not complete: expected %s - actual %s\n",
393 context, get_draw_kind_name(expected->kind), get_draw_kind_name(actual->kind));
395 if (todo && !failcount) /* succeeded yet marked todo */
396 todo_wine
397 ok_(file, line)(1, "%s: marked \"todo_wine\" but succeeds\n", context);
399 flush_sequence(seq, sequence_index);
402 #define ok_sequence(seq, index, exp, contx, todo) \
403 ok_sequence_(seq, index, (exp), (contx), (todo), __FILE__, __LINE__)
405 static HRESULT WINAPI testrenderer_QI(IDWriteTextRenderer *iface, REFIID riid, void **obj)
407 if (IsEqualIID(riid, &IID_IDWriteTextRenderer) ||
408 IsEqualIID(riid, &IID_IDWritePixelSnapping) ||
409 IsEqualIID(riid, &IID_IUnknown)
411 *obj = iface;
412 return S_OK;
415 *obj = NULL;
417 /* IDWriteTextRenderer1 overrides drawing calls, ignore for now */
418 if (IsEqualIID(riid, &IID_IDWriteTextRenderer1))
419 return E_NOINTERFACE;
421 ok(0, "unexpected QI %s\n", wine_dbgstr_guid(riid));
422 return E_NOINTERFACE;
425 static ULONG WINAPI testrenderer_AddRef(IDWriteTextRenderer *iface)
427 return 2;
430 static ULONG WINAPI testrenderer_Release(IDWriteTextRenderer *iface)
432 return 1;
435 struct renderer_context {
436 BOOL gdicompat;
437 BOOL use_gdi_natural;
438 BOOL snapping_disabled;
439 DWRITE_MATRIX m;
440 FLOAT ppdip;
441 FLOAT originX;
442 FLOAT originY;
445 static HRESULT WINAPI testrenderer_IsPixelSnappingDisabled(IDWriteTextRenderer *iface,
446 void *context, BOOL *disabled)
448 struct renderer_context *ctxt = (struct renderer_context*)context;
449 if (ctxt)
450 *disabled = ctxt->snapping_disabled;
451 else
452 *disabled = TRUE;
453 return S_OK;
456 static HRESULT WINAPI testrenderer_GetCurrentTransform(IDWriteTextRenderer *iface,
457 void *context, DWRITE_MATRIX *m)
459 struct renderer_context *ctxt = (struct renderer_context*)context;
460 ok(!ctxt->snapping_disabled, "expected enabled snapping\n");
461 *m = ctxt->m;
462 return S_OK;
465 static HRESULT WINAPI testrenderer_GetPixelsPerDip(IDWriteTextRenderer *iface,
466 void *context, FLOAT *pixels_per_dip)
468 struct renderer_context *ctxt = (struct renderer_context*)context;
469 *pixels_per_dip = ctxt->ppdip;
470 return S_OK;
473 #define TEST_MEASURING_MODE(ctxt, mode) test_measuring_mode(ctxt, mode, __LINE__)
474 static void test_measuring_mode(const struct renderer_context *ctxt, DWRITE_MEASURING_MODE mode, int line)
476 if (ctxt->gdicompat) {
477 if (ctxt->use_gdi_natural)
478 ok_(__FILE__, line)(mode == DWRITE_MEASURING_MODE_GDI_NATURAL, "got %d\n", mode);
479 else
480 ok_(__FILE__, line)(mode == DWRITE_MEASURING_MODE_GDI_CLASSIC, "got %d\n", mode);
482 else
483 ok_(__FILE__, line)(mode == DWRITE_MEASURING_MODE_NATURAL, "got %d\n", mode);
486 static HRESULT WINAPI testrenderer_DrawGlyphRun(IDWriteTextRenderer *iface,
487 void *context,
488 FLOAT baselineOriginX,
489 FLOAT baselineOriginY,
490 DWRITE_MEASURING_MODE mode,
491 DWRITE_GLYPH_RUN const *run,
492 DWRITE_GLYPH_RUN_DESCRIPTION const *descr,
493 IUnknown *effect)
495 struct renderer_context *ctxt = (struct renderer_context*)context;
496 struct drawcall_entry entry;
497 DWRITE_SCRIPT_ANALYSIS sa;
499 if (ctxt) {
500 TEST_MEASURING_MODE(ctxt, mode);
501 ctxt->originX = baselineOriginX;
502 ctxt->originY = baselineOriginY;
505 ok(descr->stringLength < sizeof(entry.string)/sizeof(WCHAR), "string is too long\n");
506 if (descr->stringLength && descr->stringLength < sizeof(entry.string)/sizeof(WCHAR)) {
507 memcpy(entry.string, descr->string, descr->stringLength*sizeof(WCHAR));
508 entry.string[descr->stringLength] = 0;
510 else
511 entry.string[0] = 0;
513 /* see what's reported for control codes runs */
514 get_script_analysis(descr->string, descr->stringLength, &sa);
515 if (sa.shapes == DWRITE_SCRIPT_SHAPES_NO_VISUAL) {
516 UINT32 i;
518 /* glyphs are not reported at all for control code runs */
519 ok(run->glyphCount == 0, "got %u\n", run->glyphCount);
520 ok(run->glyphAdvances != NULL, "advances array %p\n", run->glyphAdvances);
521 ok(run->glyphOffsets != NULL, "offsets array %p\n", run->glyphOffsets);
522 ok(run->fontFace != NULL, "got %p\n", run->fontFace);
523 /* text positions are still valid */
524 ok(descr->string != NULL, "got string %p\n", descr->string);
525 ok(descr->stringLength > 0, "got string length %u\n", descr->stringLength);
526 ok(descr->clusterMap != NULL, "clustermap %p\n", descr->clusterMap);
527 for (i = 0; i < descr->stringLength; i++)
528 ok(descr->clusterMap[i] == i, "got %u\n", descr->clusterMap[i]);
531 entry.kind = DRAW_GLYPHRUN;
532 if (effect)
533 entry.kind |= DRAW_EFFECT;
534 add_call(sequences, RENDERER_ID, &entry);
535 return S_OK;
538 static HRESULT WINAPI testrenderer_DrawUnderline(IDWriteTextRenderer *iface,
539 void *context,
540 FLOAT baselineOriginX,
541 FLOAT baselineOriginY,
542 DWRITE_UNDERLINE const* underline,
543 IUnknown *effect)
545 struct renderer_context *ctxt = (struct renderer_context*)context;
546 struct drawcall_entry entry;
548 if (ctxt)
549 TEST_MEASURING_MODE(ctxt, underline->measuringMode);
551 entry.kind = DRAW_UNDERLINE;
552 if (effect)
553 entry.kind |= DRAW_EFFECT;
554 lstrcpyW(entry.locale, underline->localeName);
555 add_call(sequences, RENDERER_ID, &entry);
556 return S_OK;
559 static HRESULT WINAPI testrenderer_DrawStrikethrough(IDWriteTextRenderer *iface,
560 void *context,
561 FLOAT baselineOriginX,
562 FLOAT baselineOriginY,
563 DWRITE_STRIKETHROUGH const* strikethrough,
564 IUnknown *effect)
566 struct renderer_context *ctxt = (struct renderer_context*)context;
567 struct drawcall_entry entry;
569 if (ctxt)
570 TEST_MEASURING_MODE(ctxt, strikethrough->measuringMode);
572 entry.kind = DRAW_STRIKETHROUGH;
573 if (effect)
574 entry.kind |= DRAW_EFFECT;
575 add_call(sequences, RENDERER_ID, &entry);
576 return S_OK;
579 static HRESULT WINAPI testrenderer_DrawInlineObject(IDWriteTextRenderer *iface,
580 void *context,
581 FLOAT originX,
582 FLOAT originY,
583 IDWriteInlineObject *object,
584 BOOL is_sideways,
585 BOOL is_rtl,
586 IUnknown *effect)
588 struct drawcall_entry entry;
589 entry.kind = DRAW_INLINE;
590 if (effect)
591 entry.kind |= DRAW_EFFECT;
592 add_call(sequences, RENDERER_ID, &entry);
593 return S_OK;
596 static const IDWriteTextRendererVtbl testrenderervtbl = {
597 testrenderer_QI,
598 testrenderer_AddRef,
599 testrenderer_Release,
600 testrenderer_IsPixelSnappingDisabled,
601 testrenderer_GetCurrentTransform,
602 testrenderer_GetPixelsPerDip,
603 testrenderer_DrawGlyphRun,
604 testrenderer_DrawUnderline,
605 testrenderer_DrawStrikethrough,
606 testrenderer_DrawInlineObject
609 static IDWriteTextRenderer testrenderer = { &testrenderervtbl };
611 /* test IDWriteInlineObject */
612 static HRESULT WINAPI testinlineobj_QI(IDWriteInlineObject *iface, REFIID riid, void **obj)
614 if (IsEqualIID(riid, &IID_IDWriteInlineObject) || IsEqualIID(riid, &IID_IUnknown)) {
615 *obj = iface;
616 IDWriteInlineObject_AddRef(iface);
617 return S_OK;
620 *obj = NULL;
621 return E_NOINTERFACE;
624 static ULONG WINAPI testinlineobj_AddRef(IDWriteInlineObject *iface)
626 return 2;
629 static ULONG WINAPI testinlineobj_Release(IDWriteInlineObject *iface)
631 return 1;
634 static HRESULT WINAPI testinlineobj_Draw(IDWriteInlineObject *iface,
635 void* client_drawingontext, IDWriteTextRenderer* renderer,
636 FLOAT originX, FLOAT originY, BOOL is_sideways, BOOL is_rtl, IUnknown *drawing_effect)
638 ok(0, "unexpected call\n");
639 return E_NOTIMPL;
642 static HRESULT WINAPI testinlineobj_GetMetrics(IDWriteInlineObject *iface, DWRITE_INLINE_OBJECT_METRICS *metrics)
644 metrics->width = 123.0;
645 return 0x8faecafe;
648 static HRESULT WINAPI testinlineobj_GetOverhangMetrics(IDWriteInlineObject *iface, DWRITE_OVERHANG_METRICS *overhangs)
650 ok(0, "unexpected call\n");
651 return E_NOTIMPL;
654 static HRESULT WINAPI testinlineobj_GetBreakConditions(IDWriteInlineObject *iface, DWRITE_BREAK_CONDITION *before,
655 DWRITE_BREAK_CONDITION *after)
657 *before = *after = DWRITE_BREAK_CONDITION_MUST_BREAK;
658 return 0x8feacafe;
661 static HRESULT WINAPI testinlineobj2_GetBreakConditions(IDWriteInlineObject *iface, DWRITE_BREAK_CONDITION *before,
662 DWRITE_BREAK_CONDITION *after)
664 *before = *after = DWRITE_BREAK_CONDITION_MAY_NOT_BREAK;
665 return S_OK;
668 static IDWriteInlineObjectVtbl testinlineobjvtbl = {
669 testinlineobj_QI,
670 testinlineobj_AddRef,
671 testinlineobj_Release,
672 testinlineobj_Draw,
673 testinlineobj_GetMetrics,
674 testinlineobj_GetOverhangMetrics,
675 testinlineobj_GetBreakConditions
678 static IDWriteInlineObjectVtbl testinlineobjvtbl2 = {
679 testinlineobj_QI,
680 testinlineobj_AddRef,
681 testinlineobj_Release,
682 testinlineobj_Draw,
683 testinlineobj_GetMetrics,
684 testinlineobj_GetOverhangMetrics,
685 testinlineobj2_GetBreakConditions
688 static IDWriteInlineObject testinlineobj = { &testinlineobjvtbl };
689 static IDWriteInlineObject testinlineobj2 = { &testinlineobjvtbl };
690 static IDWriteInlineObject testinlineobj3 = { &testinlineobjvtbl2 };
692 static HRESULT WINAPI testeffect_QI(IUnknown *iface, REFIID riid, void **obj)
694 if (IsEqualIID(riid, &IID_IUnknown)) {
695 *obj = iface;
696 IUnknown_AddRef(iface);
697 return S_OK;
700 *obj = NULL;
701 return E_NOINTERFACE;
704 static ULONG WINAPI testeffect_AddRef(IUnknown *iface)
706 return 2;
709 static ULONG WINAPI testeffect_Release(IUnknown *iface)
711 return 1;
714 static const IUnknownVtbl testeffectvtbl = {
715 testeffect_QI,
716 testeffect_AddRef,
717 testeffect_Release
720 static IUnknown testeffect = { &testeffectvtbl };
722 static void test_CreateTextLayout(void)
724 static const WCHAR strW[] = {'s','t','r','i','n','g',0};
725 IDWriteTextLayout2 *layout2;
726 IDWriteTextLayout *layout;
727 IDWriteTextFormat *format;
728 IDWriteFactory *factory;
729 HRESULT hr;
731 factory = create_factory();
733 layout = (void*)0xdeadbeef;
734 hr = IDWriteFactory_CreateTextLayout(factory, NULL, 0, NULL, 0.0, 0.0, &layout);
735 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
736 ok(layout == NULL, "got %p\n", layout);
738 layout = (void*)0xdeadbeef;
739 hr = IDWriteFactory_CreateTextLayout(factory, strW, 6, NULL, 0.0, 0.0, &layout);
740 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
741 ok(layout == NULL, "got %p\n", layout);
743 layout = (void*)0xdeadbeef;
744 hr = IDWriteFactory_CreateTextLayout(factory, strW, 6, NULL, 1.0, 0.0, &layout);
745 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
746 ok(layout == NULL, "got %p\n", layout);
748 layout = (void*)0xdeadbeef;
749 hr = IDWriteFactory_CreateTextLayout(factory, strW, 6, NULL, 0.0, 1.0, &layout);
750 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
751 ok(layout == NULL, "got %p\n", layout);
753 layout = (void*)0xdeadbeef;
754 hr = IDWriteFactory_CreateTextLayout(factory, strW, 6, NULL, 1000.0, 1000.0, &layout);
755 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
756 ok(layout == NULL, "got %p\n", layout);
758 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
759 DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format);
760 ok(hr == S_OK, "got 0x%08x\n", hr);
762 layout = (void*)0xdeadbeef;
763 hr = IDWriteFactory_CreateTextLayout(factory, NULL, 0, format, 100.0f, 100.0f, &layout);
764 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
765 ok(layout == NULL, "got %p\n", layout);
767 hr = IDWriteFactory_CreateTextLayout(factory, strW, 0, format, 0.0f, 0.0f, &layout);
768 ok(hr == S_OK, "got 0x%08x\n", hr);
769 IDWriteTextLayout_Release(layout);
771 EXPECT_REF(format, 1);
772 hr = IDWriteFactory_CreateTextLayout(factory, strW, 6, format, 1000.0, 1000.0, &layout);
773 ok(hr == S_OK, "got 0x%08x\n", hr);
774 EXPECT_REF(format, 1);
776 hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextLayout2, (void**)&layout2);
777 if (hr == S_OK) {
778 IDWriteTextLayout1 *layout1;
779 IDWriteTextFormat1 *format1;
780 IDWriteTextFormat *format;
782 hr = IDWriteTextLayout2_QueryInterface(layout2, &IID_IDWriteTextLayout1, (void**)&layout1);
783 ok(hr == S_OK, "got 0x%08x\n", hr);
784 IDWriteTextLayout1_Release(layout1);
786 EXPECT_REF(layout2, 2);
787 hr = IDWriteTextLayout2_QueryInterface(layout2, &IID_IDWriteTextFormat1, (void**)&format1);
788 ok(hr == S_OK, "got 0x%08x\n", hr);
789 EXPECT_REF(layout2, 3);
791 hr = IDWriteTextLayout2_QueryInterface(layout2, &IID_IDWriteTextFormat, (void**)&format);
792 ok(hr == S_OK, "got 0x%08x\n", hr);
793 ok(format == (IDWriteTextFormat*)format1, "got %p, %p\n", format, format1);
794 ok(format != (IDWriteTextFormat*)layout2, "got %p, %p\n", format, layout2);
795 EXPECT_REF(layout2, 4);
797 hr = IDWriteTextFormat_QueryInterface(format, &IID_IDWriteTextLayout1, (void**)&layout1);
798 ok(hr == S_OK, "got 0x%08x\n", hr);
799 IDWriteTextLayout1_Release(layout1);
801 IDWriteTextFormat1_Release(format1);
802 IDWriteTextFormat_Release(format);
804 hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextFormat1, (void**)&format1);
805 ok(hr == S_OK, "got 0x%08x\n", hr);
806 EXPECT_REF(layout2, 3);
808 hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextFormat, (void**)&format);
809 ok(hr == S_OK, "got 0x%08x\n", hr);
810 ok(format == (IDWriteTextFormat*)format1, "got %p, %p\n", format, format1);
811 EXPECT_REF(layout2, 4);
813 IDWriteTextFormat1_Release(format1);
814 IDWriteTextFormat_Release(format);
815 IDWriteTextLayout2_Release(layout2);
817 else
818 win_skip("IDWriteTextLayout2 is not supported.\n");
820 IDWriteTextLayout_Release(layout);
821 IDWriteTextFormat_Release(format);
822 IDWriteFactory_Release(factory);
825 static DWRITE_MATRIX layoutcreate_transforms[] = {
826 { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 },
827 { 1.0, 0.0, 0.0, 1.0, 0.3, 0.2 },
828 { 1.0, 0.0, 0.0, 1.0,-0.3,-0.2 },
830 { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 },
831 { 1.0, 0.0, 0.0, 0.0, 0.0, 0.0 },
832 { 1.0, 2.0, 0.5, 1.0, 0.0, 0.0 },
835 static void test_CreateGdiCompatibleTextLayout(void)
837 static const WCHAR strW[] = {'s','t','r','i','n','g',0};
838 IDWriteTextLayout *layout;
839 IDWriteTextFormat *format;
840 IDWriteFactory *factory;
841 FLOAT dimension;
842 HRESULT hr;
843 int i;
845 factory = create_factory();
847 layout = (void*)0xdeadbeef;
848 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, NULL, 0, NULL, 0.0, 0.0, 0.0, NULL, FALSE, &layout);
849 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
850 ok(layout == NULL, "got %p\n", layout);
852 layout = (void*)0xdeadbeef;
853 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, strW, 6, NULL, 0.0, 0.0, 0.0, NULL, FALSE, &layout);
854 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
855 ok(layout == NULL, "got %p\n", layout);
857 layout = (void*)0xdeadbeef;
858 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, strW, 6, NULL, 1.0, 0.0, 0.0, NULL, FALSE, &layout);
859 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
860 ok(layout == NULL, "got %p\n", layout);
862 layout = (void*)0xdeadbeef;
863 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, strW, 6, NULL, 1.0, 0.0, 1.0, NULL, FALSE, &layout);
864 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
865 ok(layout == NULL, "got %p\n", layout);
867 layout = (void*)0xdeadbeef;
868 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, strW, 6, NULL, 1000.0, 1000.0, 1.0, NULL, FALSE, &layout);
869 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
870 ok(layout == NULL, "got %p\n", layout);
872 /* create with text format */
873 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
874 DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format);
875 ok(hr == S_OK, "got 0x%08x\n", hr);
876 EXPECT_REF(format, 1);
878 layout = (void*)0xdeadbeef;
879 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, NULL, 0, format, 100.0f, 100.0f, 1.0f, NULL, FALSE, &layout);
880 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
881 ok(layout == NULL, "got %p\n", layout);
883 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, strW, 6, format, 100.0, 100.0, 1.0, NULL, FALSE, &layout);
884 ok(hr == S_OK, "got 0x%08x\n", hr);
885 EXPECT_REF(format, 1);
886 EXPECT_REF(layout, 1);
888 IDWriteTextLayout_AddRef(layout);
889 EXPECT_REF(format, 1);
890 EXPECT_REF(layout, 2);
891 IDWriteTextLayout_Release(layout);
892 IDWriteTextLayout_Release(layout);
894 /* zero length string is okay */
895 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, strW, 0, format, 100.0, 100.0, 1.0, NULL, FALSE, &layout);
896 ok(hr == S_OK, "got 0x%08x\n", hr);
898 dimension = IDWriteTextLayout_GetMaxWidth(layout);
899 ok(dimension == 100.0, "got %f\n", dimension);
901 dimension = IDWriteTextLayout_GetMaxHeight(layout);
902 ok(dimension == 100.0, "got %f\n", dimension);
904 IDWriteTextLayout_Release(layout);
906 /* negative, zero ppdip */
907 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, strW, 1, format, 100.0, 100.0, -1.0, NULL, FALSE, &layout);
908 ok(hr == S_OK, "got 0x%08x\n", hr);
909 IDWriteTextLayout_Release(layout);
911 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, strW, 1, format, 100.0, 100.0, 0.0, NULL, FALSE, &layout);
912 ok(hr == S_OK, "got 0x%08x\n", hr);
913 IDWriteTextLayout_Release(layout);
915 /* transforms */
916 for (i = 0; i < sizeof(layoutcreate_transforms)/sizeof(layoutcreate_transforms[0]); i++) {
917 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, strW, 1, format, 100.0, 100.0, 1.0,
918 &layoutcreate_transforms[i], FALSE, &layout);
919 ok(hr == S_OK, "got 0x%08x\n", hr);
920 IDWriteTextLayout_Release(layout);
923 IDWriteTextFormat_Release(format);
924 IDWriteFactory_Release(factory);
927 static void test_CreateTextFormat(void)
929 IDWriteFontCollection *collection, *syscoll;
930 DWRITE_PARAGRAPH_ALIGNMENT paralign;
931 DWRITE_READING_DIRECTION readdir;
932 DWRITE_WORD_WRAPPING wrapping;
933 DWRITE_TEXT_ALIGNMENT align;
934 DWRITE_FLOW_DIRECTION flow;
935 DWRITE_LINE_SPACING_METHOD method;
936 DWRITE_TRIMMING trimming;
937 IDWriteTextFormat *format;
938 FLOAT spacing, baseline;
939 IDWriteInlineObject *trimmingsign;
940 IDWriteFactory *factory;
941 HRESULT hr;
943 factory = create_factory();
945 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
946 DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format);
947 ok(hr == S_OK, "got 0x%08x\n", hr);
949 if (0) /* crashes on native */
950 hr = IDWriteTextFormat_GetFontCollection(format, NULL);
952 collection = NULL;
953 hr = IDWriteTextFormat_GetFontCollection(format, &collection);
954 ok(hr == S_OK, "got 0x%08x\n", hr);
955 ok(collection != NULL, "got %p\n", collection);
957 hr = IDWriteFactory_GetSystemFontCollection(factory, &syscoll, FALSE);
958 ok(hr == S_OK, "got 0x%08x\n", hr);
959 ok(collection == syscoll, "got %p, was %p\n", syscoll, collection);
960 IDWriteFontCollection_Release(syscoll);
961 IDWriteFontCollection_Release(collection);
963 /* default format properties */
964 align = IDWriteTextFormat_GetTextAlignment(format);
965 ok(align == DWRITE_TEXT_ALIGNMENT_LEADING, "got %d\n", align);
967 paralign = IDWriteTextFormat_GetParagraphAlignment(format);
968 ok(paralign == DWRITE_PARAGRAPH_ALIGNMENT_NEAR, "got %d\n", paralign);
970 wrapping = IDWriteTextFormat_GetWordWrapping(format);
971 ok(wrapping == DWRITE_WORD_WRAPPING_WRAP, "got %d\n", wrapping);
973 readdir = IDWriteTextFormat_GetReadingDirection(format);
974 ok(readdir == DWRITE_READING_DIRECTION_LEFT_TO_RIGHT, "got %d\n", readdir);
976 flow = IDWriteTextFormat_GetFlowDirection(format);
977 ok(flow == DWRITE_FLOW_DIRECTION_TOP_TO_BOTTOM, "got %d\n", flow);
979 hr = IDWriteTextFormat_GetLineSpacing(format, &method, &spacing, &baseline);
980 ok(hr == S_OK, "got 0x%08x\n", hr);
981 ok(spacing == 0.0, "got %f\n", spacing);
982 ok(baseline == 0.0, "got %f\n", baseline);
983 ok(method == DWRITE_LINE_SPACING_METHOD_DEFAULT, "got %d\n", method);
985 trimming.granularity = DWRITE_TRIMMING_GRANULARITY_WORD;
986 trimming.delimiter = 10;
987 trimming.delimiterCount = 10;
988 trimmingsign = (void*)0xdeadbeef;
989 hr = IDWriteTextFormat_GetTrimming(format, &trimming, &trimmingsign);
990 ok(hr == S_OK, "got 0x%08x\n", hr);
991 ok(trimming.granularity == DWRITE_TRIMMING_GRANULARITY_NONE, "got %d\n", trimming.granularity);
992 ok(trimming.delimiter == 0, "got %d\n", trimming.delimiter);
993 ok(trimming.delimiterCount == 0, "got %d\n", trimming.delimiterCount);
994 ok(trimmingsign == NULL, "got %p\n", trimmingsign);
996 /* setters */
997 hr = IDWriteTextFormat_SetTextAlignment(format, DWRITE_TEXT_ALIGNMENT_LEADING);
998 ok(hr == S_OK, "got 0x%08x\n", hr);
1000 hr = IDWriteTextFormat_SetTextAlignment(format, DWRITE_TEXT_ALIGNMENT_JUSTIFIED+1);
1001 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1003 hr = IDWriteTextFormat_SetParagraphAlignment(format, DWRITE_PARAGRAPH_ALIGNMENT_NEAR);
1004 ok(hr == S_OK, "got 0x%08x\n", hr);
1006 hr = IDWriteTextFormat_SetParagraphAlignment(format, DWRITE_PARAGRAPH_ALIGNMENT_CENTER+1);
1007 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1009 hr = IDWriteTextFormat_SetWordWrapping(format, DWRITE_WORD_WRAPPING_WRAP);
1010 ok(hr == S_OK, "got 0x%08x\n", hr);
1012 hr = IDWriteTextFormat_SetWordWrapping(format, DWRITE_WORD_WRAPPING_CHARACTER+1);
1013 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1015 hr = IDWriteTextFormat_SetReadingDirection(format, DWRITE_READING_DIRECTION_LEFT_TO_RIGHT);
1016 ok(hr == S_OK, "got 0x%08x\n", hr);
1018 hr = IDWriteTextFormat_SetFlowDirection(format, DWRITE_FLOW_DIRECTION_TOP_TO_BOTTOM);
1019 ok(hr == S_OK, "got 0x%08x\n", hr);
1021 hr = IDWriteTextFormat_SetLineSpacing(format, DWRITE_LINE_SPACING_METHOD_DEFAULT, 0.0, 0.0);
1022 ok(hr == S_OK, "got 0x%08x\n", hr);
1024 hr = IDWriteTextFormat_SetLineSpacing(format, DWRITE_LINE_SPACING_METHOD_DEFAULT, 0.0, -10.0);
1025 ok(hr == S_OK, "got 0x%08x\n", hr);
1027 hr = IDWriteTextFormat_SetLineSpacing(format, DWRITE_LINE_SPACING_METHOD_DEFAULT, -10.0, 0.0);
1028 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1030 hr = IDWriteTextFormat_SetLineSpacing(format, DWRITE_LINE_SPACING_METHOD_PROPORTIONAL+1, 0.0, 0.0);
1031 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1033 hr = IDWriteTextFormat_SetTrimming(format, &trimming, NULL);
1034 ok(hr == S_OK, "got 0x%08x\n", hr);
1036 IDWriteTextFormat_Release(format);
1037 IDWriteFactory_Release(factory);
1040 static void test_GetLocaleName(void)
1042 static const WCHAR strW[] = {'s','t','r','i','n','g',0};
1043 static const WCHAR ruW[] = {'r','u',0};
1044 IDWriteTextLayout *layout;
1045 IDWriteTextFormat *format, *format2;
1046 IDWriteFactory *factory;
1047 WCHAR buff[10];
1048 UINT32 len;
1049 HRESULT hr;
1051 factory = create_factory();
1053 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
1054 DWRITE_FONT_STRETCH_NORMAL, 10.0, ruW, &format);
1055 ok(hr == S_OK, "got 0x%08x\n", hr);
1057 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, strW, 0, format, 100.0, 100.0, 1.0, NULL, FALSE, &layout);
1058 ok(hr == S_OK, "got 0x%08x\n", hr);
1060 hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextFormat, (void**)&format2);
1061 ok(hr == S_OK, "got 0x%08x\n", hr);
1063 len = IDWriteTextFormat_GetLocaleNameLength(format2);
1064 ok(len == 2, "got %u\n", len);
1065 len = IDWriteTextFormat_GetLocaleNameLength(format);
1066 ok(len == 2, "got %u\n", len);
1067 hr = IDWriteTextFormat_GetLocaleName(format2, buff, len);
1068 ok(hr == E_NOT_SUFFICIENT_BUFFER, "got 0x%08x\n", hr);
1069 hr = IDWriteTextFormat_GetLocaleName(format2, buff, len+1);
1070 ok(hr == S_OK, "got 0x%08x\n", hr);
1071 ok(!lstrcmpW(buff, ruW), "got %s\n", wine_dbgstr_w(buff));
1072 hr = IDWriteTextFormat_GetLocaleName(format, buff, len);
1073 ok(hr == E_NOT_SUFFICIENT_BUFFER, "got 0x%08x\n", hr);
1074 hr = IDWriteTextFormat_GetLocaleName(format, buff, len+1);
1075 ok(hr == S_OK, "got 0x%08x\n", hr);
1076 ok(!lstrcmpW(buff, ruW), "got %s\n", wine_dbgstr_w(buff));
1078 IDWriteTextLayout_Release(layout);
1079 IDWriteTextFormat_Release(format);
1080 IDWriteTextFormat_Release(format2);
1081 IDWriteFactory_Release(factory);
1084 static const struct drawcall_entry drawellipsis_seq[] = {
1085 { DRAW_GLYPHRUN, {0x2026, 0} },
1086 { DRAW_LAST_KIND }
1089 static void test_CreateEllipsisTrimmingSign(void)
1091 DWRITE_INLINE_OBJECT_METRICS metrics;
1092 DWRITE_BREAK_CONDITION before, after;
1093 IDWriteTextFormat *format;
1094 IDWriteInlineObject *sign;
1095 IDWriteFactory *factory;
1096 IUnknown *unk;
1097 HRESULT hr;
1099 factory = create_factory();
1101 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
1102 DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format);
1103 ok(hr == S_OK, "got 0x%08x\n", hr);
1105 EXPECT_REF(format, 1);
1106 hr = IDWriteFactory_CreateEllipsisTrimmingSign(factory, format, &sign);
1107 ok(hr == S_OK, "got 0x%08x\n", hr);
1108 EXPECT_REF(format, 1);
1110 hr = IDWriteInlineObject_QueryInterface(sign, &IID_IDWriteTextLayout, (void**)&unk);
1111 ok(hr == E_NOINTERFACE, "got 0x%08x\n", hr);
1113 if (0) {/* crashes on native */
1114 hr = IDWriteInlineObject_GetBreakConditions(sign, NULL, NULL);
1115 hr = IDWriteInlineObject_GetMetrics(sign, NULL);
1117 metrics.width = 0.0;
1118 metrics.height = 123.0;
1119 metrics.baseline = 123.0;
1120 metrics.supportsSideways = TRUE;
1121 hr = IDWriteInlineObject_GetMetrics(sign, &metrics);
1122 ok(hr == S_OK, "got 0x%08x\n", hr);
1123 ok(metrics.width > 0.0, "got %.2f\n", metrics.width);
1124 ok(metrics.height == 0.0, "got %.2f\n", metrics.height);
1125 ok(metrics.baseline == 0.0, "got %.2f\n", metrics.baseline);
1126 ok(!metrics.supportsSideways, "got %d\n", metrics.supportsSideways);
1128 before = after = DWRITE_BREAK_CONDITION_CAN_BREAK;
1129 hr = IDWriteInlineObject_GetBreakConditions(sign, &before, &after);
1130 ok(hr == S_OK, "got 0x%08x\n", hr);
1131 ok(before == DWRITE_BREAK_CONDITION_NEUTRAL, "got %d\n", before);
1132 ok(after == DWRITE_BREAK_CONDITION_NEUTRAL, "got %d\n", after);
1134 /* Draw tests */
1135 flush_sequence(sequences, RENDERER_ID);
1136 hr = IDWriteInlineObject_Draw(sign, NULL, &testrenderer, 0.0, 0.0, FALSE, FALSE, NULL);
1137 ok(hr == S_OK, "got 0x%08x\n", hr);
1138 ok_sequence(sequences, RENDERER_ID, drawellipsis_seq, "ellipsis sign draw test", FALSE);
1139 IDWriteInlineObject_Release(sign);
1141 /* non-orthogonal flow/reading combination */
1142 hr = IDWriteTextFormat_SetReadingDirection(format, DWRITE_READING_DIRECTION_LEFT_TO_RIGHT);
1143 ok(hr == S_OK, "got 0x%08x\n", hr);
1145 hr = IDWriteTextFormat_SetFlowDirection(format, DWRITE_FLOW_DIRECTION_LEFT_TO_RIGHT);
1146 ok(hr == S_OK || broken(hr == E_INVALIDARG) /* vista, win7 */, "got 0x%08x\n", hr);
1147 if (hr == S_OK) {
1148 hr = IDWriteFactory_CreateEllipsisTrimmingSign(factory, format, &sign);
1149 ok(hr == DWRITE_E_FLOWDIRECTIONCONFLICTS, "got 0x%08x\n", hr);
1152 IDWriteTextFormat_Release(format);
1153 IDWriteFactory_Release(factory);
1156 static void test_fontweight(void)
1158 static const WCHAR strW[] = {'s','t','r','i','n','g',0};
1159 static const WCHAR ruW[] = {'r','u',0};
1160 IDWriteTextFormat *format, *fmt2;
1161 IDWriteTextLayout *layout;
1162 DWRITE_FONT_WEIGHT weight;
1163 DWRITE_TEXT_RANGE range;
1164 IDWriteFactory *factory;
1165 FLOAT size;
1166 HRESULT hr;
1168 factory = create_factory();
1170 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_BOLD, DWRITE_FONT_STYLE_NORMAL,
1171 DWRITE_FONT_STRETCH_NORMAL, 10.0, ruW, &format);
1172 ok(hr == S_OK, "got 0x%08x\n", hr);
1174 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, strW, 6, format, 100.0, 100.0, 1.0, NULL, FALSE, &layout);
1175 ok(hr == S_OK, "got 0x%08x\n", hr);
1177 hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextFormat, (void**)&fmt2);
1178 ok(hr == S_OK, "got 0x%08x\n", hr);
1180 weight = IDWriteTextFormat_GetFontWeight(fmt2);
1181 ok(weight == DWRITE_FONT_WEIGHT_BOLD, "got %u\n", weight);
1183 range.startPosition = range.length = 0;
1184 hr = IDWriteTextLayout_GetFontWeight(layout, 0, &weight, &range);
1185 ok(hr == S_OK, "got 0x%08x\n", hr);
1186 ok(range.startPosition == 0 && range.length == ~0u, "got %u, %u\n", range.startPosition, range.length);
1188 range.startPosition = 0;
1189 range.length = 6;
1190 hr = IDWriteTextLayout_SetFontWeight(layout, DWRITE_FONT_WEIGHT_NORMAL, range);
1191 ok(hr == S_OK, "got 0x%08x\n", hr);
1193 range.startPosition = range.length = 0;
1194 hr = IDWriteTextLayout_GetFontWeight(layout, 0, &weight, &range);
1195 ok(hr == S_OK, "got 0x%08x\n", hr);
1196 ok(range.startPosition == 0 && range.length == 6, "got %u, %u\n", range.startPosition, range.length);
1198 /* IDWriteTextFormat methods output doesn't reflect layout changes */
1199 weight = IDWriteTextFormat_GetFontWeight(fmt2);
1200 ok(weight == DWRITE_FONT_WEIGHT_BOLD, "got %u\n", weight);
1202 range.length = 0;
1203 weight = DWRITE_FONT_WEIGHT_BOLD;
1204 hr = IDWriteTextLayout_GetFontWeight(layout, 0, &weight, &range);
1205 ok(hr == S_OK, "got 0x%08x\n", hr);
1206 ok(weight == DWRITE_FONT_WEIGHT_NORMAL, "got %d\n", weight);
1207 ok(range.length == 6, "got %d\n", range.length);
1209 size = IDWriteTextLayout_GetMaxWidth(layout);
1210 ok(size == 100.0, "got %.2f\n", size);
1212 hr = IDWriteTextLayout_SetMaxWidth(layout, 0.0);
1213 ok(hr == S_OK, "got 0x%08x\n", hr);
1215 size = IDWriteTextLayout_GetMaxWidth(layout);
1216 ok(size == 0.0, "got %.2f\n", size);
1218 hr = IDWriteTextLayout_SetMaxWidth(layout, -1.0);
1219 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1221 size = IDWriteTextLayout_GetMaxWidth(layout);
1222 ok(size == 0.0, "got %.2f\n", size);
1224 hr = IDWriteTextLayout_SetMaxWidth(layout, 100.0);
1225 ok(hr == S_OK, "got 0x%08x\n", hr);
1227 size = IDWriteTextLayout_GetMaxWidth(layout);
1228 ok(size == 100.0, "got %.2f\n", size);
1230 size = IDWriteTextLayout_GetMaxHeight(layout);
1231 ok(size == 100.0, "got %.2f\n", size);
1233 hr = IDWriteTextLayout_SetMaxHeight(layout, 0.0);
1234 ok(hr == S_OK, "got 0x%08x\n", hr);
1236 size = IDWriteTextLayout_GetMaxHeight(layout);
1237 ok(size == 0.0, "got %.2f\n", size);
1239 hr = IDWriteTextLayout_SetMaxHeight(layout, -1.0);
1240 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1242 size = IDWriteTextLayout_GetMaxHeight(layout);
1243 ok(size == 0.0, "got %.2f\n", size);
1245 hr = IDWriteTextLayout_SetMaxHeight(layout, 100.0);
1246 ok(hr == S_OK, "got 0x%08x\n", hr);
1248 size = IDWriteTextLayout_GetMaxHeight(layout);
1249 ok(size == 100.0, "got %.2f\n", size);
1251 IDWriteTextLayout_Release(layout);
1252 IDWriteTextFormat_Release(fmt2);
1253 IDWriteTextFormat_Release(format);
1254 IDWriteFactory_Release(factory);
1257 static void test_SetInlineObject(void)
1259 static const WCHAR strW[] = {'s','t','r','i','n','g',0};
1260 static const WCHAR ruW[] = {'r','u',0};
1262 IDWriteInlineObject *inlineobj, *inlineobj2, *inlinetest;
1263 IDWriteTextFormat *format;
1264 IDWriteTextLayout *layout;
1265 DWRITE_TEXT_RANGE range, r2;
1266 IDWriteFactory *factory;
1267 HRESULT hr;
1269 factory = create_factory();
1271 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_BOLD, DWRITE_FONT_STYLE_NORMAL,
1272 DWRITE_FONT_STRETCH_NORMAL, 10.0, ruW, &format);
1273 ok(hr == S_OK, "got 0x%08x\n", hr);
1275 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, strW, 6, format, 100.0, 100.0, 1.0, NULL, FALSE, &layout);
1276 ok(hr == S_OK, "got 0x%08x\n", hr);
1278 hr = IDWriteFactory_CreateEllipsisTrimmingSign(factory, format, &inlineobj);
1279 ok(hr == S_OK, "got 0x%08x\n", hr);
1281 hr = IDWriteFactory_CreateEllipsisTrimmingSign(factory, format, &inlineobj2);
1282 ok(hr == S_OK, "got 0x%08x\n", hr);
1284 EXPECT_REF(inlineobj, 1);
1285 EXPECT_REF(inlineobj2, 1);
1287 inlinetest = (void*)0x1;
1288 hr = IDWriteTextLayout_GetInlineObject(layout, 0, &inlinetest, NULL);
1289 ok(hr == S_OK, "got 0x%08x\n", hr);
1290 ok(inlinetest == NULL, "got %p\n", inlinetest);
1292 range.startPosition = 0;
1293 range.length = 2;
1294 hr = IDWriteTextLayout_SetInlineObject(layout, inlineobj, range);
1295 ok(hr == S_OK, "got 0x%08x\n", hr);
1297 EXPECT_REF(inlineobj, 2);
1299 inlinetest = (void*)0x1;
1300 hr = IDWriteTextLayout_GetInlineObject(layout, 2, &inlinetest, NULL);
1301 ok(hr == S_OK, "got 0x%08x\n", hr);
1302 ok(inlinetest == NULL, "got %p\n", inlinetest);
1304 inlinetest = NULL;
1305 r2.startPosition = r2.length = 100;
1306 hr = IDWriteTextLayout_GetInlineObject(layout, 0, &inlinetest, &r2);
1307 ok(hr == S_OK, "got 0x%08x\n", hr);
1308 ok(inlinetest == inlineobj, "got %p\n", inlinetest);
1309 ok(r2.startPosition == 0 && r2.length == 2, "got %d, %d\n", r2.startPosition, r2.length);
1310 IDWriteInlineObject_Release(inlinetest);
1312 EXPECT_REF(inlineobj, 2);
1314 /* get from somewhere inside a range */
1315 inlinetest = NULL;
1316 r2.startPosition = r2.length = 100;
1317 hr = IDWriteTextLayout_GetInlineObject(layout, 1, &inlinetest, &r2);
1318 ok(hr == S_OK, "got 0x%08x\n", hr);
1319 ok(inlinetest == inlineobj, "got %p\n", inlinetest);
1320 ok(r2.startPosition == 0 && r2.length == 2, "got %d, %d\n", r2.startPosition, r2.length);
1321 IDWriteInlineObject_Release(inlinetest);
1323 EXPECT_REF(inlineobj, 2);
1325 range.startPosition = 1;
1326 range.length = 1;
1327 hr = IDWriteTextLayout_SetInlineObject(layout, inlineobj2, range);
1328 ok(hr == S_OK, "got 0x%08x\n", hr);
1330 inlinetest = NULL;
1331 r2.startPosition = r2.length = 100;
1332 hr = IDWriteTextLayout_GetInlineObject(layout, 1, &inlinetest, &r2);
1333 ok(hr == S_OK, "got 0x%08x\n", hr);
1334 ok(inlinetest == inlineobj2, "got %p\n", inlinetest);
1335 ok(r2.startPosition == 1 && r2.length == 1, "got %d, %d\n", r2.startPosition, r2.length);
1336 IDWriteInlineObject_Release(inlinetest);
1338 EXPECT_REF(inlineobj, 2);
1339 EXPECT_REF(inlineobj2, 2);
1341 inlinetest = NULL;
1342 r2.startPosition = r2.length = 100;
1343 hr = IDWriteTextLayout_GetInlineObject(layout, 0, &inlinetest, &r2);
1344 ok(hr == S_OK, "got 0x%08x\n", hr);
1345 ok(inlinetest == inlineobj, "got %p\n", inlinetest);
1346 ok(r2.startPosition == 0 && r2.length == 1, "got %d, %d\n", r2.startPosition, r2.length);
1347 IDWriteInlineObject_Release(inlinetest);
1349 EXPECT_REF(inlineobj, 2);
1351 range.startPosition = 1;
1352 range.length = 1;
1353 hr = IDWriteTextLayout_SetInlineObject(layout, inlineobj, range);
1354 ok(hr == S_OK, "got 0x%08x\n", hr);
1356 r2.startPosition = r2.length = 100;
1357 hr = IDWriteTextLayout_GetInlineObject(layout, 0, &inlinetest, &r2);
1358 ok(hr == S_OK, "got 0x%08x\n", hr);
1359 ok(inlinetest == inlineobj, "got %p\n", inlinetest);
1360 ok(r2.startPosition == 0 && r2.length == 2, "got %d, %d\n", r2.startPosition, r2.length);
1361 IDWriteInlineObject_Release(inlinetest);
1363 EXPECT_REF(inlineobj, 2);
1365 range.startPosition = 1;
1366 range.length = 2;
1367 hr = IDWriteTextLayout_SetInlineObject(layout, inlineobj, range);
1368 ok(hr == S_OK, "got 0x%08x\n", hr);
1370 EXPECT_REF(inlineobj, 2);
1372 r2.startPosition = r2.length = 100;
1373 hr = IDWriteTextLayout_GetInlineObject(layout, 0, &inlinetest, &r2);
1374 ok(hr == S_OK, "got 0x%08x\n", hr);
1375 ok(inlinetest == inlineobj, "got %p\n", inlinetest);
1376 ok(r2.startPosition == 0 && r2.length == 3, "got %d, %d\n", r2.startPosition, r2.length);
1377 IDWriteInlineObject_Release(inlinetest);
1379 EXPECT_REF(inlineobj, 2);
1380 EXPECT_REF(inlineobj2, 1);
1382 IDWriteTextLayout_Release(layout);
1384 EXPECT_REF(inlineobj, 1);
1386 IDWriteInlineObject_Release(inlineobj);
1387 IDWriteInlineObject_Release(inlineobj2);
1388 IDWriteTextFormat_Release(format);
1389 IDWriteFactory_Release(factory);
1392 /* drawing calls sequence doesn't depend on run order, instead all runs are
1393 drawn first, inline objects next and then underline/strikes */
1394 static const struct drawcall_entry draw_seq[] = {
1395 { DRAW_GLYPHRUN, {'s',0} },
1396 { DRAW_GLYPHRUN, {'r','i',0} },
1397 { DRAW_GLYPHRUN|DRAW_EFFECT, {'n',0} },
1398 { DRAW_GLYPHRUN, {'g',0} },
1399 { DRAW_INLINE },
1400 { DRAW_UNDERLINE, {0}, {'r','u',0} },
1401 { DRAW_STRIKETHROUGH },
1402 { DRAW_LAST_KIND }
1405 static const struct drawcall_entry draw_seq2[] = {
1406 { DRAW_GLYPHRUN, {'s',0} },
1407 { DRAW_GLYPHRUN, {'t',0} },
1408 { DRAW_GLYPHRUN, {'r',0} },
1409 { DRAW_GLYPHRUN, {'i',0} },
1410 { DRAW_GLYPHRUN, {'n',0} },
1411 { DRAW_GLYPHRUN, {'g',0} },
1412 { DRAW_LAST_KIND }
1415 static const struct drawcall_entry draw_seq3[] = {
1416 { DRAW_GLYPHRUN, {0x202a,0x202c,0} },
1417 { DRAW_GLYPHRUN, {'a','b',0} },
1418 { DRAW_LAST_KIND }
1421 static const struct drawcall_entry draw_seq4[] = {
1422 { DRAW_GLYPHRUN, {'s','t','r',0} },
1423 { DRAW_GLYPHRUN, {'i','n','g',0} },
1424 { DRAW_STRIKETHROUGH },
1425 { DRAW_LAST_KIND }
1428 static const struct drawcall_entry draw_seq5[] = {
1429 { DRAW_GLYPHRUN, {'s','t',0} },
1430 { DRAW_GLYPHRUN, {'r','i',0} },
1431 { DRAW_GLYPHRUN, {'n','g',0} },
1432 { DRAW_STRIKETHROUGH },
1433 { DRAW_LAST_KIND }
1436 static const struct drawcall_entry empty_seq[] = {
1437 { DRAW_LAST_KIND }
1440 static const struct drawcall_entry draw_single_run_seq[] = {
1441 { DRAW_GLYPHRUN, {'s','t','r','i','n','g',0} },
1442 { DRAW_LAST_KIND }
1445 static const struct drawcall_entry draw_reordered_run_seq[] = {
1446 { DRAW_GLYPHRUN, {'1','2','3','-','5','2',0} },
1447 { DRAW_GLYPHRUN, {0x64a,0x64f,0x633,0x627,0x648,0x650,0x64a,0} },
1448 { DRAW_GLYPHRUN, {'7','1',0} },
1449 { DRAW_GLYPHRUN, {'.',0} },
1450 { DRAW_LAST_KIND }
1453 static void test_Draw(void)
1455 static const WCHAR str3W[] = {'1','2','3','-','5','2',0x64a,0x64f,0x633,0x627,0x648,0x650,
1456 0x64a,'7','1','.',0};
1457 static const WCHAR strW[] = {'s','t','r','i','n','g',0};
1458 static const WCHAR str2W[] = {0x202a,0x202c,'a','b',0};
1459 static const WCHAR ruW[] = {'r','u',0};
1460 IDWriteInlineObject *inlineobj;
1461 struct renderer_context ctxt;
1462 IDWriteTextFormat *format;
1463 IDWriteTextLayout *layout;
1464 DWRITE_TEXT_RANGE range;
1465 IDWriteFactory *factory;
1466 DWRITE_TEXT_METRICS tm;
1467 DWRITE_MATRIX m;
1468 HRESULT hr;
1470 factory = create_factory();
1472 ctxt.gdicompat = FALSE;
1473 ctxt.use_gdi_natural = FALSE;
1474 ctxt.snapping_disabled = TRUE;
1476 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_BOLD, DWRITE_FONT_STYLE_NORMAL,
1477 DWRITE_FONT_STRETCH_NORMAL, 10.0, ruW, &format);
1478 ok(hr == S_OK, "got 0x%08x\n", hr);
1480 hr = IDWriteFactory_CreateTextLayout(factory, strW, 6, format, 100.0, 100.0, &layout);
1481 ok(hr == S_OK, "got 0x%08x\n", hr);
1483 hr = IDWriteFactory_CreateEllipsisTrimmingSign(factory, format, &inlineobj);
1484 ok(hr == S_OK, "got 0x%08x\n", hr);
1486 range.startPosition = 5;
1487 range.length = 1;
1488 hr = IDWriteTextLayout_SetStrikethrough(layout, TRUE, range);
1489 ok(hr == S_OK, "got 0x%08x\n", hr);
1491 range.startPosition = 1;
1492 range.length = 1;
1493 hr = IDWriteTextLayout_SetInlineObject(layout, inlineobj, range);
1494 ok(hr == S_OK, "got 0x%08x\n", hr);
1496 range.startPosition = 4;
1497 range.length = 1;
1498 hr = IDWriteTextLayout_SetDrawingEffect(layout, (IUnknown*)inlineobj, range);
1499 ok(hr == S_OK, "got 0x%08x\n", hr);
1501 range.startPosition = 0;
1502 range.length = 1;
1503 hr = IDWriteTextLayout_SetUnderline(layout, TRUE, range);
1504 ok(hr == S_OK, "got 0x%08x\n", hr);
1506 flush_sequence(sequences, RENDERER_ID);
1507 hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0, 0.0);
1508 ok(hr == S_OK, "got 0x%08x\n", hr);
1509 ok_sequence(sequences, RENDERER_ID, draw_seq, "draw test", FALSE);
1510 IDWriteTextLayout_Release(layout);
1512 /* with reduced width DrawGlyphRun() is called for every line */
1513 hr = IDWriteFactory_CreateTextLayout(factory, strW, 6, format, 5.0, 100.0, &layout);
1514 ok(hr == S_OK, "got 0x%08x\n", hr);
1515 flush_sequence(sequences, RENDERER_ID);
1516 hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0, 0.0);
1517 ok(hr == S_OK, "got 0x%08x\n", hr);
1518 ok_sequence(sequences, RENDERER_ID, draw_seq2, "draw test 2", TRUE);
1519 IDWriteTextLayout_Release(layout);
1521 /* string with control characters */
1522 hr = IDWriteFactory_CreateTextLayout(factory, str2W, 4, format, 500.0, 100.0, &layout);
1523 ok(hr == S_OK, "got 0x%08x\n", hr);
1524 flush_sequence(sequences, RENDERER_ID);
1525 hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0, 0.0);
1526 ok(hr == S_OK, "got 0x%08x\n", hr);
1527 ok_sequence(sequences, RENDERER_ID, draw_seq3, "draw test 3", TRUE);
1528 IDWriteTextLayout_Release(layout);
1530 /* strikethrough splits ranges from renderer point of view, but doesn't break
1531 shaping */
1532 hr = IDWriteFactory_CreateTextLayout(factory, strW, 6, format, 500.0, 100.0, &layout);
1533 ok(hr == S_OK, "got 0x%08x\n", hr);
1534 flush_sequence(sequences, RENDERER_ID);
1536 range.startPosition = 0;
1537 range.length = 3;
1538 hr = IDWriteTextLayout_SetStrikethrough(layout, TRUE, range);
1539 ok(hr == S_OK, "got 0x%08x\n", hr);
1541 hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0, 0.0);
1542 ok(hr == S_OK, "got 0x%08x\n", hr);
1543 ok_sequence(sequences, RENDERER_ID, draw_seq4, "draw test 4", FALSE);
1544 IDWriteTextLayout_Release(layout);
1546 /* strikethrough somewhere in the middle */
1547 hr = IDWriteFactory_CreateTextLayout(factory, strW, 6, format, 500.0, 100.0, &layout);
1548 ok(hr == S_OK, "got 0x%08x\n", hr);
1549 flush_sequence(sequences, RENDERER_ID);
1551 range.startPosition = 2;
1552 range.length = 2;
1553 hr = IDWriteTextLayout_SetStrikethrough(layout, TRUE, range);
1554 ok(hr == S_OK, "got 0x%08x\n", hr);
1556 hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0, 0.0);
1557 ok(hr == S_OK, "got 0x%08x\n", hr);
1558 ok_sequence(sequences, RENDERER_ID, draw_seq5, "draw test 5", FALSE);
1559 IDWriteTextLayout_Release(layout);
1561 /* empty string */
1562 hr = IDWriteFactory_CreateTextLayout(factory, strW, 0, format, 500.0, 100.0, &layout);
1563 ok(hr == S_OK, "got 0x%08x\n", hr);
1565 flush_sequence(sequences, RENDERER_ID);
1566 hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0, 0.0);
1567 ok(hr == S_OK, "got 0x%08x\n", hr);
1568 ok_sequence(sequences, RENDERER_ID, empty_seq, "draw test 6", FALSE);
1569 IDWriteTextLayout_Release(layout);
1571 ctxt.gdicompat = TRUE;
1572 ctxt.use_gdi_natural = TRUE;
1574 /* different parameter combinations with gdi-compatible layout */
1575 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, strW, 6, format, 100.0, 100.0, 1.0, NULL, TRUE, &layout);
1576 ok(hr == S_OK, "got 0x%08x\n", hr);
1577 flush_sequence(sequences, RENDERER_ID);
1578 hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0, 0.0);
1579 ok(hr == S_OK, "got 0x%08x\n", hr);
1580 ok_sequence(sequences, RENDERER_ID, draw_single_run_seq, "draw test 7", FALSE);
1582 /* text alignment keeps pixel-aligned origin */
1583 hr = IDWriteTextLayout_GetMetrics(layout, &tm);
1584 ok(hr == S_OK, "got 0x%08x\n", hr);
1585 ok(tm.width == floorf(tm.width), "got %f\n", tm.width);
1587 hr = IDWriteTextLayout_SetMaxWidth(layout, tm.width + 3.0);
1588 ok(hr == S_OK, "got 0x%08x\n", hr);
1589 hr = IDWriteTextLayout_SetTextAlignment(layout, DWRITE_TEXT_ALIGNMENT_CENTER);
1590 ok(hr == S_OK, "got 0x%08x\n", hr);
1592 ctxt.originX = ctxt.originY = 0.0;
1593 flush_sequence(sequences, RENDERER_ID);
1594 hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0, 0.0);
1595 ok(hr == S_OK, "got 0x%08x\n", hr);
1596 ok_sequence(sequences, RENDERER_ID, draw_single_run_seq, "draw test 7", FALSE);
1597 ok(ctxt.originX != 0.0 && ctxt.originX == floorf(ctxt.originX), "got %f\n", ctxt.originX);
1599 IDWriteTextLayout_Release(layout);
1601 ctxt.gdicompat = TRUE;
1602 ctxt.use_gdi_natural = FALSE;
1604 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, strW, 6, format, 100.0, 100.0, 1.0, NULL, FALSE, &layout);
1605 ok(hr == S_OK, "got 0x%08x\n", hr);
1606 flush_sequence(sequences, RENDERER_ID);
1607 hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0, 0.0);
1608 ok(hr == S_OK, "got 0x%08x\n", hr);
1609 ok_sequence(sequences, RENDERER_ID, draw_single_run_seq, "draw test 8", FALSE);
1610 IDWriteTextLayout_Release(layout);
1612 ctxt.gdicompat = TRUE;
1613 ctxt.use_gdi_natural = TRUE;
1615 m.m11 = m.m22 = 2.0;
1616 m.m12 = m.m21 = m.dx = m.dy = 0.0;
1617 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, strW, 6, format, 100.0, 100.0, 1.0, &m, TRUE, &layout);
1618 ok(hr == S_OK, "got 0x%08x\n", hr);
1619 flush_sequence(sequences, RENDERER_ID);
1620 hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0, 0.0);
1621 ok(hr == S_OK, "got 0x%08x\n", hr);
1622 ok_sequence(sequences, RENDERER_ID, draw_single_run_seq, "draw test 9", FALSE);
1623 IDWriteTextLayout_Release(layout);
1625 ctxt.gdicompat = TRUE;
1626 ctxt.use_gdi_natural = FALSE;
1628 m.m11 = m.m22 = 2.0;
1629 m.m12 = m.m21 = m.dx = m.dy = 0.0;
1630 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, strW, 6, format, 100.0, 100.0, 1.0, &m, FALSE, &layout);
1631 ok(hr == S_OK, "got 0x%08x\n", hr);
1632 flush_sequence(sequences, RENDERER_ID);
1633 hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0, 0.0);
1634 ok(hr == S_OK, "got 0x%08x\n", hr);
1635 ok_sequence(sequences, RENDERER_ID, draw_single_run_seq, "draw test 10", FALSE);
1636 IDWriteTextLayout_Release(layout);
1638 IDWriteInlineObject_Release(inlineobj);
1640 /* text that triggers bidi run reordering */
1641 hr = IDWriteFactory_CreateTextLayout(factory, str3W, lstrlenW(str3W), format, 1000.0f, 100.0f, &layout);
1642 ok(hr == S_OK, "got 0x%08x\n", hr);
1644 ctxt.gdicompat = FALSE;
1645 ctxt.use_gdi_natural = FALSE;
1646 ctxt.snapping_disabled = TRUE;
1648 flush_sequence(sequences, RENDERER_ID);
1649 hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0f, 0.0f);
1650 ok(hr == S_OK, "got 0x%08x\n", hr);
1651 ok_sequence(sequences, RENDERER_ID, draw_reordered_run_seq, "draw test 11", FALSE);
1653 IDWriteTextLayout_Release(layout);
1655 IDWriteTextFormat_Release(format);
1656 IDWriteFactory_Release(factory);
1659 static void test_typography(void)
1661 DWRITE_FONT_FEATURE feature;
1662 IDWriteTypography *typography;
1663 IDWriteFactory *factory;
1664 UINT32 count;
1665 HRESULT hr;
1667 factory = create_factory();
1669 hr = IDWriteFactory_CreateTypography(factory, &typography);
1670 ok(hr == S_OK, "got 0x%08x\n", hr);
1672 feature.nameTag = DWRITE_FONT_FEATURE_TAG_KERNING;
1673 feature.parameter = 1;
1674 hr = IDWriteTypography_AddFontFeature(typography, feature);
1675 ok(hr == S_OK, "got 0x%08x\n", hr);
1677 count = IDWriteTypography_GetFontFeatureCount(typography);
1678 ok(count == 1, "got %u\n", count);
1680 /* duplicated features work just fine */
1681 feature.nameTag = DWRITE_FONT_FEATURE_TAG_KERNING;
1682 feature.parameter = 0;
1683 hr = IDWriteTypography_AddFontFeature(typography, feature);
1684 ok(hr == S_OK, "got 0x%08x\n", hr);
1686 count = IDWriteTypography_GetFontFeatureCount(typography);
1687 ok(count == 2, "got %u\n", count);
1689 memset(&feature, 0xcc, sizeof(feature));
1690 hr = IDWriteTypography_GetFontFeature(typography, 0, &feature);
1691 ok(hr == S_OK, "got 0x%08x\n", hr);
1692 ok(feature.nameTag == DWRITE_FONT_FEATURE_TAG_KERNING, "got tag %x\n", feature.nameTag);
1693 ok(feature.parameter == 1, "got %u\n", feature.parameter);
1695 memset(&feature, 0xcc, sizeof(feature));
1696 hr = IDWriteTypography_GetFontFeature(typography, 1, &feature);
1697 ok(hr == S_OK, "got 0x%08x\n", hr);
1698 ok(feature.nameTag == DWRITE_FONT_FEATURE_TAG_KERNING, "got tag %x\n", feature.nameTag);
1699 ok(feature.parameter == 0, "got %u\n", feature.parameter);
1701 hr = IDWriteTypography_GetFontFeature(typography, 2, &feature);
1702 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1704 /* duplicated with same parameter value */
1705 feature.nameTag = DWRITE_FONT_FEATURE_TAG_KERNING;
1706 feature.parameter = 0;
1707 hr = IDWriteTypography_AddFontFeature(typography, feature);
1708 ok(hr == S_OK, "got 0x%08x\n", hr);
1710 count = IDWriteTypography_GetFontFeatureCount(typography);
1711 ok(count == 3, "got %u\n", count);
1713 memset(&feature, 0xcc, sizeof(feature));
1714 hr = IDWriteTypography_GetFontFeature(typography, 2, &feature);
1715 ok(hr == S_OK, "got 0x%08x\n", hr);
1716 ok(feature.nameTag == DWRITE_FONT_FEATURE_TAG_KERNING, "got tag %x\n", feature.nameTag);
1717 ok(feature.parameter == 0, "got %u\n", feature.parameter);
1719 IDWriteTypography_Release(typography);
1720 IDWriteFactory_Release(factory);
1723 static void test_GetClusterMetrics(void)
1725 static const WCHAR str_white_spaceW[] = {
1726 /* BK - FORM FEED, LINE TABULATION, LINE SEP, PARA SEP */ 0xc, 0xb, 0x2028, 0x2029,
1727 /* ZW - ZERO WIDTH SPACE */ 0x200b,
1728 /* SP - SPACE */ 0x20
1730 static const WCHAR str5W[] = {'a','\r','b','\n','c','\n','\r','d','\r','\n','e',0xb,'f',0xc,
1731 'g',0x0085,'h',0x2028,'i',0x2029,0xad,0xa,0};
1732 static const WCHAR str3W[] = {0x2066,')',')',0x661,'(',0x627,')',0};
1733 static const WCHAR str2W[] = {0x202a,0x202c,'a',0};
1734 static const WCHAR strW[] = {'a','b','c','d',0};
1735 static const WCHAR str4W[] = {'a',' ',0};
1736 DWRITE_INLINE_OBJECT_METRICS inline_metrics;
1737 DWRITE_CLUSTER_METRICS metrics[22];
1738 IDWriteTextLayout1 *layout1;
1739 IDWriteInlineObject *trimm;
1740 IDWriteTextFormat *format;
1741 IDWriteTextLayout *layout;
1742 DWRITE_TEXT_RANGE range;
1743 IDWriteFactory *factory;
1744 UINT32 count, i;
1745 HRESULT hr;
1747 factory = create_factory();
1749 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
1750 DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format);
1751 ok(hr == S_OK, "got 0x%08x\n", hr);
1753 hr = IDWriteFactory_CreateTextLayout(factory, str3W, 7, format, 1000.0, 1000.0, &layout);
1754 ok(hr == S_OK, "got 0x%08x\n", hr);
1755 hr = IDWriteTextLayout_GetClusterMetrics(layout, NULL, 0, &count);
1756 ok(hr == E_NOT_SUFFICIENT_BUFFER, "got 0x%08x\n", hr);
1757 ok(count == 7, "got %u\n", count);
1758 IDWriteTextLayout_Release(layout);
1760 hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 1000.0, 1000.0, &layout);
1761 ok(hr == S_OK, "got 0x%08x\n", hr);
1763 count = 0;
1764 hr = IDWriteTextLayout_GetClusterMetrics(layout, NULL, 0, &count);
1765 ok(hr == E_NOT_SUFFICIENT_BUFFER, "got 0x%08x\n", hr);
1766 ok(count == 4, "got %u\n", count);
1768 /* check every cluster width */
1769 count = 0;
1770 hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, sizeof(metrics)/sizeof(metrics[0]), &count);
1771 ok(hr == S_OK, "got 0x%08x\n", hr);
1772 ok(count == 4, "got %u\n", count);
1773 for (i = 0; i < count; i++) {
1774 ok(metrics[i].width > 0.0, "%u: got width %.2f\n", i, metrics[i].width);
1775 ok(metrics[i].length == 1, "%u: got length %u\n", i, metrics[i].length);
1778 /* apply spacing and check widths again */
1779 if (IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextLayout1, (void**)&layout1) == S_OK) {
1780 DWRITE_CLUSTER_METRICS metrics2[4];
1781 FLOAT leading, trailing, min_advance;
1782 DWRITE_TEXT_RANGE r;
1784 leading = trailing = min_advance = 2.0;
1785 hr = IDWriteTextLayout1_GetCharacterSpacing(layout1, 500, &leading, &trailing,
1786 &min_advance, &r);
1787 ok(hr == S_OK, "got 0x%08x\n", hr);
1788 ok(leading == 0.0 && trailing == 0.0 && min_advance == 0.0,
1789 "got %.2f, %.2f, %.2f\n", leading, trailing, min_advance);
1790 ok(r.startPosition == 0 && r.length == ~0u, "got %u, %u\n", r.startPosition, r.length);
1792 leading = trailing = min_advance = 2.0;
1793 hr = IDWriteTextLayout1_GetCharacterSpacing(layout1, 0, &leading, &trailing,
1794 &min_advance, NULL);
1795 ok(hr == S_OK, "got 0x%08x\n", hr);
1796 ok(leading == 0.0 && trailing == 0.0 && min_advance == 0.0,
1797 "got %.2f, %.2f, %.2f\n", leading, trailing, min_advance);
1799 r.startPosition = 0;
1800 r.length = 4;
1801 hr = IDWriteTextLayout1_SetCharacterSpacing(layout1, 10.0, 15.0, 0.0, r);
1802 ok(hr == S_OK, "got 0x%08x\n", hr);
1804 count = 0;
1805 hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics2, sizeof(metrics2)/sizeof(metrics2[0]), &count);
1806 ok(hr == S_OK, "got 0x%08x\n", hr);
1807 ok(count == 4, "got %u\n", count);
1808 for (i = 0; i < count; i++) {
1809 todo_wine
1810 ok(metrics2[i].width > metrics[i].width, "%u: got width %.2f, was %.2f\n", i, metrics2[i].width,
1811 metrics[i].width);
1812 ok(metrics2[i].length == 1, "%u: got length %u\n", i, metrics2[i].length);
1815 /* back to defaults */
1816 r.startPosition = 0;
1817 r.length = 4;
1818 hr = IDWriteTextLayout1_SetCharacterSpacing(layout1, 0.0, 0.0, 0.0, r);
1819 ok(hr == S_OK, "got 0x%08x\n", hr);
1821 /* negative advance limit */
1822 r.startPosition = 0;
1823 r.length = 4;
1824 hr = IDWriteTextLayout1_SetCharacterSpacing(layout1, 0.0, 0.0, -10.0, r);
1825 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1827 IDWriteTextLayout1_Release(layout1);
1829 else
1830 win_skip("IDWriteTextLayout1 is not supported, cluster spacing test skipped.\n");
1832 hr = IDWriteFactory_CreateEllipsisTrimmingSign(factory, format, &trimm);
1833 ok(hr == S_OK, "got 0x%08x\n", hr);
1835 range.startPosition = 0;
1836 range.length = 2;
1837 hr = IDWriteTextLayout_SetInlineObject(layout, trimm, range);
1838 ok(hr == S_OK, "got 0x%08x\n", hr);
1840 /* inline object takes a separate cluster, replaced codepoints number doesn't matter */
1841 count = 0;
1842 hr = IDWriteTextLayout_GetClusterMetrics(layout, NULL, 0, &count);
1843 ok(hr == E_NOT_SUFFICIENT_BUFFER, "got 0x%08x\n", hr);
1844 ok(count == 3, "got %u\n", count);
1846 count = 0;
1847 memset(&metrics, 0, sizeof(metrics));
1848 hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 1, &count);
1849 ok(hr == E_NOT_SUFFICIENT_BUFFER, "got 0x%08x\n", hr);
1850 ok(count == 3, "got %u\n", count);
1851 ok(metrics[0].length == 2, "got %u\n", metrics[0].length);
1853 hr = IDWriteInlineObject_GetMetrics(trimm, &inline_metrics);
1854 ok(hr == S_OK, "got 0x%08x\n", hr);
1855 ok(inline_metrics.width > 0.0 && inline_metrics.width == metrics[0].width, "got %.2f, expected %.2f\n",
1856 inline_metrics.width, metrics[0].width);
1858 IDWriteTextLayout_Release(layout);
1860 /* text with non-visual control codes */
1861 hr = IDWriteFactory_CreateTextLayout(factory, str2W, 3, format, 1000.0, 1000.0, &layout);
1862 ok(hr == S_OK, "got 0x%08x\n", hr);
1864 /* bidi control codes take a separate cluster */
1865 count = 0;
1866 memset(metrics, 0, sizeof(metrics));
1867 hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 3, &count);
1868 ok(hr == S_OK, "got 0x%08x\n", hr);
1869 ok(count == 3, "got %u\n", count);
1871 ok(metrics[0].width == 0.0, "got %.2f\n", metrics[0].width);
1872 ok(metrics[0].length == 1, "got %d\n", metrics[0].length);
1873 ok(metrics[0].canWrapLineAfter == 0, "got %d\n", metrics[0].canWrapLineAfter);
1874 ok(metrics[0].isWhitespace == 0, "got %d\n", metrics[0].isWhitespace);
1875 ok(metrics[0].isNewline == 0, "got %d\n", metrics[0].isNewline);
1876 ok(metrics[0].isSoftHyphen == 0, "got %d\n", metrics[0].isSoftHyphen);
1877 ok(metrics[0].isRightToLeft == 0, "got %d\n", metrics[0].isRightToLeft);
1879 ok(metrics[1].width == 0.0, "got %.2f\n", metrics[1].width);
1880 ok(metrics[1].length == 1, "got %d\n", metrics[1].length);
1881 ok(metrics[1].canWrapLineAfter == 0, "got %d\n", metrics[1].canWrapLineAfter);
1882 ok(metrics[1].isWhitespace == 0, "got %d\n", metrics[1].isWhitespace);
1883 ok(metrics[1].isNewline == 0, "got %d\n", metrics[1].isNewline);
1884 ok(metrics[1].isSoftHyphen == 0, "got %d\n", metrics[1].isSoftHyphen);
1885 ok(metrics[1].isRightToLeft == 0, "got %d\n", metrics[1].isRightToLeft);
1887 ok(metrics[2].width > 0.0, "got %.2f\n", metrics[2].width);
1888 ok(metrics[2].length == 1, "got %d\n", metrics[2].length);
1889 ok(metrics[2].canWrapLineAfter == 1, "got %d\n", metrics[2].canWrapLineAfter);
1890 ok(metrics[2].isWhitespace == 0, "got %d\n", metrics[2].isWhitespace);
1891 ok(metrics[2].isNewline == 0, "got %d\n", metrics[2].isNewline);
1892 ok(metrics[2].isSoftHyphen == 0, "got %d\n", metrics[2].isSoftHyphen);
1893 ok(metrics[2].isRightToLeft == 0, "got %d\n", metrics[2].isRightToLeft);
1895 IDWriteTextLayout_Release(layout);
1897 /* single inline object that fails to report its metrics */
1898 hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 1000.0, 1000.0, &layout);
1899 ok(hr == S_OK, "got 0x%08x\n", hr);
1901 range.startPosition = 0;
1902 range.length = 4;
1903 hr = IDWriteTextLayout_SetInlineObject(layout, &testinlineobj, range);
1904 ok(hr == S_OK, "got 0x%08x\n", hr);
1906 count = 0;
1907 memset(metrics, 0, sizeof(metrics));
1908 hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 3, &count);
1909 ok(hr == S_OK, "got 0x%08x\n", hr);
1910 ok(count == 1, "got %u\n", count);
1912 /* object sets a width to 123.0, but returns failure from GetMetrics() */
1913 ok(metrics[0].width == 0.0, "got %.2f\n", metrics[0].width);
1914 ok(metrics[0].length == 4, "got %d\n", metrics[0].length);
1915 ok(metrics[0].canWrapLineAfter == 1, "got %d\n", metrics[0].canWrapLineAfter);
1916 ok(metrics[0].isWhitespace == 0, "got %d\n", metrics[0].isWhitespace);
1917 ok(metrics[0].isNewline == 0, "got %d\n", metrics[0].isNewline);
1918 ok(metrics[0].isSoftHyphen == 0, "got %d\n", metrics[0].isSoftHyphen);
1919 ok(metrics[0].isRightToLeft == 0, "got %d\n", metrics[0].isRightToLeft);
1921 /* now set two inline object for [0,1] and [2,3], both fail to report break conditions */
1922 range.startPosition = 2;
1923 range.length = 2;
1924 hr = IDWriteTextLayout_SetInlineObject(layout, &testinlineobj2, range);
1925 ok(hr == S_OK, "got 0x%08x\n", hr);
1927 count = 0;
1928 memset(metrics, 0, sizeof(metrics));
1929 hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 3, &count);
1930 ok(hr == S_OK, "got 0x%08x\n", hr);
1931 ok(count == 2, "got %u\n", count);
1933 ok(metrics[0].width == 0.0, "got %.2f\n", metrics[0].width);
1934 ok(metrics[0].length == 2, "got %d\n", metrics[0].length);
1935 ok(metrics[0].canWrapLineAfter == 0, "got %d\n", metrics[0].canWrapLineAfter);
1936 ok(metrics[0].isWhitespace == 0, "got %d\n", metrics[0].isWhitespace);
1937 ok(metrics[0].isNewline == 0, "got %d\n", metrics[0].isNewline);
1938 ok(metrics[0].isSoftHyphen == 0, "got %d\n", metrics[0].isSoftHyphen);
1939 ok(metrics[0].isRightToLeft == 0, "got %d\n", metrics[0].isRightToLeft);
1941 ok(metrics[1].width == 0.0, "got %.2f\n", metrics[1].width);
1942 ok(metrics[1].length == 2, "got %d\n", metrics[1].length);
1943 ok(metrics[1].canWrapLineAfter == 1, "got %d\n", metrics[1].canWrapLineAfter);
1944 ok(metrics[1].isWhitespace == 0, "got %d\n", metrics[1].isWhitespace);
1945 ok(metrics[1].isNewline == 0, "got %d\n", metrics[1].isNewline);
1946 ok(metrics[1].isSoftHyphen == 0, "got %d\n", metrics[1].isSoftHyphen);
1947 ok(metrics[1].isRightToLeft == 0, "got %d\n", metrics[1].isRightToLeft);
1949 IDWriteTextLayout_Release(layout);
1951 /* zero length string */
1952 hr = IDWriteFactory_CreateTextLayout(factory, strW, 0, format, 1000.0, 1000.0, &layout);
1953 ok(hr == S_OK, "got 0x%08x\n", hr);
1955 count = 1;
1956 memset(metrics, 0, sizeof(metrics));
1957 hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 3, &count);
1958 ok(hr == S_OK, "got 0x%08x\n", hr);
1959 ok(count == 0, "got %u\n", count);
1960 IDWriteTextLayout_Release(layout);
1962 /* whitespace */
1963 hr = IDWriteFactory_CreateTextLayout(factory, str4W, 2, format, 1000.0, 1000.0, &layout);
1964 ok(hr == S_OK, "got 0x%08x\n", hr);
1966 count = 0;
1967 memset(metrics, 0, sizeof(metrics));
1968 hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 2, &count);
1969 ok(hr == S_OK, "got 0x%08x\n", hr);
1970 ok(count == 2, "got %u\n", count);
1971 ok(metrics[0].isWhitespace == 0, "got %d\n", metrics[0].isWhitespace);
1972 ok(metrics[0].canWrapLineAfter == 0, "got %d\n", metrics[0].canWrapLineAfter);
1973 ok(metrics[1].isWhitespace == 1, "got %d\n", metrics[1].isWhitespace);
1974 ok(metrics[1].canWrapLineAfter == 1, "got %d\n", metrics[1].canWrapLineAfter);
1975 IDWriteTextLayout_Release(layout);
1977 /* layout is fully covered by inline object with after condition DWRITE_BREAK_CONDITION_MAY_NOT_BREAK */
1978 hr = IDWriteFactory_CreateTextLayout(factory, str4W, 2, format, 1000.0, 1000.0, &layout);
1979 ok(hr == S_OK, "got 0x%08x\n", hr);
1981 range.startPosition = 0;
1982 range.length = ~0u;
1983 hr = IDWriteTextLayout_SetInlineObject(layout, &testinlineobj3, range);
1984 ok(hr == S_OK, "got 0x%08x\n", hr);
1986 count = 0;
1987 memset(metrics, 0, sizeof(metrics));
1988 hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 2, &count);
1989 ok(hr == S_OK, "got 0x%08x\n", hr);
1990 ok(count == 1, "got %u\n", count);
1991 ok(metrics[0].canWrapLineAfter == 1, "got %d\n", metrics[0].canWrapLineAfter);
1993 IDWriteTextLayout_Release(layout);
1995 /* compare natural cluster width with gdi layout */
1996 hr = IDWriteFactory_CreateTextLayout(factory, str4W, 1, format, 100.0, 100.0, &layout);
1997 ok(hr == S_OK, "got 0x%08x\n", hr);
1999 count = 0;
2000 memset(metrics, 0, sizeof(metrics));
2001 hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 1, &count);
2002 ok(hr == S_OK, "got 0x%08x\n", hr);
2003 ok(count == 1, "got %u\n", count);
2004 ok(metrics[0].width != floorf(metrics[0].width), "got %f\n", metrics[0].width);
2006 IDWriteTextLayout_Release(layout);
2008 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, str4W, 1, format, 100.0, 100.0, 1.0, NULL, FALSE, &layout);
2009 ok(hr == S_OK, "got 0x%08x\n", hr);
2011 count = 0;
2012 memset(metrics, 0, sizeof(metrics));
2013 hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 1, &count);
2014 ok(hr == S_OK, "got 0x%08x\n", hr);
2015 ok(count == 1, "got %u\n", count);
2016 ok(metrics[0].width == floorf(metrics[0].width), "got %f\n", metrics[0].width);
2018 IDWriteTextLayout_Release(layout);
2020 /* isNewline tests */
2021 hr = IDWriteFactory_CreateTextLayout(factory, str5W, lstrlenW(str5W), format, 100.0f, 200.0f, &layout);
2022 ok(hr == S_OK, "got 0x%08x\n", hr);
2024 count = 0;
2025 memset(metrics, 0, sizeof(metrics));
2026 hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, sizeof(metrics)/sizeof(metrics[0]), &count);
2027 ok(hr == S_OK, "got 0x%08x\n", hr);
2028 ok(count == 22, "got %u\n", count);
2030 ok(metrics[1].isNewline == 1, "got %d\n", metrics[1].isNewline);
2031 ok(metrics[3].isNewline == 1, "got %d\n", metrics[3].isNewline);
2032 ok(metrics[5].isNewline == 1, "got %d\n", metrics[5].isNewline);
2033 ok(metrics[6].isNewline == 1, "got %d\n", metrics[6].isNewline);
2034 ok(metrics[9].isNewline == 1, "got %d\n", metrics[9].isNewline);
2035 ok(metrics[11].isNewline == 1, "got %d\n", metrics[11].isNewline);
2036 ok(metrics[13].isNewline == 1, "got %d\n", metrics[13].isNewline);
2037 ok(metrics[15].isNewline == 1, "got %d\n", metrics[15].isNewline);
2038 ok(metrics[17].isNewline == 1, "got %d\n", metrics[17].isNewline);
2039 ok(metrics[19].isNewline == 1, "got %d\n", metrics[19].isNewline);
2040 ok(metrics[21].isNewline == 1, "got %d\n", metrics[21].isNewline);
2042 ok(metrics[0].isNewline == 0, "got %d\n", metrics[0].isNewline);
2043 ok(metrics[2].isNewline == 0, "got %d\n", metrics[2].isNewline);
2044 ok(metrics[4].isNewline == 0, "got %d\n", metrics[4].isNewline);
2045 ok(metrics[7].isNewline == 0, "got %d\n", metrics[7].isNewline);
2046 ok(metrics[8].isNewline == 0, "got %d\n", metrics[8].isNewline);
2047 ok(metrics[10].isNewline == 0, "got %d\n", metrics[10].isNewline);
2048 ok(metrics[12].isNewline == 0, "got %d\n", metrics[12].isNewline);
2049 ok(metrics[14].isNewline == 0, "got %d\n", metrics[14].isNewline);
2050 ok(metrics[16].isNewline == 0, "got %d\n", metrics[16].isNewline);
2051 ok(metrics[18].isNewline == 0, "got %d\n", metrics[18].isNewline);
2052 ok(metrics[20].isNewline == 0, "got %d\n", metrics[20].isNewline);
2054 for (i = 0; i < count; i++) {
2055 ok(metrics[i].length == 1, "%d: got %d\n", i, metrics[i].length);
2056 ok(metrics[i].isSoftHyphen == (i == count - 2), "%d: got %d\n", i, metrics[i].isSoftHyphen);
2057 if (metrics[i].isSoftHyphen)
2058 ok(!metrics[i].isWhitespace, "%u: got %d\n", i, metrics[i].isWhitespace);
2059 if (metrics[i].isNewline) {
2060 if (i == 17 || i == 19)
2061 todo_wine ok(metrics[i].width == 0.0f, "%u: got width %f\n", i, metrics[i].width);
2062 else
2063 ok(metrics[i].width == 0.0f, "%u: got width %f\n", i, metrics[i].width);
2064 ok(metrics[i].isWhitespace == 1, "%u: got %d\n", i, metrics[i].isWhitespace);
2065 ok(metrics[i].canWrapLineAfter == 1, "%u: got %d\n", i, metrics[i].canWrapLineAfter);
2069 IDWriteTextLayout_Release(layout);
2071 /* Test whitespace resolution from linebreaking classes BK, ZW, and SP */
2072 hr = IDWriteFactory_CreateTextLayout(factory, str_white_spaceW, sizeof(str_white_spaceW)/sizeof(WCHAR), format,
2073 100.0f, 200.0f, &layout);
2074 ok(hr == S_OK, "got 0x%08x\n", hr);
2076 count = 0;
2077 memset(metrics, 0, sizeof(metrics));
2078 hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 20, &count);
2079 ok(hr == S_OK, "got 0x%08x\n", hr);
2080 ok(count == 6, "got %u\n", count);
2082 ok(metrics[0].isWhitespace == 1, "got %d\n", metrics[0].isWhitespace);
2083 ok(metrics[1].isWhitespace == 1, "got %d\n", metrics[1].isWhitespace);
2084 ok(metrics[2].isWhitespace == 1, "got %d\n", metrics[2].isWhitespace);
2085 ok(metrics[3].isWhitespace == 1, "got %d\n", metrics[3].isWhitespace);
2086 ok(metrics[4].isWhitespace == 0, "got %d\n", metrics[4].isWhitespace);
2087 ok(metrics[5].isWhitespace == 1, "got %d\n", metrics[5].isWhitespace);
2089 IDWriteInlineObject_Release(trimm);
2090 IDWriteTextFormat_Release(format);
2091 IDWriteFactory_Release(factory);
2094 static void test_SetLocaleName(void)
2096 static const WCHAR eNuSW[] = {'e','N','-','u','S',0};
2097 static const WCHAR strW[] = {'a','b','c','d',0};
2098 WCHAR buffW[LOCALE_NAME_MAX_LENGTH+sizeof(strW)/sizeof(WCHAR)];
2099 IDWriteTextFormat *format, *format2;
2100 IDWriteTextLayout *layout;
2101 DWRITE_TEXT_RANGE range;
2102 IDWriteFactory *factory;
2103 HRESULT hr;
2105 factory = create_factory();
2107 /* create format with mixed case locale name, get it back */
2108 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
2109 DWRITE_FONT_STRETCH_NORMAL, 10.0, eNuSW, &format);
2110 ok(hr == S_OK, "got 0x%08x\n", hr);
2112 hr = IDWriteTextFormat_GetLocaleName(format, buffW, sizeof(buffW)/sizeof(buffW[0]));
2113 ok(hr == S_OK, "got 0x%08x\n", hr);
2114 ok(!lstrcmpW(buffW, enusW), "got %s\n", wine_dbgstr_w(buffW));
2116 hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 1000.0, 1000.0, &layout);
2117 ok(hr == S_OK, "got 0x%08x\n", hr);
2119 hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextFormat, (void**)&format2);
2120 ok(hr == S_OK, "got 0x%08x\n", hr);
2122 hr = IDWriteTextFormat_GetLocaleName(format2, buffW, sizeof(buffW)/sizeof(buffW[0]));
2123 ok(hr == S_OK, "got 0x%08x\n", hr);
2124 ok(!lstrcmpW(buffW, enusW), "got %s\n", wine_dbgstr_w(buffW));
2126 hr = IDWriteTextLayout_GetLocaleName(layout, 0, buffW, sizeof(buffW)/sizeof(buffW[0]), NULL);
2127 ok(hr == S_OK, "got 0x%08x\n", hr);
2128 ok(!lstrcmpW(buffW, enusW), "got %s\n", wine_dbgstr_w(buffW));
2130 IDWriteTextFormat_Release(format2);
2131 IDWriteTextLayout_Release(layout);
2132 IDWriteTextFormat_Release(format);
2134 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
2135 DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format);
2136 ok(hr == S_OK, "got 0x%08x\n", hr);
2138 hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 1000.0, 1000.0, &layout);
2139 ok(hr == S_OK, "got 0x%08x\n", hr);
2141 range.startPosition = 0;
2142 range.length = 1;
2143 hr = IDWriteTextLayout_SetLocaleName(layout, enusW, range);
2144 ok(hr == S_OK, "got 0x%08x\n", hr);
2146 hr = IDWriteTextLayout_SetLocaleName(layout, NULL, range);
2147 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
2149 /* invalid locale name is allowed */
2150 hr = IDWriteTextLayout_SetLocaleName(layout, strW, range);
2151 ok(hr == S_OK, "got 0x%08x\n", hr);
2153 hr = IDWriteTextLayout_GetLocaleName(layout, 0, NULL, 0, NULL);
2154 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
2156 if (0) /* crashes on native */
2157 hr = IDWriteTextLayout_GetLocaleName(layout, 0, NULL, 1, NULL);
2159 buffW[0] = 0;
2160 range.length = 0;
2161 hr = IDWriteTextLayout_GetLocaleName(layout, 0, buffW, sizeof(buffW)/sizeof(WCHAR), &range);
2162 ok(hr == S_OK, "got 0x%08x\n", hr);
2163 ok(!lstrcmpW(buffW, strW), "got %s\n", wine_dbgstr_w(buffW));
2164 ok(range.startPosition == 0 && range.length == 1, "got %u,%u\n", range.startPosition, range.length);
2166 /* get with a shorter buffer */
2167 buffW[0] = 0xa;
2168 hr = IDWriteTextLayout_GetLocaleName(layout, 0, buffW, 1, NULL);
2169 ok(hr == E_NOT_SUFFICIENT_BUFFER, "got 0x%08x\n", hr);
2170 ok(buffW[0] == 0, "got %x\n", buffW[0]);
2172 /* name is too long */
2173 lstrcpyW(buffW, strW);
2174 while (lstrlenW(buffW) <= LOCALE_NAME_MAX_LENGTH)
2175 lstrcatW(buffW, strW);
2177 range.startPosition = 0;
2178 range.length = 1;
2179 hr = IDWriteTextLayout_SetLocaleName(layout, buffW, range);
2180 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
2182 buffW[0] = 0;
2183 hr = IDWriteTextLayout_GetLocaleName(layout, 0, buffW, sizeof(buffW)/sizeof(WCHAR), NULL);
2184 ok(hr == S_OK, "got 0x%08x\n", hr);
2185 ok(!lstrcmpW(buffW, strW), "got %s\n", wine_dbgstr_w(buffW));
2187 /* set initial locale name for whole text, except with a different casing */
2188 range.startPosition = 0;
2189 range.length = 4;
2190 hr = IDWriteTextLayout_SetLocaleName(layout, eNuSW, range);
2191 ok(hr == S_OK, "got 0x%08x\n", hr);
2193 buffW[0] = 0;
2194 range.length = 0;
2195 hr = IDWriteTextLayout_GetLocaleName(layout, 0, buffW, sizeof(buffW)/sizeof(WCHAR), &range);
2196 ok(hr == S_OK, "got 0x%08x\n", hr);
2197 ok(!lstrcmpW(buffW, enusW), "got %s\n", wine_dbgstr_w(buffW));
2198 ok((range.startPosition == 0 && range.length == ~0u) ||
2199 broken(range.startPosition == 0 && range.length == 4) /* vista/win7 */, "got %u,%u\n", range.startPosition, range.length);
2201 /* check what's returned for positions after the text */
2202 buffW[0] = 0;
2203 range.length = 0;
2204 hr = IDWriteTextLayout_GetLocaleName(layout, 100, buffW, sizeof(buffW)/sizeof(WCHAR), &range);
2205 ok(hr == S_OK, "got 0x%08x\n", hr);
2206 ok(!lstrcmpW(buffW, enusW), "got %s\n", wine_dbgstr_w(buffW));
2207 ok((range.startPosition == 0 && range.length == ~0u) ||
2208 broken(range.startPosition == 4 && range.length == ~0u-4) /* vista/win7 */, "got %u,%u\n", range.startPosition, range.length);
2210 IDWriteTextLayout_Release(layout);
2211 IDWriteTextFormat_Release(format);
2212 IDWriteFactory_Release(factory);
2215 static void test_SetPairKerning(void)
2217 static const WCHAR strW[] = {'a','e',0x0300,'d',0}; /* accent grave */
2218 DWRITE_CLUSTER_METRICS clusters[4];
2219 IDWriteTextLayout1 *layout1;
2220 IDWriteTextFormat *format;
2221 IDWriteTextLayout *layout;
2222 DWRITE_TEXT_RANGE range;
2223 IDWriteFactory *factory;
2224 BOOL kerning;
2225 UINT32 count;
2226 HRESULT hr;
2228 factory = create_factory();
2230 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
2231 DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format);
2232 ok(hr == S_OK, "got 0x%08x\n", hr);
2234 hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 1000.0, 1000.0, &layout);
2235 ok(hr == S_OK, "got 0x%08x\n", hr);
2236 IDWriteTextFormat_Release(format);
2238 hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextLayout1, (void**)&layout1);
2239 IDWriteTextLayout_Release(layout);
2241 if (hr != S_OK) {
2242 win_skip("SetPairKerning() is not supported.\n");
2243 IDWriteFactory_Release(factory);
2244 return;
2247 if (0) { /* crashes on native */
2248 hr = IDWriteTextLayout1_GetPairKerning(layout1, 0, NULL, NULL);
2249 hr = IDWriteTextLayout1_GetPairKerning(layout1, 0, NULL, &range);
2252 hr = IDWriteTextLayout1_GetPairKerning(layout1, 0, &kerning, NULL);
2253 ok(hr == S_OK, "got 0x%08x\n", hr);
2255 range.startPosition = 0;
2256 range.length = 0;
2257 kerning = TRUE;
2258 hr = IDWriteTextLayout1_GetPairKerning(layout1, 0, &kerning, &range);
2259 ok(hr == S_OK, "got 0x%08x\n", hr);
2260 ok(!kerning, "got %d\n", kerning);
2261 ok(range.length == ~0u, "got %u\n", range.length);
2263 count = 0;
2264 hr = IDWriteTextLayout1_GetClusterMetrics(layout1, clusters, 4, &count);
2265 ok(hr == S_OK, "got 0x%08x\n", hr);
2266 todo_wine
2267 ok(count == 3, "got %u\n", count);
2268 if (count == 3) {
2269 ok(clusters[0].length == 1, "got %u\n", clusters[0].length);
2270 ok(clusters[1].length == 2, "got %u\n", clusters[1].length);
2271 ok(clusters[2].length == 1, "got %u\n", clusters[2].length);
2273 /* pair kerning flag participates in itemization - combining characters
2274 breaks */
2275 range.startPosition = 0;
2276 range.length = 2;
2277 hr = IDWriteTextLayout1_SetPairKerning(layout1, 2, range);
2278 ok(hr == S_OK, "got 0x%08x\n", hr);
2280 kerning = FALSE;
2281 hr = IDWriteTextLayout1_GetPairKerning(layout1, 0, &kerning, &range);
2282 ok(hr == S_OK, "got 0x%08x\n", hr);
2283 ok(kerning == TRUE, "got %d\n", kerning);
2285 count = 0;
2286 hr = IDWriteTextLayout1_GetClusterMetrics(layout1, clusters, 4, &count);
2287 ok(hr == S_OK, "got 0x%08x\n", hr);
2288 ok(count == 4, "got %u\n", count);
2289 ok(clusters[0].length == 1, "got %u\n", clusters[0].length);
2290 ok(clusters[1].length == 1, "got %u\n", clusters[1].length);
2291 ok(clusters[2].length == 1, "got %u\n", clusters[2].length);
2292 ok(clusters[3].length == 1, "got %u\n", clusters[3].length);
2294 IDWriteTextLayout1_Release(layout1);
2295 IDWriteFactory_Release(factory);
2298 static void test_SetVerticalGlyphOrientation(void)
2300 static const WCHAR strW[] = {'a','b','c','d',0};
2301 DWRITE_VERTICAL_GLYPH_ORIENTATION orientation;
2302 IDWriteTextLayout2 *layout2;
2303 IDWriteTextFormat *format;
2304 IDWriteTextLayout *layout;
2305 IDWriteFactory *factory;
2306 HRESULT hr;
2308 factory = create_factory();
2310 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
2311 DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format);
2312 ok(hr == S_OK, "got 0x%08x\n", hr);
2314 hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 1000.0, 1000.0, &layout);
2315 ok(hr == S_OK, "got 0x%08x\n", hr);
2316 IDWriteTextFormat_Release(format);
2318 hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextLayout2, (void**)&layout2);
2319 IDWriteTextLayout_Release(layout);
2321 if (hr != S_OK) {
2322 win_skip("SetVerticalGlyphOrientation() is not supported.\n");
2323 IDWriteFactory_Release(factory);
2324 return;
2327 orientation = IDWriteTextLayout2_GetVerticalGlyphOrientation(layout2);
2328 ok(orientation == DWRITE_VERTICAL_GLYPH_ORIENTATION_DEFAULT, "got %d\n", orientation);
2330 hr = IDWriteTextLayout2_SetVerticalGlyphOrientation(layout2, DWRITE_VERTICAL_GLYPH_ORIENTATION_STACKED+1);
2331 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
2333 IDWriteTextLayout2_Release(layout2);
2334 IDWriteFactory_Release(factory);
2337 static void test_fallback(void)
2339 static const WCHAR strW[] = {'a','b','c','d',0};
2340 IDWriteFontFallback *fallback, *fallback2;
2341 IDWriteTextLayout2 *layout2;
2342 IDWriteTextFormat1 *format1;
2343 IDWriteTextFormat *format;
2344 IDWriteTextLayout *layout;
2345 IDWriteFactory2 *factory2;
2346 IDWriteFactory *factory;
2347 HRESULT hr;
2349 factory = create_factory();
2351 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
2352 DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format);
2353 ok(hr == S_OK, "got 0x%08x\n", hr);
2355 hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 1000.0, 1000.0, &layout);
2356 ok(hr == S_OK, "got 0x%08x\n", hr);
2357 IDWriteTextFormat_Release(format);
2359 hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextLayout2, (void**)&layout2);
2360 IDWriteTextLayout_Release(layout);
2362 if (hr != S_OK) {
2363 win_skip("GetFontFallback() is not supported.\n");
2364 IDWriteFactory_Release(factory);
2365 return;
2368 if (0) /* crashes on native */
2369 hr = IDWriteTextLayout2_GetFontFallback(layout2, NULL);
2371 fallback = (void*)0xdeadbeef;
2372 hr = IDWriteTextLayout2_GetFontFallback(layout2, &fallback);
2373 ok(hr == S_OK, "got 0x%08x\n", hr);
2374 ok(fallback == NULL, "got %p\n", fallback);
2376 hr = IDWriteTextLayout2_QueryInterface(layout2, &IID_IDWriteTextFormat1, (void**)&format1);
2377 ok(hr == S_OK, "got 0x%08x\n", hr);
2379 fallback = (void*)0xdeadbeef;
2380 hr = IDWriteTextFormat1_GetFontFallback(format1, &fallback);
2381 ok(hr == S_OK, "got 0x%08x\n", hr);
2382 ok(fallback == NULL, "got %p\n", fallback);
2384 hr = IDWriteFactory_QueryInterface(factory, &IID_IDWriteFactory2, (void**)&factory2);
2385 ok(hr == S_OK, "got 0x%08x\n", hr);
2387 fallback = NULL;
2388 hr = IDWriteFactory2_GetSystemFontFallback(factory2, &fallback);
2389 ok(hr == S_OK, "got 0x%08x\n", hr);
2390 ok(fallback != NULL, "got %p\n", fallback);
2392 hr = IDWriteTextFormat1_SetFontFallback(format1, fallback);
2393 ok(hr == S_OK, "got 0x%08x\n", hr);
2395 fallback2 = (void*)0xdeadbeef;
2396 hr = IDWriteTextLayout2_GetFontFallback(layout2, &fallback2);
2397 ok(hr == S_OK, "got 0x%08x\n", hr);
2398 ok(fallback2 == fallback, "got %p\n", fallback2);
2400 hr = IDWriteTextLayout2_SetFontFallback(layout2, NULL);
2401 ok(hr == S_OK, "got 0x%08x\n", hr);
2403 fallback2 = (void*)0xdeadbeef;
2404 hr = IDWriteTextFormat1_GetFontFallback(format1, &fallback2);
2405 ok(hr == S_OK, "got 0x%08x\n", hr);
2406 ok(fallback2 == NULL, "got %p\n", fallback2);
2408 IDWriteFontFallback_Release(fallback);
2409 IDWriteTextFormat1_Release(format1);
2410 IDWriteTextLayout2_Release(layout2);
2411 IDWriteFactory_Release(factory);
2414 static void test_DetermineMinWidth(void)
2416 struct minwidth_test {
2417 const WCHAR text[10]; /* text to create a layout for */
2418 const WCHAR mintext[10]; /* text that represents sequence of minimal width */
2419 } minwidth_tests[] = {
2420 { {' ','a','b',' ',0}, {'a','b',0} },
2421 { {'a','\n',' ',' ',0}, {'a',0} },
2422 { {'a','\n',' ',' ','b',0}, {'b',0} },
2423 { {'a','b','c','\n',' ',' ','b',0}, {'a','b','c',0} },
2425 static const WCHAR strW[] = {'a','b','c','d',0};
2426 DWRITE_CLUSTER_METRICS metrics[10];
2427 IDWriteTextFormat *format;
2428 IDWriteTextLayout *layout;
2429 IDWriteFactory *factory;
2430 UINT32 count, i, j;
2431 FLOAT minwidth;
2432 HRESULT hr;
2434 factory = create_factory();
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, lstrlenW(strW), format, 1000.0, 1000.0, &layout);
2441 ok(hr == S_OK, "got 0x%08x\n", hr);
2443 hr = IDWriteTextLayout_DetermineMinWidth(layout, NULL);
2444 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
2445 IDWriteTextLayout_Release(layout);
2447 /* empty string */
2448 hr = IDWriteFactory_CreateTextLayout(factory, strW, 0, format, 100.0f, 100.0f, &layout);
2449 ok(hr == S_OK, "got 0x%08x\n", hr);
2451 minwidth = 1.0f;
2452 hr = IDWriteTextLayout_DetermineMinWidth(layout, &minwidth);
2453 ok(hr == S_OK, "got 0x%08x\n", hr);
2454 ok(minwidth == 0.0f, "got %f\n", minwidth);
2455 IDWriteTextLayout_Release(layout);
2457 for (i = 0; i < sizeof(minwidth_tests)/sizeof(minwidth_tests[0]); i++) {
2458 FLOAT width = 0.0f;
2460 /* measure expected width */
2461 hr = IDWriteFactory_CreateTextLayout(factory, minwidth_tests[i].mintext, lstrlenW(minwidth_tests[i].mintext), format, 1000.0f, 1000.0f, &layout);
2462 ok(hr == S_OK, "got 0x%08x\n", hr);
2464 hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, sizeof(metrics)/sizeof(metrics[0]), &count);
2465 ok(hr == S_OK, "got 0x%08x\n", hr);
2467 for (j = 0; j < count; j++)
2468 width += metrics[j].width;
2470 IDWriteTextLayout_Release(layout);
2472 hr = IDWriteFactory_CreateTextLayout(factory, minwidth_tests[i].text, lstrlenW(minwidth_tests[i].text), format, 1000.0f, 1000.0f, &layout);
2473 ok(hr == S_OK, "got 0x%08x\n", hr);
2475 minwidth = 0.0f;
2476 hr = IDWriteTextLayout_DetermineMinWidth(layout, &minwidth);
2477 ok(hr == S_OK, "got 0x%08x\n", hr);
2478 ok(minwidth == width, "test %u: expected width %f, got %f\n", i, width, minwidth);
2480 IDWriteTextLayout_Release(layout);
2483 IDWriteTextFormat_Release(format);
2484 IDWriteFactory_Release(factory);
2487 static void test_SetFontSize(void)
2489 static const WCHAR strW[] = {'a','b','c','d',0};
2490 IDWriteTextFormat *format;
2491 IDWriteTextLayout *layout;
2492 IDWriteFactory *factory;
2493 DWRITE_TEXT_RANGE r;
2494 FLOAT size;
2495 HRESULT hr;
2497 factory = create_factory();
2499 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
2500 DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format);
2501 ok(hr == S_OK, "got 0x%08x\n", hr);
2503 hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 1000.0, 1000.0, &layout);
2504 ok(hr == S_OK, "got 0x%08x\n", hr);
2506 /* negative/zero size */
2507 r.startPosition = 1;
2508 r.length = 1;
2509 hr = IDWriteTextLayout_SetFontSize(layout, -15.0, r);
2510 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
2512 hr = IDWriteTextLayout_SetFontSize(layout, 0.0, r);
2513 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
2515 r.startPosition = 1;
2516 r.length = 0;
2517 size = 0.0;
2518 hr = IDWriteTextLayout_GetFontSize(layout, 0, &size, &r);
2519 ok(hr == S_OK, "got 0x%08x\n", hr);
2520 ok(r.startPosition == 0 && r.length == ~0u, "got %u, %u\n", r.startPosition, r.length);
2521 ok(size == 10.0, "got %.2f\n", size);
2523 r.startPosition = 1;
2524 r.length = 1;
2525 hr = IDWriteTextLayout_SetFontSize(layout, 15.0, r);
2526 ok(hr == S_OK, "got 0x%08x\n", hr);
2528 /* zero length range */
2529 r.startPosition = 1;
2530 r.length = 0;
2531 hr = IDWriteTextLayout_SetFontSize(layout, 123.0, r);
2532 ok(hr == S_OK, "got 0x%08x\n", hr);
2534 size = 0.0;
2535 hr = IDWriteTextLayout_GetFontSize(layout, 1, &size, &r);
2536 ok(hr == S_OK, "got 0x%08x\n", hr);
2537 ok(size == 15.0, "got %.2f\n", size);
2539 r.startPosition = 0;
2540 r.length = 4;
2541 hr = IDWriteTextLayout_SetFontSize(layout, 15.0, r);
2542 ok(hr == S_OK, "got 0x%08x\n", hr);
2544 size = 0.0;
2545 hr = IDWriteTextLayout_GetFontSize(layout, 1, &size, &r);
2546 ok(hr == S_OK, "got 0x%08x\n", hr);
2547 ok(size == 15.0, "got %.2f\n", size);
2549 size = 0.0;
2550 hr = IDWriteTextLayout_GetFontSize(layout, 0, &size, &r);
2551 ok(hr == S_OK, "got 0x%08x\n", hr);
2552 ok(r.startPosition == 0 && r.length == 4, "got %u, %u\n", r.startPosition, r.length);
2553 ok(size == 15.0, "got %.2f\n", size);
2555 size = 15.0;
2556 r.startPosition = r.length = 0;
2557 hr = IDWriteTextLayout_GetFontSize(layout, 20, &size, &r);
2558 ok(hr == S_OK, "got 0x%08x\n", hr);
2559 ok(r.startPosition == 4 && r.length == ~0u-4, "got %u, %u\n", r.startPosition, r.length);
2560 ok(size == 10.0, "got %.2f\n", size);
2562 r.startPosition = 100;
2563 r.length = 4;
2564 hr = IDWriteTextLayout_SetFontSize(layout, 25.0, r);
2565 ok(hr == S_OK, "got 0x%08x\n", hr);
2567 size = 15.0;
2568 r.startPosition = r.length = 0;
2569 hr = IDWriteTextLayout_GetFontSize(layout, 100, &size, &r);
2570 ok(hr == S_OK, "got 0x%08x\n", hr);
2571 ok(r.startPosition == 100 && r.length == 4, "got %u, %u\n", r.startPosition, r.length);
2572 ok(size == 25.0, "got %.2f\n", size);
2574 IDWriteTextLayout_Release(layout);
2575 IDWriteTextFormat_Release(format);
2576 IDWriteFactory_Release(factory);
2579 static void test_SetFontFamilyName(void)
2581 static const WCHAR taHomaW[] = {'T','a','H','o','m','a',0};
2582 static const WCHAR arialW[] = {'A','r','i','a','l',0};
2583 static const WCHAR strW[] = {'a','b','c','d',0};
2584 IDWriteTextFormat *format;
2585 IDWriteTextLayout *layout;
2586 IDWriteFactory *factory;
2587 DWRITE_TEXT_RANGE r;
2588 WCHAR nameW[50];
2589 HRESULT hr;
2591 factory = create_factory();
2593 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
2594 DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format);
2595 ok(hr == S_OK, "got 0x%08x\n", hr);
2597 hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 1000.0, 1000.0, &layout);
2598 ok(hr == S_OK, "got 0x%08x\n", hr);
2600 /* NULL name */
2601 r.startPosition = 1;
2602 r.length = 1;
2603 hr = IDWriteTextLayout_SetFontFamilyName(layout, NULL, r);
2604 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
2606 r.startPosition = 1;
2607 r.length = 0;
2608 nameW[0] = 0;
2609 hr = IDWriteTextLayout_GetFontFamilyName(layout, 1, nameW, sizeof(nameW)/sizeof(WCHAR), &r);
2610 ok(hr == S_OK, "got 0x%08x\n", hr);
2611 ok(r.startPosition == 0 && r.length == ~0u, "got %u, %u\n", r.startPosition, r.length);
2613 /* set name only different in casing */
2614 r.startPosition = 1;
2615 r.length = 1;
2616 hr = IDWriteTextLayout_SetFontFamilyName(layout, taHomaW, r);
2617 ok(hr == S_OK, "got 0x%08x\n", hr);
2619 /* zero length range */
2620 r.startPosition = 1;
2621 r.length = 0;
2622 hr = IDWriteTextLayout_SetFontFamilyName(layout, arialW, r);
2623 ok(hr == S_OK, "got 0x%08x\n", hr);
2625 r.startPosition = 0;
2626 r.length = 0;
2627 nameW[0] = 0;
2628 hr = IDWriteTextLayout_GetFontFamilyName(layout, 1, nameW, sizeof(nameW)/sizeof(WCHAR), &r);
2629 ok(hr == S_OK, "got 0x%08x\n", hr);
2630 ok(!lstrcmpW(nameW, taHomaW), "got %s\n", wine_dbgstr_w(nameW));
2631 ok(r.startPosition == 1 && r.length == 1, "got %u, %u\n", r.startPosition, r.length);
2633 r.startPosition = 1;
2634 r.length = 1;
2635 hr = IDWriteTextLayout_SetFontFamilyName(layout, arialW, r);
2636 ok(hr == S_OK, "got 0x%08x\n", hr);
2638 r.startPosition = 1;
2639 r.length = 0;
2640 hr = IDWriteTextLayout_GetFontFamilyName(layout, 1, nameW, sizeof(nameW)/sizeof(WCHAR), &r);
2641 ok(hr == S_OK, "got 0x%08x\n", hr);
2642 ok(r.startPosition == 1 && r.length == 1, "got %u, %u\n", r.startPosition, r.length);
2644 r.startPosition = 0;
2645 r.length = 4;
2646 hr = IDWriteTextLayout_SetFontFamilyName(layout, arialW, r);
2647 ok(hr == S_OK, "got 0x%08x\n", hr);
2649 nameW[0] = 0;
2650 hr = IDWriteTextLayout_GetFontFamilyName(layout, 1, nameW, sizeof(nameW)/sizeof(WCHAR), &r);
2651 ok(hr == S_OK, "got 0x%08x\n", hr);
2652 ok(r.startPosition == 0 && r.length == 4, "got %u, %u\n", r.startPosition, r.length);
2653 ok(!lstrcmpW(nameW, arialW), "got name %s\n", wine_dbgstr_w(nameW));
2655 IDWriteTextLayout_Release(layout);
2656 IDWriteTextFormat_Release(format);
2657 IDWriteFactory_Release(factory);
2660 static void test_SetFontStyle(void)
2662 static const WCHAR strW[] = {'a','b','c','d',0};
2663 IDWriteTextFormat *format;
2664 IDWriteTextLayout *layout;
2665 IDWriteFactory *factory;
2666 DWRITE_FONT_STYLE style;
2667 DWRITE_TEXT_RANGE r;
2668 HRESULT hr;
2670 factory = create_factory();
2672 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
2673 DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format);
2674 ok(hr == S_OK, "got 0x%08x\n", hr);
2676 hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 1000.0, 1000.0, &layout);
2677 ok(hr == S_OK, "got 0x%08x\n", hr);
2679 /* invalid style value */
2680 r.startPosition = 1;
2681 r.length = 1;
2682 hr = IDWriteTextLayout_SetFontStyle(layout, DWRITE_FONT_STYLE_ITALIC+1, r);
2683 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
2685 r.startPosition = 1;
2686 r.length = 0;
2687 hr = IDWriteTextLayout_GetFontStyle(layout, 0, &style, &r);
2688 ok(hr == S_OK, "got 0x%08x\n", hr);
2689 ok(r.startPosition == 0 && r.length == ~0u, "got %u, %u\n", r.startPosition, r.length);
2690 ok(style == DWRITE_FONT_STYLE_NORMAL, "got %d\n", style);
2692 r.startPosition = 1;
2693 r.length = 1;
2694 hr = IDWriteTextLayout_SetFontStyle(layout, DWRITE_FONT_STYLE_ITALIC, r);
2695 ok(hr == S_OK, "got 0x%08x\n", hr);
2697 /* zero length range */
2698 r.startPosition = 1;
2699 r.length = 0;
2700 hr = IDWriteTextLayout_SetFontStyle(layout, DWRITE_FONT_STYLE_NORMAL, r);
2701 ok(hr == S_OK, "got 0x%08x\n", hr);
2703 style = DWRITE_FONT_STYLE_NORMAL;
2704 hr = IDWriteTextLayout_GetFontStyle(layout, 1, &style, &r);
2705 ok(hr == S_OK, "got 0x%08x\n", hr);
2706 ok(style == DWRITE_FONT_STYLE_ITALIC, "got %d\n", style);
2708 r.startPosition = 0;
2709 r.length = 4;
2710 hr = IDWriteTextLayout_SetFontStyle(layout, DWRITE_FONT_STYLE_OBLIQUE, r);
2711 ok(hr == S_OK, "got 0x%08x\n", hr);
2713 style = DWRITE_FONT_STYLE_ITALIC;
2714 hr = IDWriteTextLayout_GetFontStyle(layout, 1, &style, &r);
2715 ok(hr == S_OK, "got 0x%08x\n", hr);
2716 ok(style == DWRITE_FONT_STYLE_OBLIQUE, "got %d\n", style);
2718 style = DWRITE_FONT_STYLE_ITALIC;
2719 hr = IDWriteTextLayout_GetFontStyle(layout, 0, &style, &r);
2720 ok(hr == S_OK, "got 0x%08x\n", hr);
2721 ok(r.startPosition == 0 && r.length == 4, "got %u, %u\n", r.startPosition, r.length);
2722 ok(style == DWRITE_FONT_STYLE_OBLIQUE, "got %d\n", style);
2724 style = DWRITE_FONT_STYLE_ITALIC;
2725 r.startPosition = r.length = 0;
2726 hr = IDWriteTextLayout_GetFontStyle(layout, 20, &style, &r);
2727 ok(hr == S_OK, "got 0x%08x\n", hr);
2728 ok(r.startPosition == 4 && r.length == ~0u-4, "got %u, %u\n", r.startPosition, r.length);
2729 ok(style == DWRITE_FONT_STYLE_NORMAL, "got %d\n", style);
2731 r.startPosition = 100;
2732 r.length = 4;
2733 hr = IDWriteTextLayout_SetFontStyle(layout, DWRITE_FONT_STYLE_OBLIQUE, r);
2734 ok(hr == S_OK, "got 0x%08x\n", hr);
2736 style = DWRITE_FONT_STYLE_NORMAL;
2737 r.startPosition = r.length = 0;
2738 hr = IDWriteTextLayout_GetFontStyle(layout, 100, &style, &r);
2739 ok(hr == S_OK, "got 0x%08x\n", hr);
2740 ok(r.startPosition == 100 && r.length == 4, "got %u, %u\n", r.startPosition, r.length);
2741 ok(style == DWRITE_FONT_STYLE_OBLIQUE, "got %d\n", style);
2743 IDWriteTextLayout_Release(layout);
2744 IDWriteTextFormat_Release(format);
2745 IDWriteFactory_Release(factory);
2748 static void test_SetFontStretch(void)
2750 static const WCHAR strW[] = {'a','b','c','d',0};
2751 DWRITE_FONT_STRETCH stretch;
2752 IDWriteTextFormat *format;
2753 IDWriteTextLayout *layout;
2754 IDWriteFactory *factory;
2755 DWRITE_TEXT_RANGE r;
2756 HRESULT hr;
2758 factory = create_factory();
2760 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
2761 DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format);
2762 ok(hr == S_OK, "got 0x%08x\n", hr);
2764 hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 1000.0, 1000.0, &layout);
2765 ok(hr == S_OK, "got 0x%08x\n", hr);
2767 /* invalid stretch value */
2768 r.startPosition = 1;
2769 r.length = 1;
2770 hr = IDWriteTextLayout_SetFontStretch(layout, DWRITE_FONT_STRETCH_ULTRA_EXPANDED+1, r);
2771 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
2773 r.startPosition = 1;
2774 r.length = 0;
2775 stretch = DWRITE_FONT_STRETCH_UNDEFINED;
2776 hr = IDWriteTextLayout_GetFontStretch(layout, 0, &stretch, &r);
2777 ok(hr == S_OK, "got 0x%08x\n", hr);
2778 ok(r.startPosition == 0 && r.length == ~0u, "got %u, %u\n", r.startPosition, r.length);
2779 ok(stretch == DWRITE_FONT_STRETCH_NORMAL, "got %d\n", stretch);
2781 r.startPosition = 1;
2782 r.length = 1;
2783 hr = IDWriteTextLayout_SetFontStretch(layout, DWRITE_FONT_STRETCH_CONDENSED, r);
2784 ok(hr == S_OK, "got 0x%08x\n", hr);
2786 /* zero length range */
2787 r.startPosition = 1;
2788 r.length = 0;
2789 hr = IDWriteTextLayout_SetFontStretch(layout, DWRITE_FONT_STRETCH_NORMAL, r);
2790 ok(hr == S_OK, "got 0x%08x\n", hr);
2792 stretch = DWRITE_FONT_STRETCH_UNDEFINED;
2793 hr = IDWriteTextLayout_GetFontStretch(layout, 1, &stretch, &r);
2794 ok(hr == S_OK, "got 0x%08x\n", hr);
2795 ok(stretch == DWRITE_FONT_STRETCH_CONDENSED, "got %d\n", stretch);
2797 r.startPosition = 0;
2798 r.length = 4;
2799 hr = IDWriteTextLayout_SetFontStretch(layout, DWRITE_FONT_STRETCH_EXPANDED, r);
2800 ok(hr == S_OK, "got 0x%08x\n", hr);
2802 stretch = DWRITE_FONT_STRETCH_UNDEFINED;
2803 hr = IDWriteTextLayout_GetFontStretch(layout, 1, &stretch, &r);
2804 ok(hr == S_OK, "got 0x%08x\n", hr);
2805 ok(stretch == DWRITE_FONT_STRETCH_EXPANDED, "got %d\n", stretch);
2807 stretch = DWRITE_FONT_STRETCH_UNDEFINED;
2808 hr = IDWriteTextLayout_GetFontStretch(layout, 0, &stretch, &r);
2809 ok(hr == S_OK, "got 0x%08x\n", hr);
2810 ok(r.startPosition == 0 && r.length == 4, "got %u, %u\n", r.startPosition, r.length);
2811 ok(stretch == DWRITE_FONT_STRETCH_EXPANDED, "got %d\n", stretch);
2813 stretch = DWRITE_FONT_STRETCH_UNDEFINED;
2814 r.startPosition = r.length = 0;
2815 hr = IDWriteTextLayout_GetFontStretch(layout, 20, &stretch, &r);
2816 ok(hr == S_OK, "got 0x%08x\n", hr);
2817 ok(r.startPosition == 4 && r.length == ~0u-4, "got %u, %u\n", r.startPosition, r.length);
2818 ok(stretch == DWRITE_FONT_STRETCH_NORMAL, "got %d\n", stretch);
2820 r.startPosition = 100;
2821 r.length = 4;
2822 hr = IDWriteTextLayout_SetFontStretch(layout, DWRITE_FONT_STRETCH_EXPANDED, r);
2823 ok(hr == S_OK, "got 0x%08x\n", hr);
2825 stretch = DWRITE_FONT_STRETCH_UNDEFINED;
2826 r.startPosition = r.length = 0;
2827 hr = IDWriteTextLayout_GetFontStretch(layout, 100, &stretch, &r);
2828 ok(hr == S_OK, "got 0x%08x\n", hr);
2829 ok(r.startPosition == 100 && r.length == 4, "got %u, %u\n", r.startPosition, r.length);
2830 ok(stretch == DWRITE_FONT_STRETCH_EXPANDED, "got %d\n", stretch);
2832 /* trying to set undefined value */
2833 r.startPosition = 0;
2834 r.length = 2;
2835 hr = IDWriteTextLayout_SetFontStretch(layout, DWRITE_FONT_STRETCH_UNDEFINED, r);
2836 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
2838 IDWriteTextLayout_Release(layout);
2839 IDWriteTextFormat_Release(format);
2840 IDWriteFactory_Release(factory);
2843 static void test_SetStrikethrough(void)
2845 static const WCHAR strW[] = {'a','b','c','d',0};
2846 IDWriteTextFormat *format;
2847 IDWriteTextLayout *layout;
2848 IDWriteFactory *factory;
2849 DWRITE_TEXT_RANGE r;
2850 BOOL value;
2851 HRESULT hr;
2853 factory = create_factory();
2855 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
2856 DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format);
2857 ok(hr == S_OK, "got 0x%08x\n", hr);
2859 hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 1000.0, 1000.0, &layout);
2860 ok(hr == S_OK, "got 0x%08x\n", hr);
2862 r.startPosition = 1;
2863 r.length = 0;
2864 value = TRUE;
2865 hr = IDWriteTextLayout_GetStrikethrough(layout, 0, &value, &r);
2866 ok(hr == S_OK, "got 0x%08x\n", hr);
2867 ok(r.startPosition == 0 && r.length == ~0u, "got %u, %u\n", r.startPosition, r.length);
2868 ok(value == FALSE, "got %d\n", value);
2870 r.startPosition = 1;
2871 r.length = 1;
2872 hr = IDWriteTextLayout_SetStrikethrough(layout, TRUE, r);
2873 ok(hr == S_OK, "got 0x%08x\n", hr);
2875 value = FALSE;
2876 hr = IDWriteTextLayout_GetStrikethrough(layout, 1, &value, &r);
2877 ok(hr == S_OK, "got 0x%08x\n", hr);
2878 ok(value == TRUE, "got %d\n", value);
2879 ok(r.startPosition == 1 && r.length == 1, "got %u, %u\n", r.startPosition, r.length);
2881 value = TRUE;
2882 r.startPosition = r.length = 0;
2883 hr = IDWriteTextLayout_GetStrikethrough(layout, 20, &value, &r);
2884 ok(hr == S_OK, "got 0x%08x\n", hr);
2885 ok(r.startPosition == 2 && r.length == ~0u-2, "got %u, %u\n", r.startPosition, r.length);
2886 ok(value == FALSE, "got %d\n", value);
2888 r.startPosition = 100;
2889 r.length = 4;
2890 hr = IDWriteTextLayout_SetStrikethrough(layout, TRUE, r);
2891 ok(hr == S_OK, "got 0x%08x\n", hr);
2893 value = FALSE;
2894 r.startPosition = r.length = 0;
2895 hr = IDWriteTextLayout_GetStrikethrough(layout, 100, &value, &r);
2896 ok(hr == S_OK, "got 0x%08x\n", hr);
2897 ok(r.startPosition == 100 && r.length == 4, "got %u, %u\n", r.startPosition, r.length);
2898 ok(value == TRUE, "got %d\n", value);
2900 IDWriteTextLayout_Release(layout);
2901 IDWriteTextFormat_Release(format);
2902 IDWriteFactory_Release(factory);
2905 static void test_GetMetrics(void)
2907 static const WCHAR str2W[] = {0x2066,')',')',0x661,'(',0x627,')',0};
2908 static const WCHAR strW[] = {'a','b','c','d',0};
2909 static const WCHAR str3W[] = {'a',0};
2910 DWRITE_CLUSTER_METRICS clusters[4];
2911 DWRITE_TEXT_METRICS metrics;
2912 IDWriteTextFormat *format;
2913 IDWriteTextLayout *layout;
2914 IDWriteFactory *factory;
2915 UINT32 count, i;
2916 FLOAT width;
2917 HRESULT hr;
2919 factory = create_factory();
2921 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
2922 DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format);
2923 ok(hr == S_OK, "got 0x%08x\n", hr);
2925 hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 500.0, 1000.0, &layout);
2926 ok(hr == S_OK, "got 0x%08x\n", hr);
2928 count = 0;
2929 hr = IDWriteTextLayout_GetClusterMetrics(layout, clusters, 4, &count);
2930 ok(hr == S_OK, "got 0x%08x\n", hr);
2931 ok(count == 4, "got %u\n", count);
2932 for (i = 0, width = 0.0; i < count; i++)
2933 width += clusters[i].width;
2935 memset(&metrics, 0xcc, sizeof(metrics));
2936 hr = IDWriteTextLayout_GetMetrics(layout, &metrics);
2937 ok(hr == S_OK, "got 0x%08x\n", hr);
2938 ok(metrics.left == 0.0, "got %.2f\n", metrics.left);
2939 ok(metrics.top == 0.0, "got %.2f\n", metrics.top);
2940 ok(metrics.width == width, "got %.2f, expected %.2f\n", metrics.width, width);
2941 ok(metrics.widthIncludingTrailingWhitespace == width, "got %.2f, expected %.2f\n",
2942 metrics.widthIncludingTrailingWhitespace, width);
2943 ok(metrics.height > 0.0, "got %.2f\n", metrics.height);
2944 ok(metrics.layoutWidth == 500.0, "got %.2f\n", metrics.layoutWidth);
2945 ok(metrics.layoutHeight == 1000.0, "got %.2f\n", metrics.layoutHeight);
2946 ok(metrics.maxBidiReorderingDepth == 1, "got %u\n", metrics.maxBidiReorderingDepth);
2947 ok(metrics.lineCount == 1, "got %u\n", metrics.lineCount);
2949 IDWriteTextLayout_Release(layout);
2951 /* a string with more complex bidi sequence */
2952 hr = IDWriteFactory_CreateTextLayout(factory, str2W, 7, format, 500.0, 1000.0, &layout);
2953 ok(hr == S_OK, "got 0x%08x\n", hr);
2955 memset(&metrics, 0xcc, sizeof(metrics));
2956 metrics.maxBidiReorderingDepth = 0;
2957 hr = IDWriteTextLayout_GetMetrics(layout, &metrics);
2958 ok(hr == S_OK, "got 0x%08x\n", hr);
2959 ok(metrics.left == 0.0, "got %.2f\n", metrics.left);
2960 ok(metrics.top == 0.0, "got %.2f\n", metrics.top);
2961 ok(metrics.width > 0.0, "got %.2f\n", metrics.width);
2962 ok(metrics.widthIncludingTrailingWhitespace > 0.0, "got %.2f\n", metrics.widthIncludingTrailingWhitespace);
2963 ok(metrics.height > 0.0, "got %.2f\n", metrics.height);
2964 ok(metrics.layoutWidth == 500.0, "got %.2f\n", metrics.layoutWidth);
2965 ok(metrics.layoutHeight == 1000.0, "got %.2f\n", metrics.layoutHeight);
2966 todo_wine
2967 ok(metrics.maxBidiReorderingDepth > 1, "got %u\n", metrics.maxBidiReorderingDepth);
2968 ok(metrics.lineCount == 1, "got %u\n", metrics.lineCount);
2970 IDWriteTextLayout_Release(layout);
2972 /* single cluster layout */
2973 hr = IDWriteFactory_CreateTextLayout(factory, str3W, 1, format, 500.0, 1000.0, &layout);
2974 ok(hr == S_OK, "got 0x%08x\n", hr);
2976 count = 0;
2977 hr = IDWriteTextLayout_GetClusterMetrics(layout, clusters, 1, &count);
2978 ok(hr == S_OK, "got 0x%08x\n", hr);
2979 ok(count == 1, "got %u\n", count);
2981 memset(&metrics, 0xcc, sizeof(metrics));
2982 hr = IDWriteTextLayout_GetMetrics(layout, &metrics);
2983 ok(hr == S_OK, "got 0x%08x\n", hr);
2984 ok(metrics.left == 0.0, "got %.2f\n", metrics.left);
2985 ok(metrics.top == 0.0, "got %.2f\n", metrics.top);
2986 ok(metrics.width == clusters[0].width, "got %.2f, expected %.2f\n", metrics.width, clusters[0].width);
2987 ok(metrics.widthIncludingTrailingWhitespace == clusters[0].width, "got %.2f\n", metrics.widthIncludingTrailingWhitespace);
2988 ok(metrics.height > 0.0, "got %.2f\n", metrics.height);
2989 ok(metrics.layoutWidth == 500.0, "got %.2f\n", metrics.layoutWidth);
2990 ok(metrics.layoutHeight == 1000.0, "got %.2f\n", metrics.layoutHeight);
2991 ok(metrics.maxBidiReorderingDepth == 1, "got %u\n", metrics.maxBidiReorderingDepth);
2992 ok(metrics.lineCount == 1, "got %u\n", metrics.lineCount);
2993 IDWriteTextLayout_Release(layout);
2995 IDWriteTextFormat_Release(format);
2996 IDWriteFactory_Release(factory);
2999 static void test_SetFlowDirection(void)
3001 static const WCHAR strW[] = {'a','b','c','d',0};
3002 DWRITE_READING_DIRECTION reading;
3003 DWRITE_FLOW_DIRECTION flow;
3004 IDWriteTextFormat *format;
3005 IDWriteTextLayout *layout;
3006 IDWriteFactory *factory;
3007 HRESULT hr;
3009 factory = create_factory();
3011 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
3012 DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format);
3013 ok(hr == S_OK, "got 0x%08x\n", hr);
3015 flow = IDWriteTextFormat_GetFlowDirection(format);
3016 ok(flow == DWRITE_FLOW_DIRECTION_TOP_TO_BOTTOM, "got %d\n", flow);
3018 reading = IDWriteTextFormat_GetReadingDirection(format);
3019 ok(reading == DWRITE_READING_DIRECTION_LEFT_TO_RIGHT, "got %d\n", reading);
3021 hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 500.0, 1000.0, &layout);
3022 ok(hr == S_OK, "got 0x%08x\n", hr);
3023 IDWriteTextLayout_Release(layout);
3025 hr = IDWriteTextFormat_SetFlowDirection(format, DWRITE_FLOW_DIRECTION_LEFT_TO_RIGHT);
3026 ok(hr == S_OK || broken(hr == E_INVALIDARG) /* vista,win7 */, "got 0x%08x\n", hr);
3027 if (hr == S_OK) {
3028 hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 500.0, 1000.0, &layout);
3029 ok(hr == S_OK, "got 0x%08x\n", hr);
3030 IDWriteTextLayout_Release(layout);
3032 hr = IDWriteTextFormat_SetReadingDirection(format, DWRITE_READING_DIRECTION_TOP_TO_BOTTOM);
3033 ok(hr == S_OK, "got 0x%08x\n", hr);
3035 hr = IDWriteTextFormat_SetFlowDirection(format, DWRITE_FLOW_DIRECTION_TOP_TO_BOTTOM);
3036 ok(hr == S_OK, "got 0x%08x\n", hr);
3038 hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 500.0, 1000.0, &layout);
3039 ok(hr == S_OK, "got 0x%08x\n", hr);
3040 IDWriteTextLayout_Release(layout);
3042 else
3043 win_skip("DWRITE_FLOW_DIRECTION_LEFT_TO_RIGHT is not supported\n");
3045 IDWriteTextFormat_Release(format);
3046 IDWriteFactory_Release(factory);
3049 static const struct drawcall_entry draweffect_seq[] = {
3050 { DRAW_GLYPHRUN|DRAW_EFFECT, {'a','e',0x0300,0} },
3051 { DRAW_GLYPHRUN, {'d',0} },
3052 { DRAW_LAST_KIND }
3055 static const struct drawcall_entry draweffect2_seq[] = {
3056 { DRAW_GLYPHRUN|DRAW_EFFECT, {'a','e',0} },
3057 { DRAW_GLYPHRUN, {'c','d',0} },
3058 { DRAW_LAST_KIND }
3061 static const struct drawcall_entry draweffect3_seq[] = {
3062 { DRAW_INLINE|DRAW_EFFECT },
3063 { DRAW_LAST_KIND }
3066 static const struct drawcall_entry draweffect4_seq[] = {
3067 { DRAW_INLINE },
3068 { DRAW_LAST_KIND }
3071 static void test_SetDrawingEffect(void)
3073 static const WCHAR strW[] = {'a','e',0x0300,'d',0}; /* accent grave */
3074 static const WCHAR str2W[] = {'a','e','c','d',0};
3075 IDWriteInlineObject *sign;
3076 IDWriteTextFormat *format;
3077 IDWriteTextLayout *layout;
3078 IDWriteFactory *factory;
3079 DWRITE_TEXT_RANGE r;
3080 IUnknown *unk;
3081 HRESULT hr;
3083 factory = create_factory();
3085 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
3086 DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format);
3087 ok(hr == S_OK, "got 0x%08x\n", hr);
3089 /* string with combining mark */
3090 hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 500.0, 1000.0, &layout);
3091 ok(hr == S_OK, "got 0x%08x\n", hr);
3093 /* set effect past the end of text */
3094 r.startPosition = 100;
3095 r.length = 10;
3096 hr = IDWriteTextLayout_SetDrawingEffect(layout, &testeffect, r);
3097 ok(hr == S_OK, "got 0x%08x\n", hr);
3099 r.startPosition = r.length = 0;
3100 hr = IDWriteTextLayout_GetDrawingEffect(layout, 101, &unk, &r);
3101 ok(hr == S_OK, "got 0x%08x\n", hr);
3102 ok(r.startPosition == 100 && r.length == 10, "got %u, %u\n", r.startPosition, r.length);
3104 r.startPosition = r.length = 0;
3105 unk = (void*)0xdeadbeef;
3106 hr = IDWriteTextLayout_GetDrawingEffect(layout, 1000, &unk, &r);
3107 ok(hr == S_OK, "got 0x%08x\n", hr);
3108 ok(r.startPosition == 110 && r.length == ~0u-110, "got %u, %u\n", r.startPosition, r.length);
3109 ok(unk == NULL, "got %p\n", unk);
3111 /* effect is applied to clusters, not individual text positions */
3112 r.startPosition = 0;
3113 r.length = 2;
3114 hr = IDWriteTextLayout_SetDrawingEffect(layout, &testeffect, r);
3115 ok(hr == S_OK, "got 0x%08x\n", hr);
3117 flush_sequence(sequences, RENDERER_ID);
3118 hr = IDWriteTextLayout_Draw(layout, NULL, &testrenderer, 0.0, 0.0);
3119 ok(hr == S_OK, "got 0x%08x\n", hr);
3120 ok_sequence(sequences, RENDERER_ID, draweffect_seq, "effect draw test", TRUE);
3121 IDWriteTextLayout_Release(layout);
3123 /* simple string */
3124 hr = IDWriteFactory_CreateTextLayout(factory, str2W, 4, format, 500.0, 1000.0, &layout);
3125 ok(hr == S_OK, "got 0x%08x\n", hr);
3127 r.startPosition = 0;
3128 r.length = 2;
3129 hr = IDWriteTextLayout_SetDrawingEffect(layout, &testeffect, r);
3130 ok(hr == S_OK, "got 0x%08x\n", hr);
3132 flush_sequence(sequences, RENDERER_ID);
3133 hr = IDWriteTextLayout_Draw(layout, NULL, &testrenderer, 0.0, 0.0);
3134 ok(hr == S_OK, "got 0x%08x\n", hr);
3135 ok_sequence(sequences, RENDERER_ID, draweffect2_seq, "effect draw test 2", FALSE);
3136 IDWriteTextLayout_Release(layout);
3138 /* Inline object - effect set for same range */
3139 hr = IDWriteFactory_CreateEllipsisTrimmingSign(factory, format, &sign);
3140 ok(hr == S_OK, "got 0x%08x\n", hr);
3142 hr = IDWriteFactory_CreateTextLayout(factory, str2W, 4, format, 500.0, 1000.0, &layout);
3143 ok(hr == S_OK, "got 0x%08x\n", hr);
3145 r.startPosition = 0;
3146 r.length = 4;
3147 hr = IDWriteTextLayout_SetInlineObject(layout, sign, r);
3148 ok(hr == S_OK, "got 0x%08x\n", hr);
3150 hr = IDWriteTextLayout_SetDrawingEffect(layout, &testeffect, r);
3151 ok(hr == S_OK, "got 0x%08x\n", hr);
3153 flush_sequence(sequences, RENDERER_ID);
3154 hr = IDWriteTextLayout_Draw(layout, NULL, &testrenderer, 0.0, 0.0);
3155 ok(hr == S_OK, "got 0x%08x\n", hr);
3156 ok_sequence(sequences, RENDERER_ID, draweffect3_seq, "effect draw test 3", FALSE);
3158 /* now set effect somewhere inside a range replaced by inline object */
3159 hr = IDWriteTextLayout_SetDrawingEffect(layout, NULL, r);
3160 ok(hr == S_OK, "got 0x%08x\n", hr);
3162 r.startPosition = 1;
3163 r.length = 1;
3164 hr = IDWriteTextLayout_SetDrawingEffect(layout, &testeffect, r);
3165 ok(hr == S_OK, "got 0x%08x\n", hr);
3167 /* no effect is reported in this case */
3168 flush_sequence(sequences, RENDERER_ID);
3169 hr = IDWriteTextLayout_Draw(layout, NULL, &testrenderer, 0.0, 0.0);
3170 ok(hr == S_OK, "got 0x%08x\n", hr);
3171 ok_sequence(sequences, RENDERER_ID, draweffect4_seq, "effect draw test 4", FALSE);
3173 r.startPosition = 0;
3174 r.length = 4;
3175 hr = IDWriteTextLayout_SetDrawingEffect(layout, NULL, r);
3176 ok(hr == S_OK, "got 0x%08x\n", hr);
3178 r.startPosition = 0;
3179 r.length = 1;
3180 hr = IDWriteTextLayout_SetDrawingEffect(layout, &testeffect, r);
3181 ok(hr == S_OK, "got 0x%08x\n", hr);
3183 /* first range position is all that matters for inline ranges */
3184 flush_sequence(sequences, RENDERER_ID);
3185 hr = IDWriteTextLayout_Draw(layout, NULL, &testrenderer, 0.0, 0.0);
3186 ok(hr == S_OK, "got 0x%08x\n", hr);
3187 ok_sequence(sequences, RENDERER_ID, draweffect3_seq, "effect draw test 5", FALSE);
3189 IDWriteTextLayout_Release(layout);
3191 IDWriteInlineObject_Release(sign);
3192 IDWriteTextFormat_Release(format);
3193 IDWriteFactory_Release(factory);
3196 static IDWriteFontFace *get_fontface_from_format(IDWriteTextFormat *format)
3198 IDWriteFontCollection *collection;
3199 IDWriteFontFamily *family;
3200 IDWriteFontFace *fontface;
3201 IDWriteFont *font;
3202 WCHAR nameW[255];
3203 UINT32 index;
3204 BOOL exists;
3205 HRESULT hr;
3207 hr = IDWriteTextFormat_GetFontCollection(format, &collection);
3208 ok(hr == S_OK, "got 0x%08x\n", hr);
3210 hr = IDWriteTextFormat_GetFontFamilyName(format, nameW, sizeof(nameW)/sizeof(WCHAR));
3211 ok(hr == S_OK, "got 0x%08x\n", hr);
3213 hr = IDWriteFontCollection_FindFamilyName(collection, nameW, &index, &exists);
3214 ok(hr == S_OK, "got 0x%08x\n", hr);
3216 hr = IDWriteFontCollection_GetFontFamily(collection, index, &family);
3217 ok(hr == S_OK, "got 0x%08x\n", hr);
3218 IDWriteFontCollection_Release(collection);
3220 hr = IDWriteFontFamily_GetFirstMatchingFont(family,
3221 IDWriteTextFormat_GetFontWeight(format),
3222 IDWriteTextFormat_GetFontStretch(format),
3223 IDWriteTextFormat_GetFontStyle(format),
3224 &font);
3225 ok(hr == S_OK, "got 0x%08x\n", hr);
3227 hr = IDWriteFont_CreateFontFace(font, &fontface);
3228 ok(hr == S_OK, "got 0x%08x\n", hr);
3230 IDWriteFont_Release(font);
3231 IDWriteFontFamily_Release(family);
3233 return fontface;
3236 static BOOL get_enus_string(IDWriteLocalizedStrings *strings, WCHAR *buff, UINT32 size)
3238 UINT32 index;
3239 BOOL exists = FALSE;
3240 HRESULT hr;
3242 hr = IDWriteLocalizedStrings_FindLocaleName(strings, enusW, &index, &exists);
3243 ok(hr == S_OK, "got 0x%08x\n", hr);
3245 if (exists) {
3246 hr = IDWriteLocalizedStrings_GetString(strings, index, buff, size);
3247 ok(hr == S_OK, "got 0x%08x\n", hr);
3249 else
3250 *buff = 0;
3252 return exists;
3255 static void test_GetLineMetrics(void)
3257 static const WCHAR str3W[] = {'a','\r','b','\n','c','\n','\r','d','\r','\n',0};
3258 static const WCHAR strW[] = {'a','b','c','d',' ',0};
3259 static const WCHAR str2W[] = {'a','b','\r','c','d',0};
3260 static const WCHAR str4W[] = {'a','\r',0};
3261 IDWriteFontCollection *syscollection;
3262 DWRITE_FONT_METRICS fontmetrics;
3263 DWRITE_LINE_METRICS metrics[6];
3264 UINT32 count, i, familycount;
3265 IDWriteTextFormat *format;
3266 IDWriteTextLayout *layout;
3267 IDWriteFontFace *fontface;
3268 IDWriteFactory *factory;
3269 DWRITE_TEXT_RANGE range;
3270 WCHAR nameW[256];
3271 HRESULT hr;
3273 factory = create_factory();
3275 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
3276 DWRITE_FONT_STRETCH_NORMAL, 2048.0, enusW, &format);
3277 ok(hr == S_OK, "got 0x%08x\n", hr);
3279 hr = IDWriteFactory_CreateTextLayout(factory, strW, 5, format, 30000.0, 1000.0, &layout);
3280 ok(hr == S_OK, "got 0x%08x\n", hr);
3282 count = 0;
3283 hr = IDWriteTextLayout_GetLineMetrics(layout, metrics, 0, &count);
3284 ok(hr == E_NOT_SUFFICIENT_BUFFER, "got 0x%08x\n", hr);
3285 ok(count == 1, "got count %u\n", count);
3287 memset(metrics, 0, sizeof(metrics));
3288 hr = IDWriteTextLayout_GetLineMetrics(layout, metrics, 1, &count);
3289 ok(hr == S_OK, "got 0x%08x\n", hr);
3290 ok(metrics[0].length == 5, "got %u\n", metrics[0].length);
3291 ok(metrics[0].trailingWhitespaceLength == 1, "got %u\n", metrics[0].trailingWhitespaceLength);
3293 ok(metrics[0].newlineLength == 0, "got %u\n", metrics[0].newlineLength);
3294 ok(metrics[0].isTrimmed == FALSE, "got %d\n", metrics[0].isTrimmed);
3296 IDWriteTextLayout_Release(layout);
3298 /* Test line height and baseline calculation */
3299 hr = IDWriteFactory_GetSystemFontCollection(factory, &syscollection, FALSE);
3300 ok(hr == S_OK, "got 0x%08x\n", hr);
3301 familycount = IDWriteFontCollection_GetFontFamilyCount(syscollection);
3303 for (i = 0; i < familycount; i++) {
3304 static const WCHAR mvboliW[] = {'M','V',' ','B','o','l','i',0};
3305 IDWriteLocalizedStrings *names;
3306 IDWriteFontFamily *family;
3307 IDWriteFont *font;
3308 BOOL exists;
3310 format = NULL;
3311 layout = NULL;
3313 hr = IDWriteFontCollection_GetFontFamily(syscollection, i, &family);
3314 ok(hr == S_OK, "got 0x%08x\n", hr);
3316 hr = IDWriteFontFamily_GetFirstMatchingFont(family, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STRETCH_NORMAL,
3317 DWRITE_FONT_STYLE_NORMAL, &font);
3318 ok(hr == S_OK, "got 0x%08x\n", hr);
3320 hr = IDWriteFont_CreateFontFace(font, &fontface);
3321 ok(hr == S_OK, "got 0x%08x\n", hr);
3323 hr = IDWriteFontFamily_GetFamilyNames(family, &names);
3324 ok(hr == S_OK, "got 0x%08x\n", hr);
3326 if (!(exists = get_enus_string(names, nameW, sizeof(nameW)/sizeof(nameW[0])))) {
3327 IDWriteLocalFontFileLoader *localloader;
3328 IDWriteFontFileLoader *loader;
3329 IDWriteFontFile *file;
3330 const void *key;
3331 UINT32 keysize;
3332 UINT32 count;
3334 count = 1;
3335 hr = IDWriteFontFace_GetFiles(fontface, &count, &file);
3336 ok(hr == S_OK, "got 0x%08x\n", hr);
3338 hr = IDWriteFontFile_GetLoader(file, &loader);
3339 ok(hr == S_OK, "got 0x%08x\n", hr);
3341 hr = IDWriteFontFileLoader_QueryInterface(loader, &IID_IDWriteLocalFontFileLoader, (void**)&localloader);
3342 ok(hr == S_OK, "got 0x%08x\n", hr);
3343 IDWriteFontFileLoader_Release(loader);
3345 hr = IDWriteFontFile_GetReferenceKey(file, &key, &keysize);
3346 ok(hr == S_OK, "got 0x%08x\n", hr);
3348 hr = IDWriteLocalFontFileLoader_GetFilePathFromKey(localloader, key, keysize, nameW, sizeof(nameW)/sizeof(*nameW));
3349 ok(hr == S_OK, "got 0x%08x\n", hr);
3351 skip("Failed to get English family name, font file %s\n", wine_dbgstr_w(nameW));
3353 IDWriteLocalFontFileLoader_Release(localloader);
3354 IDWriteFontFile_Release(file);
3357 IDWriteLocalizedStrings_Release(names);
3358 IDWriteFont_Release(font);
3360 if (!exists)
3361 goto cleanup;
3363 /* This will effectively skip on Vista/2008 only, newer systems work just fine with this font. */
3364 if (!lstrcmpW(nameW, mvboliW)) {
3365 skip("Skipping line metrics test for %s, gives inconsistent results\n", wine_dbgstr_w(nameW));
3366 goto cleanup;
3369 IDWriteFontFace_GetMetrics(fontface, &fontmetrics);
3370 hr = IDWriteFactory_CreateTextFormat(factory, nameW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
3371 DWRITE_FONT_STRETCH_NORMAL, fontmetrics.designUnitsPerEm, enusW, &format);
3372 ok(hr == S_OK, "got 0x%08x\n", hr);
3374 hr = IDWriteFactory_CreateTextLayout(factory, strW, 5, format, 30000.0f, 100.0f, &layout);
3375 ok(hr == S_OK, "got 0x%08x\n", hr);
3377 memset(metrics, 0, sizeof(metrics));
3378 count = 0;
3379 hr = IDWriteTextLayout_GetLineMetrics(layout, metrics, 2, &count);
3380 ok(hr == S_OK, "got 0x%08x\n", hr);
3381 ok(count == 1, "got %u\n", count);
3383 ok(metrics[0].baseline == fontmetrics.ascent + fontmetrics.lineGap, "%s: got %.2f, expected %d, "
3384 "linegap %d\n", wine_dbgstr_w(nameW), metrics[0].baseline, fontmetrics.ascent + fontmetrics.lineGap,
3385 fontmetrics.lineGap);
3386 ok(metrics[0].height == fontmetrics.ascent + fontmetrics.descent + fontmetrics.lineGap,
3387 "%s: got %.2f, expected %d, linegap %d\n", wine_dbgstr_w(nameW), metrics[0].height,
3388 fontmetrics.ascent + fontmetrics.descent + fontmetrics.lineGap, fontmetrics.lineGap);
3390 cleanup:
3391 if (layout)
3392 IDWriteTextLayout_Release(layout);
3393 if (format)
3394 IDWriteTextFormat_Release(format);
3395 IDWriteFontFace_Release(fontface);
3396 IDWriteFontFamily_Release(family);
3398 IDWriteFontCollection_Release(syscollection);
3400 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
3401 DWRITE_FONT_STRETCH_NORMAL, 2048.0f, enusW, &format);
3402 ok(hr == S_OK, "got 0x%08x\n", hr);
3404 fontface = get_fontface_from_format(format);
3405 ok(fontface != NULL, "got %p\n", fontface);
3407 /* force 2 lines */
3408 hr = IDWriteFactory_CreateTextLayout(factory, str2W, 5, format, 10000.0, 1000.0, &layout);
3409 ok(hr == S_OK, "got 0x%08x\n", hr);
3411 memset(metrics, 0, sizeof(metrics));
3412 count = 0;
3413 hr = IDWriteTextLayout_GetLineMetrics(layout, metrics, sizeof(metrics)/sizeof(*metrics), &count);
3414 ok(hr == S_OK, "got 0x%08x\n", hr);
3415 ok(count == 2, "got %u\n", count);
3416 /* baseline is relative to a line, and is not accumulated */
3417 ok(metrics[0].baseline == metrics[1].baseline, "got %.2f, %.2f\n", metrics[0].baseline,
3418 metrics[1].baseline);
3420 IDWriteTextLayout_Release(layout);
3421 IDWriteTextFormat_Release(format);
3423 /* line breaks */
3424 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
3425 DWRITE_FONT_STRETCH_NORMAL, 12.0, enusW, &format);
3426 ok(hr == S_OK, "got 0x%08x\n", hr);
3428 hr = IDWriteFactory_CreateTextLayout(factory, str3W, 10, format, 100.0, 300.0, &layout);
3429 ok(hr == S_OK, "got 0x%08x\n", hr);
3431 memset(metrics, 0xcc, sizeof(metrics));
3432 count = 0;
3433 hr = IDWriteTextLayout_GetLineMetrics(layout, metrics, sizeof(metrics)/sizeof(*metrics), &count);
3434 ok(hr == S_OK, "got 0x%08x\n", hr);
3435 ok(count == 6, "got %u\n", count);
3437 ok(metrics[0].length == 2, "got %u\n", metrics[0].length);
3438 ok(metrics[1].length == 2, "got %u\n", metrics[1].length);
3439 ok(metrics[2].length == 2, "got %u\n", metrics[2].length);
3440 ok(metrics[3].length == 1, "got %u\n", metrics[3].length);
3441 ok(metrics[4].length == 3, "got %u\n", metrics[4].length);
3442 ok(metrics[5].length == 0, "got %u\n", metrics[5].length);
3444 ok(metrics[0].newlineLength == 1, "got %u\n", metrics[0].newlineLength);
3445 ok(metrics[1].newlineLength == 1, "got %u\n", metrics[1].newlineLength);
3446 ok(metrics[2].newlineLength == 1, "got %u\n", metrics[2].newlineLength);
3447 ok(metrics[3].newlineLength == 1, "got %u\n", metrics[3].newlineLength);
3448 ok(metrics[4].newlineLength == 2, "got %u\n", metrics[4].newlineLength);
3449 ok(metrics[5].newlineLength == 0, "got %u\n", metrics[5].newlineLength);
3451 ok(metrics[0].trailingWhitespaceLength == 1, "got %u\n", metrics[0].newlineLength);
3452 ok(metrics[1].trailingWhitespaceLength == 1, "got %u\n", metrics[1].newlineLength);
3453 ok(metrics[2].trailingWhitespaceLength == 1, "got %u\n", metrics[2].newlineLength);
3454 ok(metrics[3].trailingWhitespaceLength == 1, "got %u\n", metrics[3].newlineLength);
3455 ok(metrics[4].trailingWhitespaceLength == 2, "got %u\n", metrics[4].newlineLength);
3456 ok(metrics[5].trailingWhitespaceLength == 0, "got %u\n", metrics[5].newlineLength);
3458 IDWriteTextLayout_Release(layout);
3460 /* empty text layout */
3461 hr = IDWriteFactory_CreateTextLayout(factory, strW, 0, format, 100.0f, 300.0f, &layout);
3462 ok(hr == S_OK, "got 0x%08x\n", hr);
3464 count = 0;
3465 memset(metrics, 0, sizeof(metrics));
3466 hr = IDWriteTextLayout_GetLineMetrics(layout, metrics, 1, &count);
3467 ok(hr == S_OK, "got 0x%08x\n", hr);
3468 ok(count == 1, "got %u\n", count);
3469 ok(metrics[0].length == 0, "got %u\n", metrics[0].length);
3470 ok(metrics[0].trailingWhitespaceLength == 0, "got %u\n", metrics[0].trailingWhitespaceLength);
3471 ok(metrics[0].newlineLength == 0, "got %u\n", metrics[0].newlineLength);
3472 ok(metrics[0].height > 0.0f, "got %f\n", metrics[0].height);
3473 ok(metrics[0].baseline > 0.0f, "got %f\n", metrics[0].baseline);
3474 ok(!metrics[0].isTrimmed, "got %d\n", metrics[0].isTrimmed);
3476 /* change font size at first position, see if metrics changed */
3477 range.startPosition = 0;
3478 range.length = 1;
3479 hr = IDWriteTextLayout_SetFontSize(layout, 80.0f, range);
3480 ok(hr == S_OK, "got 0x%08x\n", hr);
3482 count = 0;
3483 hr = IDWriteTextLayout_GetLineMetrics(layout, metrics + 1, 1, &count);
3484 ok(hr == S_OK, "got 0x%08x\n", hr);
3485 ok(count == 1, "got %u\n", count);
3486 ok(metrics[1].height > metrics[0].height, "got %f\n", metrics[1].height);
3487 ok(metrics[1].baseline > metrics[0].baseline, "got %f\n", metrics[1].baseline);
3489 /* revert font size back to format value, set different size for position 1 */
3490 hr = IDWriteTextLayout_SetFontSize(layout, 12.0f, range);
3491 ok(hr == S_OK, "got 0x%08x\n", hr);
3493 range.startPosition = 1;
3494 range.length = 1;
3495 hr = IDWriteTextLayout_SetFontSize(layout, 80.0f, range);
3496 ok(hr == S_OK, "got 0x%08x\n", hr);
3498 memset(metrics + 1, 0, sizeof(*metrics));
3499 count = 0;
3500 hr = IDWriteTextLayout_GetLineMetrics(layout, metrics + 1, 1, &count);
3501 ok(hr == S_OK, "got 0x%08x\n", hr);
3502 ok(count == 1, "got %u\n", count);
3503 ok(metrics[1].height == metrics[0].height, "got %f\n", metrics[1].height);
3504 ok(metrics[1].baseline == metrics[0].baseline, "got %f\n", metrics[1].baseline);
3506 IDWriteTextLayout_Release(layout);
3508 /* text is "a\r" */
3509 hr = IDWriteFactory_CreateTextLayout(factory, str4W, 2, format, 100.0f, 300.0f, &layout);
3510 ok(hr == S_OK, "got 0x%08x\n", hr);
3512 count = 0;
3513 memset(metrics, 0, sizeof(metrics));
3514 hr = IDWriteTextLayout_GetLineMetrics(layout, metrics, sizeof(metrics)/sizeof(*metrics), &count);
3515 ok(hr == S_OK, "got 0x%08x\n", hr);
3516 ok(count == 2, "got %u\n", count);
3517 ok(metrics[0].length == 2, "got %u\n", metrics[0].length);
3518 ok(metrics[0].newlineLength == 1, "got %u\n", metrics[0].newlineLength);
3519 ok(metrics[0].height > 0.0f, "got %f\n", metrics[0].height);
3520 ok(metrics[0].baseline > 0.0f, "got %f\n", metrics[0].baseline);
3521 ok(metrics[1].length == 0, "got %u\n", metrics[1].length);
3522 ok(metrics[1].newlineLength == 0, "got %u\n", metrics[1].newlineLength);
3523 ok(metrics[1].height > 0.0f, "got %f\n", metrics[1].height);
3524 ok(metrics[1].baseline > 0.0f, "got %f\n", metrics[1].baseline);
3526 range.startPosition = 1;
3527 range.length = 1;
3528 hr = IDWriteTextLayout_SetFontSize(layout, 80.0f, range);
3529 ok(hr == S_OK, "got 0x%08x\n", hr);
3531 hr = IDWriteTextLayout_GetLineMetrics(layout, metrics + 2, 2, &count);
3532 ok(hr == S_OK, "got 0x%08x\n", hr);
3533 ok(count == 2, "got %u\n", count);
3534 ok(metrics[3].height > metrics[1].height, "got %f, old %f\n", metrics[3].height, metrics[1].height);
3535 ok(metrics[3].baseline > metrics[1].baseline, "got %f, old %f\n", metrics[3].baseline, metrics[1].baseline);
3537 /* revert to original format */
3538 hr = IDWriteTextLayout_SetFontSize(layout, 12.0f, range);
3539 ok(hr == S_OK, "got 0x%08x\n", hr);
3540 hr = IDWriteTextLayout_GetLineMetrics(layout, metrics + 2, 2, &count);
3541 ok(hr == S_OK, "got 0x%08x\n", hr);
3542 ok(count == 2, "got %u\n", count);
3543 ok(metrics[3].height == metrics[1].height, "got %f, old %f\n", metrics[3].height, metrics[1].height);
3544 ok(metrics[3].baseline == metrics[1].baseline, "got %f, old %f\n", metrics[3].baseline, metrics[1].baseline);
3546 IDWriteTextLayout_Release(layout);
3548 IDWriteTextFormat_Release(format);
3549 IDWriteFontFace_Release(fontface);
3550 IDWriteFactory_Release(factory);
3553 static void test_SetTextAlignment(void)
3555 static const WCHAR strW[] = {'a',0};
3557 static const WCHAR stringsW[][10] = {
3558 {'a',0},
3562 DWRITE_CLUSTER_METRICS clusters[10];
3563 DWRITE_TEXT_METRICS metrics;
3564 IDWriteTextFormat1 *format1;
3565 IDWriteTextFormat *format;
3566 IDWriteTextLayout *layout;
3567 IDWriteFactory *factory;
3568 DWRITE_TEXT_ALIGNMENT v;
3569 UINT32 count, i;
3570 HRESULT hr;
3572 factory = create_factory();
3574 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
3575 DWRITE_FONT_STRETCH_NORMAL, 12.0, enusW, &format);
3576 ok(hr == S_OK, "got 0x%08x\n", hr);
3578 v = IDWriteTextFormat_GetTextAlignment(format);
3579 ok(v == DWRITE_TEXT_ALIGNMENT_LEADING, "got %d\n", v);
3581 hr = IDWriteFactory_CreateTextLayout(factory, strW, 1, format, 500.0, 100.0, &layout);
3582 ok(hr == S_OK, "got 0x%08x\n", hr);
3584 v = IDWriteTextLayout_GetTextAlignment(layout);
3585 ok(v == DWRITE_TEXT_ALIGNMENT_LEADING, "got %d\n", v);
3587 hr = IDWriteTextLayout_SetTextAlignment(layout, DWRITE_TEXT_ALIGNMENT_TRAILING);
3588 ok(hr == S_OK, "got 0x%08x\n", hr);
3590 hr = IDWriteTextLayout_SetTextAlignment(layout, DWRITE_TEXT_ALIGNMENT_TRAILING);
3591 ok(hr == S_OK, "got 0x%08x\n", hr);
3593 v = IDWriteTextFormat_GetTextAlignment(format);
3594 ok(v == DWRITE_TEXT_ALIGNMENT_LEADING, "got %d\n", v);
3596 v = IDWriteTextLayout_GetTextAlignment(layout);
3597 ok(v == DWRITE_TEXT_ALIGNMENT_TRAILING, "got %d\n", v);
3599 hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextFormat1, (void**)&format1);
3600 if (hr == S_OK) {
3601 hr = IDWriteTextFormat1_SetTextAlignment(format1, DWRITE_TEXT_ALIGNMENT_CENTER);
3602 ok(hr == S_OK, "got 0x%08x\n", hr);
3604 v = IDWriteTextFormat_GetTextAlignment(format);
3605 ok(v == DWRITE_TEXT_ALIGNMENT_LEADING, "got %d\n", v);
3607 v = IDWriteTextLayout_GetTextAlignment(layout);
3608 ok(v == DWRITE_TEXT_ALIGNMENT_CENTER, "got %d\n", v);
3610 v = IDWriteTextFormat1_GetTextAlignment(format1);
3611 ok(v == DWRITE_TEXT_ALIGNMENT_CENTER, "got %d\n", v);
3613 IDWriteTextFormat1_Release(format1);
3615 else
3616 win_skip("IDWriteTextFormat1 is not supported\n");
3618 for (i = 0; i < sizeof(stringsW)/sizeof(stringsW[0]); i++) {
3619 FLOAT text_width;
3621 hr = IDWriteTextFormat_SetTextAlignment(format, DWRITE_TEXT_ALIGNMENT_LEADING);
3622 ok(hr == S_OK, "got 0x%08x\n", hr);
3624 hr = IDWriteFactory_CreateTextLayout(factory, stringsW[i], lstrlenW(stringsW[i]), format, 500.0f, 100.0f, &layout);
3625 ok(hr == S_OK, "got 0x%08x\n", hr);
3627 hr = IDWriteTextLayout_SetWordWrapping(layout, DWRITE_WORD_WRAPPING_NO_WRAP);
3628 ok(hr == S_OK, "got 0x%08x\n", hr);
3630 count = 0;
3631 hr = IDWriteTextLayout_GetClusterMetrics(layout, clusters, sizeof(clusters)/sizeof(*clusters), &count);
3632 ok(hr == S_OK, "got 0x%08x\n", hr);
3633 if (lstrlenW(stringsW[i]))
3634 ok(count > 0, "got %u\n", count);
3635 else
3636 ok(count == 0, "got %u\n", count);
3638 text_width = 0.0f;
3639 while (count)
3640 text_width += clusters[--count].width;
3642 /* maxwidth is 500, leading alignment */
3643 hr = IDWriteTextLayout_SetTextAlignment(layout, DWRITE_TEXT_ALIGNMENT_LEADING);
3644 ok(hr == S_OK, "got 0x%08x\n", hr);
3646 hr = IDWriteTextLayout_GetMetrics(layout, &metrics);
3647 ok(hr == S_OK, "got 0x%08x\n", hr);
3649 ok(metrics.left == 0.0f, "got %.2f\n", metrics.left);
3650 ok(metrics.width == text_width, "got %.2f\n", metrics.width);
3651 ok(metrics.layoutWidth == 500.0f, "got %.2f\n", metrics.layoutWidth);
3652 ok(metrics.lineCount == 1, "got %d\n", metrics.lineCount);
3654 /* maxwidth is 500, trailing alignment */
3655 hr = IDWriteTextLayout_SetTextAlignment(layout, DWRITE_TEXT_ALIGNMENT_TRAILING);
3656 ok(hr == S_OK, "got 0x%08x\n", hr);
3658 hr = IDWriteTextLayout_GetMetrics(layout, &metrics);
3659 ok(hr == S_OK, "got 0x%08x\n", hr);
3661 ok(metrics.left == metrics.layoutWidth - metrics.width, "got %.2f\n", metrics.left);
3662 ok(metrics.width == text_width, "got %.2f\n", metrics.width);
3663 ok(metrics.layoutWidth == 500.0f, "got %.2f\n", metrics.layoutWidth);
3664 ok(metrics.lineCount == 1, "got %d\n", metrics.lineCount);
3665 IDWriteTextLayout_Release(layout);
3667 /* initially created with trailing alignment */
3668 hr = IDWriteTextFormat_SetTextAlignment(format, DWRITE_TEXT_ALIGNMENT_TRAILING);
3669 ok(hr == S_OK, "got 0x%08x\n", hr);
3671 hr = IDWriteFactory_CreateTextLayout(factory, stringsW[i], lstrlenW(stringsW[i]), format, 500.0f, 100.0f, &layout);
3672 ok(hr == S_OK, "got 0x%08x\n", hr);
3674 hr = IDWriteTextLayout_GetMetrics(layout, &metrics);
3675 ok(hr == S_OK, "got 0x%08x\n", hr);
3677 ok(metrics.left == metrics.layoutWidth - metrics.width, "got %.2f\n", metrics.left);
3678 ok(metrics.width == text_width, "got %.2f\n", metrics.width);
3679 ok(metrics.layoutWidth == 500.0f, "got %.2f\n", metrics.layoutWidth);
3680 ok(metrics.lineCount == 1, "got %d\n", metrics.lineCount);
3681 IDWriteTextLayout_Release(layout);
3683 if (lstrlenW(stringsW[i]) > 0) {
3684 /* max width less than total run width, trailing alignment */
3685 hr = IDWriteTextFormat_SetWordWrapping(format, DWRITE_WORD_WRAPPING_NO_WRAP);
3686 ok(hr == S_OK, "got 0x%08x\n", hr);
3688 hr = IDWriteFactory_CreateTextLayout(factory, stringsW[i], lstrlenW(stringsW[i]), format, clusters[0].width, 100.0f, &layout);
3689 ok(hr == S_OK, "got 0x%08x\n", hr);
3690 hr = IDWriteTextLayout_GetMetrics(layout, &metrics);
3691 ok(hr == S_OK, "got 0x%08x\n", hr);
3692 ok(metrics.left == metrics.layoutWidth - metrics.width, "got %.2f\n", metrics.left);
3693 ok(metrics.width == text_width, "got %.2f\n", metrics.width);
3694 ok(metrics.lineCount == 1, "got %d\n", metrics.lineCount);
3695 IDWriteTextLayout_Release(layout);
3698 /* maxwidth is 500, centered */
3699 hr = IDWriteTextFormat_SetTextAlignment(format, DWRITE_TEXT_ALIGNMENT_CENTER);
3700 ok(hr == S_OK, "got 0x%08x\n", hr);
3702 hr = IDWriteFactory_CreateTextLayout(factory, stringsW[i], lstrlenW(stringsW[i]), format, 500.0f, 100.0f, &layout);
3703 ok(hr == S_OK, "got 0x%08x\n", hr);
3705 hr = IDWriteTextLayout_GetMetrics(layout, &metrics);
3706 ok(hr == S_OK, "got 0x%08x\n", hr);
3707 ok(metrics.left == (metrics.layoutWidth - metrics.width) / 2.0f, "got %.2f\n", metrics.left);
3708 ok(metrics.width == text_width, "got %.2f\n", metrics.width);
3709 ok(metrics.lineCount == 1, "got %d\n", metrics.lineCount);
3711 IDWriteTextLayout_Release(layout);
3714 IDWriteTextFormat_Release(format);
3715 IDWriteFactory_Release(factory);
3718 static void test_SetParagraphAlignment(void)
3720 static const WCHAR strW[] = {'a',0};
3721 DWRITE_TEXT_METRICS metrics;
3722 IDWriteTextFormat *format;
3723 IDWriteTextLayout *layout;
3724 IDWriteFactory *factory;
3725 DWRITE_PARAGRAPH_ALIGNMENT v;
3726 DWRITE_LINE_METRICS lines[1];
3727 UINT32 count;
3728 HRESULT hr;
3730 factory = create_factory();
3732 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
3733 DWRITE_FONT_STRETCH_NORMAL, 12.0, enusW, &format);
3734 ok(hr == S_OK, "got 0x%08x\n", hr);
3736 v = IDWriteTextFormat_GetParagraphAlignment(format);
3737 ok(v == DWRITE_PARAGRAPH_ALIGNMENT_NEAR, "got %d\n", v);
3739 hr = IDWriteFactory_CreateTextLayout(factory, strW, 1, format, 500.0, 100.0, &layout);
3740 ok(hr == S_OK, "got 0x%08x\n", hr);
3742 v = IDWriteTextLayout_GetParagraphAlignment(layout);
3743 ok(v == DWRITE_PARAGRAPH_ALIGNMENT_NEAR, "got %d\n", v);
3745 hr = IDWriteTextLayout_SetParagraphAlignment(layout, DWRITE_PARAGRAPH_ALIGNMENT_FAR);
3746 ok(hr == S_OK, "got 0x%08x\n", hr);
3748 hr = IDWriteTextLayout_SetParagraphAlignment(layout, DWRITE_PARAGRAPH_ALIGNMENT_FAR);
3749 ok(hr == S_OK, "got 0x%08x\n", hr);
3751 v = IDWriteTextFormat_GetParagraphAlignment(format);
3752 ok(v == DWRITE_PARAGRAPH_ALIGNMENT_NEAR, "got %d\n", v);
3754 v = IDWriteTextLayout_GetParagraphAlignment(layout);
3755 ok(v == DWRITE_PARAGRAPH_ALIGNMENT_FAR, "got %d\n", v);
3757 hr = IDWriteTextLayout_SetParagraphAlignment(layout, DWRITE_PARAGRAPH_ALIGNMENT_CENTER);
3758 ok(hr == S_OK, "got 0x%08x\n", hr);
3760 v = IDWriteTextLayout_GetParagraphAlignment(layout);
3761 ok(v == DWRITE_PARAGRAPH_ALIGNMENT_CENTER, "got %d\n", v);
3763 count = 0;
3764 hr = IDWriteTextLayout_GetLineMetrics(layout, lines, 1, &count);
3765 ok(hr == S_OK, "got 0x%08x\n", hr);
3766 ok(count == 1, "got %u\n", count);
3768 /* maxheight is 100, near alignment */
3769 hr = IDWriteTextLayout_SetParagraphAlignment(layout, DWRITE_PARAGRAPH_ALIGNMENT_NEAR);
3770 ok(hr == S_OK, "got 0x%08x\n", hr);
3772 hr = IDWriteTextLayout_GetMetrics(layout, &metrics);
3773 ok(hr == S_OK, "got 0x%08x\n", hr);
3775 ok(metrics.top == 0.0, "got %.2f\n", metrics.top);
3776 ok(metrics.height == lines[0].height, "got %.2f\n", metrics.height);
3777 ok(metrics.layoutHeight == 100.0, "got %.2f\n", metrics.layoutHeight);
3778 ok(metrics.lineCount == 1, "got %d\n", metrics.lineCount);
3780 /* maxwidth is 100, far alignment */
3781 hr = IDWriteTextLayout_SetParagraphAlignment(layout, DWRITE_PARAGRAPH_ALIGNMENT_FAR);
3782 ok(hr == S_OK, "got 0x%08x\n", hr);
3784 hr = IDWriteTextLayout_GetMetrics(layout, &metrics);
3785 ok(hr == S_OK, "got 0x%08x\n", hr);
3787 ok(metrics.top == metrics.layoutHeight - metrics.height, "got %.2f\n", metrics.top);
3788 ok(metrics.height == lines[0].height, "got %.2f\n", metrics.height);
3789 ok(metrics.layoutHeight == 100.0, "got %.2f\n", metrics.layoutHeight);
3790 ok(metrics.lineCount == 1, "got %d\n", metrics.lineCount);
3791 IDWriteTextLayout_Release(layout);
3793 /* initially created with centered alignment */
3794 hr = IDWriteTextFormat_SetParagraphAlignment(format, DWRITE_PARAGRAPH_ALIGNMENT_CENTER);
3795 ok(hr == S_OK, "got 0x%08x\n", hr);
3797 hr = IDWriteFactory_CreateTextLayout(factory, strW, 1, format, 500.0, 100.0, &layout);
3798 ok(hr == S_OK, "got 0x%08x\n", hr);
3800 hr = IDWriteTextLayout_GetMetrics(layout, &metrics);
3801 ok(hr == S_OK, "got 0x%08x\n", hr);
3803 ok(metrics.top == (metrics.layoutHeight - lines[0].height) / 2, "got %.2f\n", metrics.top);
3804 ok(metrics.height == lines[0].height, "got %.2f\n", metrics.height);
3805 ok(metrics.lineCount == 1, "got %d\n", metrics.lineCount);
3806 IDWriteTextLayout_Release(layout);
3808 IDWriteTextFormat_Release(format);
3809 IDWriteFactory_Release(factory);
3812 static void test_SetReadingDirection(void)
3814 static const WCHAR strW[] = {'a',0};
3815 DWRITE_CLUSTER_METRICS clusters[1];
3816 DWRITE_TEXT_METRICS metrics;
3817 IDWriteTextFormat *format;
3818 IDWriteTextLayout *layout;
3819 IDWriteFactory *factory;
3820 DWRITE_READING_DIRECTION v;
3821 DWRITE_LINE_METRICS lines[1];
3822 UINT32 count;
3823 HRESULT hr;
3825 factory = create_factory();
3827 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
3828 DWRITE_FONT_STRETCH_NORMAL, 12.0, enusW, &format);
3829 ok(hr == S_OK, "got 0x%08x\n", hr);
3831 v = IDWriteTextFormat_GetReadingDirection(format);
3832 ok(v == DWRITE_READING_DIRECTION_LEFT_TO_RIGHT, "got %d\n", v);
3834 hr = IDWriteFactory_CreateTextLayout(factory, strW, 1, format, 500.0, 100.0, &layout);
3835 ok(hr == S_OK, "got 0x%08x\n", hr);
3837 v = IDWriteTextLayout_GetReadingDirection(layout);
3838 ok(v == DWRITE_READING_DIRECTION_LEFT_TO_RIGHT, "got %d\n", v);
3840 v = IDWriteTextFormat_GetReadingDirection(format);
3841 ok(v == DWRITE_READING_DIRECTION_LEFT_TO_RIGHT, "got %d\n", v);
3843 hr = IDWriteTextLayout_SetReadingDirection(layout, DWRITE_READING_DIRECTION_RIGHT_TO_LEFT);
3844 ok(hr == S_OK, "got 0x%08x\n", hr);
3846 count = 0;
3847 hr = IDWriteTextLayout_GetLineMetrics(layout, lines, 1, &count);
3848 ok(hr == S_OK, "got 0x%08x\n", hr);
3849 ok(count == 1, "got %u\n", count);
3851 count = 0;
3852 hr = IDWriteTextLayout_GetClusterMetrics(layout, clusters, 1, &count);
3853 ok(hr == S_OK, "got 0x%08x\n", hr);
3854 ok(count == 1, "got %u\n", count);
3856 /* leading alignment, RTL */
3857 hr = IDWriteTextLayout_GetMetrics(layout, &metrics);
3858 ok(hr == S_OK, "got 0x%08x\n", hr);
3860 ok(metrics.left == metrics.layoutWidth - clusters[0].width, "got %.2f\n", metrics.left);
3861 ok(metrics.top == 0.0, "got %.2f\n", metrics.top);
3862 ok(metrics.width == clusters[0].width, "got %.2f\n", metrics.width);
3863 ok(metrics.height == lines[0].height, "got %.2f\n", metrics.height);
3864 ok(metrics.layoutWidth == 500.0, "got %.2f\n", metrics.layoutWidth);
3865 ok(metrics.layoutHeight == 100.0, "got %.2f\n", metrics.layoutHeight);
3866 ok(metrics.lineCount == 1, "got %d\n", metrics.lineCount);
3868 /* trailing alignment, RTL */
3869 hr = IDWriteTextLayout_SetTextAlignment(layout, DWRITE_TEXT_ALIGNMENT_TRAILING);
3870 ok(hr == S_OK, "got 0x%08x\n", hr);
3872 hr = IDWriteTextLayout_GetMetrics(layout, &metrics);
3873 ok(hr == S_OK, "got 0x%08x\n", hr);
3875 ok(metrics.left == 0.0, "got %.2f\n", metrics.left);
3876 ok(metrics.top == 0.0, "got %.2f\n", metrics.top);
3877 ok(metrics.width == clusters[0].width, "got %.2f\n", metrics.width);
3878 ok(metrics.height == lines[0].height, "got %.2f\n", metrics.height);
3879 ok(metrics.layoutWidth == 500.0, "got %.2f\n", metrics.layoutWidth);
3880 ok(metrics.layoutHeight == 100.0, "got %.2f\n", metrics.layoutHeight);
3881 ok(metrics.lineCount == 1, "got %d\n", metrics.lineCount);
3883 /* centered alignment, RTL */
3884 hr = IDWriteTextLayout_SetTextAlignment(layout, DWRITE_TEXT_ALIGNMENT_CENTER);
3885 ok(hr == S_OK, "got 0x%08x\n", hr);
3887 hr = IDWriteTextLayout_GetMetrics(layout, &metrics);
3888 ok(hr == S_OK, "got 0x%08x\n", hr);
3890 ok(metrics.left == (metrics.layoutWidth - clusters[0].width) / 2.0, "got %.2f\n", metrics.left);
3891 ok(metrics.top == 0.0, "got %.2f\n", metrics.top);
3892 ok(metrics.width == clusters[0].width, "got %.2f\n", metrics.width);
3893 ok(metrics.height == lines[0].height, "got %.2f\n", metrics.height);
3894 ok(metrics.layoutWidth == 500.0, "got %.2f\n", metrics.layoutWidth);
3895 ok(metrics.layoutHeight == 100.0, "got %.2f\n", metrics.layoutHeight);
3896 ok(metrics.lineCount == 1, "got %d\n", metrics.lineCount);
3898 IDWriteTextLayout_Release(layout);
3900 IDWriteTextFormat_Release(format);
3901 IDWriteFactory_Release(factory);
3904 static inline FLOAT get_scaled_font_metric(UINT32 metric, FLOAT emSize, const DWRITE_FONT_METRICS *metrics)
3906 return (FLOAT)metric * emSize / (FLOAT)metrics->designUnitsPerEm;
3909 static FLOAT snap_coord(const DWRITE_MATRIX *m, FLOAT ppdip, FLOAT coord)
3911 FLOAT vec[2], det, vec2[2];
3912 BOOL transform;
3914 /* has to be a diagonal matrix */
3915 if ((ppdip <= 0.0) ||
3916 (m->m11 * m->m22 != 0.0 && (m->m12 != 0.0 || m->m21 != 0.0)) ||
3917 (m->m12 * m->m21 != 0.0 && (m->m11 != 0.0 || m->m22 != 0.0)))
3918 return coord;
3920 det = m->m11 * m->m22 - m->m12 * m->m21;
3921 transform = fabsf(det) > 1e-10;
3923 if (transform) {
3924 /* apply transform */
3925 vec[0] = 0.0;
3926 vec[1] = coord * ppdip;
3928 vec2[0] = m->m11 * vec[0] + m->m21 * vec[1] + m->dx;
3929 vec2[1] = m->m12 * vec[0] + m->m22 * vec[1] + m->dy;
3931 /* snap */
3932 vec2[0] = floorf(vec2[0] + 0.5f);
3933 vec2[1] = floorf(vec2[1] + 0.5f);
3935 /* apply inverted transform */
3936 vec[1] = (-m->m12 * vec2[0] + m->m11 * vec2[1] - (m->m11 * m->dy - m->m12 * m->dx)) / det;
3937 vec[1] /= ppdip;
3939 else
3940 vec[1] = floorf(coord * ppdip + 0.5f) / ppdip;
3941 return vec[1];
3944 static inline BOOL float_eq(FLOAT left, FLOAT right)
3946 int x = *(int *)&left;
3947 int y = *(int *)&right;
3949 if (x < 0)
3950 x = INT_MIN - x;
3951 if (y < 0)
3952 y = INT_MIN - y;
3954 return abs(x - y) <= 16;
3957 struct snapping_test {
3958 DWRITE_MATRIX m;
3959 FLOAT ppdip;
3962 static struct snapping_test snapping_tests[] = {
3963 { { 0.0, 1.0, 2.0, 0.0, 0.2, 0.3 }, 1.0 },
3964 { { 0.0, 1.0, 2.0, 0.0, 0.0, 0.0 }, 1.0 },
3965 { { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }, 1.0 }, /* identity transform */
3966 { { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }, 0.9 },
3967 { { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }, -1.0 },
3968 { { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }, 0.0 },
3969 { { 1.0, 0.0, 0.0, 1.0, 0.0, 0.3 }, 1.0 }, /* simple Y shift */
3970 { { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }, 10.0 }, /* identity, 10 ppdip */
3971 { { 1.0, 0.0, 0.0, 10.0, 0.0, 0.0 }, 10.0 },
3972 { { 0.0, 1.0, 1.0, 0.0, 0.2, 0.6 }, 1.0 },
3973 { { 0.0, 2.0, 2.0, 0.0, 0.2, 0.6 }, 1.0 },
3974 { { 0.0, 0.5, -0.5, 0.0, 0.2, 0.6 }, 1.0 },
3975 { { 1.0, 2.0, 0.0, 1.0, 0.2, 0.6 }, 1.0 },
3976 { { 1.0, 1.0, 0.0, 1.0, 0.2, 0.6 }, 1.0 },
3977 { { 0.5, 0.5, -0.5, 0.5, 0.2, 0.6 }, 1.0 }, /* 45 degrees rotation */
3978 { { 0.5, 0.5, -0.5, 0.5, 0.0, 0.0 }, 100.0 }, /* 45 degrees rotation */
3979 { { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }, 100.0 },
3980 { { 0.0, 1.0, -1.0, 0.0, 0.2, 0.6 }, 1.0 }, /* 90 degrees rotation */
3981 { { -1.0, 0.0, 0.0, -1.0, 0.2, 0.6 }, 1.0 }, /* 180 degrees rotation */
3982 { { 0.0, -1.0, 1.0, 0.0, 0.2, 0.6 }, 1.0 }, /* 270 degrees rotation */
3983 { { 1.0, 0.0, 0.0, 1.0,-0.1, 0.2 }, 1.0 },
3984 { { 0.0, 1.0, -1.0, 0.0,-0.2,-0.3 }, 1.0 }, /* 90 degrees rotation */
3985 { { -1.0, 0.0, 0.0, -1.0,-0.3,-1.6 }, 1.0 }, /* 180 degrees rotation */
3986 { { 0.0, -1.0, 1.0, 0.0,-0.7, 0.6 }, 10.0 }, /* 270 degrees rotation */
3987 { { 0.0, 2.0, 1.0, 0.0, 0.2, 0.6 }, 1.0 },
3988 { { 0.0, 0.0, 1.0, 0.0, 0.0, 0.0 }, 1.0 },
3989 { { 3.0, 0.0, 0.0, 5.0, 0.2,-0.3 }, 10.0 },
3990 { { 0.0, -3.0, 5.0, 0.0,-0.1, 0.7 }, 10.0 },
3993 static DWRITE_MATRIX compattransforms[] = {
3994 { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 },
3995 { 1.0, 0.0, 0.0, 1.0, 0.2, 0.3 },
3996 { 2.0, 0.0, 0.0, 2.0, 0.2, 0.3 },
3997 { 2.0, 1.0, 2.0, 2.0, 0.2, 0.3 },
4000 static void test_pixelsnapping(void)
4002 static const WCHAR strW[] = {'a',0};
4003 IDWriteTextLayout *layout, *layout2;
4004 struct renderer_context ctxt;
4005 DWRITE_FONT_METRICS metrics;
4006 IDWriteTextFormat *format;
4007 IDWriteFontFace *fontface;
4008 IDWriteFactory *factory;
4009 FLOAT baseline, originX;
4010 HRESULT hr;
4011 int i, j;
4013 factory = create_factory();
4015 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
4016 DWRITE_FONT_STRETCH_NORMAL, 12.0, enusW, &format);
4017 ok(hr == S_OK, "got 0x%08x\n", hr);
4019 fontface = get_fontface_from_format(format);
4020 IDWriteFontFace_GetMetrics(fontface, &metrics);
4022 hr = IDWriteFactory_CreateTextLayout(factory, strW, 1, format, 500.0, 100.0, &layout);
4023 ok(hr == S_OK, "got 0x%08x\n", hr);
4025 /* disabled snapping */
4026 ctxt.snapping_disabled = TRUE;
4027 ctxt.gdicompat = FALSE;
4028 ctxt.use_gdi_natural = FALSE;
4029 ctxt.ppdip = 1.0f;
4030 memset(&ctxt.m, 0, sizeof(ctxt.m));
4031 ctxt.m.m11 = ctxt.m.m22 = 1.0;
4032 originX = 0.1;
4034 hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, originX, 0.0);
4035 ok(hr == S_OK, "got 0x%08x\n", hr);
4037 baseline = get_scaled_font_metric(metrics.ascent, 12.0, &metrics);
4038 ok(ctxt.originX == originX, "got %f, originX %f\n", ctxt.originX, originX);
4039 ok(ctxt.originY == baseline, "got %f, baseline %f\n", ctxt.originY, baseline);
4040 ok(floor(baseline) != baseline, "got %f\n", baseline);
4042 ctxt.snapping_disabled = FALSE;
4044 for (i = 0; i < sizeof(snapping_tests)/sizeof(snapping_tests[0]); i++) {
4045 struct snapping_test *ptr = &snapping_tests[i];
4046 FLOAT expectedY;
4048 ctxt.m = ptr->m;
4049 ctxt.ppdip = ptr->ppdip;
4050 ctxt.originX = 678.9;
4051 ctxt.originY = 678.9;
4053 expectedY = snap_coord(&ctxt.m, ctxt.ppdip, baseline);
4054 hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, originX, 0.0);
4055 ok(hr == S_OK, "%d: got 0x%08x\n", i, hr);
4056 ok(ctxt.originX == originX, "%d: got %f, originX %f\n", i, ctxt.originX, originX);
4057 ok(float_eq(ctxt.originY, expectedY), "%d: got %f, expected %f, baseline %f\n",
4058 i, ctxt.originY, expectedY, baseline);
4060 /* gdicompat layout transform doesn't affect snapping */
4061 for (j = 0; j < sizeof(compattransforms)/sizeof(compattransforms[0]); j++) {
4062 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, strW, 1, format, 500.0, 100.0,
4063 1.0, &compattransforms[j], FALSE, &layout2);
4064 ok(hr == S_OK, "%d: got 0x%08x\n", i, hr);
4066 expectedY = snap_coord(&ctxt.m, ctxt.ppdip, baseline);
4067 hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, originX, 0.0);
4068 ok(hr == S_OK, "%d: got 0x%08x\n", i, hr);
4069 ok(ctxt.originX == originX, "%d: got %f, originX %f\n", i, ctxt.originX, originX);
4070 ok(float_eq(ctxt.originY, expectedY), "%d: got %f, expected %f, baseline %f\n",
4071 i, ctxt.originY, expectedY, baseline);
4073 IDWriteTextLayout_Release(layout2);
4077 IDWriteTextLayout_Release(layout);
4078 IDWriteTextFormat_Release(format);
4079 IDWriteFontFace_Release(fontface);
4080 IDWriteFactory_Release(factory);
4083 static void test_SetWordWrapping(void)
4085 static const WCHAR strW[] = {'a',' ','s','o','m','e',' ','t','e','x','t',' ','a','n','d',
4086 ' ','a',' ','b','i','t',' ','m','o','r','e','\n','b'};
4087 IDWriteTextFormat *format;
4088 IDWriteTextLayout *layout;
4089 IDWriteFactory *factory;
4090 DWRITE_WORD_WRAPPING v;
4091 UINT32 count;
4092 HRESULT hr;
4094 factory = create_factory();
4096 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
4097 DWRITE_FONT_STRETCH_NORMAL, 12.0, enusW, &format);
4098 ok(hr == S_OK, "got 0x%08x\n", hr);
4100 v = IDWriteTextFormat_GetWordWrapping(format);
4101 ok(v == DWRITE_WORD_WRAPPING_WRAP, "got %d\n", v);
4103 hr = IDWriteFactory_CreateTextLayout(factory, strW, sizeof(strW)/sizeof(WCHAR), format, 10.0f, 100.0f, &layout);
4104 ok(hr == S_OK, "got 0x%08x\n", hr);
4106 v = IDWriteTextLayout_GetWordWrapping(layout);
4107 ok(v == DWRITE_WORD_WRAPPING_WRAP, "got %d\n", v);
4109 hr = IDWriteTextLayout_SetWordWrapping(layout, DWRITE_WORD_WRAPPING_NO_WRAP);
4110 ok(hr == S_OK, "got 0x%08x\n", hr);
4112 hr = IDWriteTextLayout_SetWordWrapping(layout, DWRITE_WORD_WRAPPING_NO_WRAP);
4113 ok(hr == S_OK, "got 0x%08x\n", hr);
4115 v = IDWriteTextFormat_GetWordWrapping(format);
4116 ok(v == DWRITE_WORD_WRAPPING_WRAP, "got %d\n", v);
4118 /* disable wrapping, text has explicit newline */
4119 hr = IDWriteTextLayout_SetWordWrapping(layout, DWRITE_WORD_WRAPPING_NO_WRAP);
4120 ok(hr == S_OK, "got 0x%08x\n", hr);
4122 count = 0;
4123 hr = IDWriteTextLayout_GetLineMetrics(layout, NULL, 0, &count);
4124 ok(hr == E_NOT_SUFFICIENT_BUFFER, "got 0x%08x\n", hr);
4125 ok(count == 2, "got %u\n", count);
4127 hr = IDWriteTextLayout_SetWordWrapping(layout, DWRITE_WORD_WRAPPING_WRAP);
4128 ok(hr == S_OK, "got 0x%08x\n", hr);
4130 count = 0;
4131 hr = IDWriteTextLayout_GetLineMetrics(layout, NULL, 0, &count);
4132 ok(hr == E_NOT_SUFFICIENT_BUFFER, "got 0x%08x\n", hr);
4133 ok(count > 2, "got %u\n", count);
4135 IDWriteTextLayout_Release(layout);
4136 IDWriteTextFormat_Release(format);
4137 IDWriteFactory_Release(factory);
4140 /* Collection dedicated to fallback testing */
4142 static const WCHAR g_blahfontW[] = {'B','l','a','h',0};
4143 static HRESULT WINAPI fontcollection_QI(IDWriteFontCollection *iface, REFIID riid, void **obj)
4145 if (IsEqualIID(riid, &IID_IDWriteFontCollection) || IsEqualIID(riid, &IID_IUnknown)) {
4146 *obj = iface;
4147 IDWriteFontCollection_AddRef(iface);
4148 return S_OK;
4151 *obj = NULL;
4152 return E_NOINTERFACE;
4155 static ULONG WINAPI fontcollection_AddRef(IDWriteFontCollection *iface)
4157 return 2;
4160 static ULONG WINAPI fontcollection_Release(IDWriteFontCollection *iface)
4162 return 1;
4165 static UINT32 WINAPI fontcollection_GetFontFamilyCount(IDWriteFontCollection *iface)
4167 ok(0, "unexpected call\n");
4168 return 0;
4171 static HRESULT WINAPI fontcollection_GetFontFamily(IDWriteFontCollection *iface, UINT32 index, IDWriteFontFamily **family)
4173 if (index == 123456) {
4174 IDWriteFactory *factory = create_factory();
4175 IDWriteFontCollection *syscollection;
4176 BOOL exists;
4177 HRESULT hr;
4179 hr = IDWriteFactory_GetSystemFontCollection(factory, &syscollection, FALSE);
4180 ok(hr == S_OK, "got 0x%08x\n", hr);
4182 hr = IDWriteFontCollection_FindFamilyName(syscollection, tahomaW, &index, &exists);
4183 ok(hr == S_OK, "got 0x%08x\n", hr);
4185 hr = IDWriteFontCollection_GetFontFamily(syscollection, index, family);
4186 ok(hr == S_OK, "got 0x%08x\n", hr);
4188 IDWriteFontCollection_Release(syscollection);
4189 IDWriteFactory_Release(factory);
4190 return S_OK;
4192 ok(0, "unexpected call\n");
4193 return E_NOTIMPL;
4196 static HRESULT WINAPI fontcollection_FindFamilyName(IDWriteFontCollection *iface, WCHAR const *name, UINT32 *index, BOOL *exists)
4198 if (!lstrcmpW(name, g_blahfontW)) {
4199 *index = 123456;
4200 *exists = TRUE;
4201 return S_OK;
4203 ok(0, "unexpected call, name %s\n", wine_dbgstr_w(name));
4204 return E_NOTIMPL;
4207 static HRESULT WINAPI fontcollection_GetFontFromFontFace(IDWriteFontCollection *iface, IDWriteFontFace *face, IDWriteFont **font)
4209 ok(0, "unexpected call\n");
4210 return E_NOTIMPL;
4213 static const IDWriteFontCollectionVtbl fallbackcollectionvtbl = {
4214 fontcollection_QI,
4215 fontcollection_AddRef,
4216 fontcollection_Release,
4217 fontcollection_GetFontFamilyCount,
4218 fontcollection_GetFontFamily,
4219 fontcollection_FindFamilyName,
4220 fontcollection_GetFontFromFontFace
4223 static IDWriteFontCollection fallbackcollection = { &fallbackcollectionvtbl };
4225 static void test_MapCharacters(void)
4227 static const WCHAR strW[] = {'a','b','c',0};
4228 static const WCHAR str2W[] = {'a',0x3058,'b',0};
4229 IDWriteLocalizedStrings *strings;
4230 IDWriteFontFallback *fallback;
4231 IDWriteFactory2 *factory2;
4232 IDWriteFactory *factory;
4233 UINT32 mappedlength;
4234 IDWriteFont *font;
4235 WCHAR buffW[50];
4236 BOOL exists;
4237 FLOAT scale;
4238 HRESULT hr;
4240 factory = create_factory();
4242 hr = IDWriteFactory_QueryInterface(factory, &IID_IDWriteFactory2, (void**)&factory2);
4243 IDWriteFactory_Release(factory);
4244 if (hr != S_OK) {
4245 win_skip("MapCharacters() is not supported\n");
4246 return;
4249 fallback = NULL;
4250 hr = IDWriteFactory2_GetSystemFontFallback(factory2, &fallback);
4251 ok(hr == S_OK, "got 0x%08x\n", hr);
4252 ok(fallback != NULL, "got %p\n", fallback);
4254 mappedlength = 1;
4255 scale = 0.0f;
4256 font = (void*)0xdeadbeef;
4257 hr = IDWriteFontFallback_MapCharacters(fallback, NULL, 0, 0, NULL, NULL, DWRITE_FONT_WEIGHT_NORMAL,
4258 DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, &mappedlength, &font, &scale);
4259 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
4260 ok(mappedlength == 0, "got %u\n", mappedlength);
4261 ok(scale == 1.0f, "got %f\n", scale);
4262 ok(font == NULL, "got %p\n", font);
4264 /* zero length source */
4265 g_source = strW;
4266 mappedlength = 1;
4267 scale = 0.0f;
4268 font = (void*)0xdeadbeef;
4269 hr = IDWriteFontFallback_MapCharacters(fallback, &analysissource, 0, 0, NULL, NULL, DWRITE_FONT_WEIGHT_NORMAL,
4270 DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, &mappedlength, &font, &scale);
4271 ok(hr == S_OK, "got 0x%08x\n", hr);
4272 ok(mappedlength == 0, "got %u\n", mappedlength);
4273 ok(scale == 1.0f, "got %f\n", scale);
4274 ok(font == NULL, "got %p\n", font);
4276 g_source = strW;
4277 mappedlength = 0;
4278 scale = 0.0f;
4279 font = NULL;
4280 hr = IDWriteFontFallback_MapCharacters(fallback, &analysissource, 0, 1, NULL, NULL, DWRITE_FONT_WEIGHT_NORMAL,
4281 DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, &mappedlength, &font, &scale);
4282 todo_wine {
4283 ok(hr == S_OK, "got 0x%08x\n", hr);
4284 ok(mappedlength == 1, "got %u\n", mappedlength);
4286 ok(scale == 1.0f, "got %f\n", scale);
4287 todo_wine
4288 ok(font != NULL, "got %p\n", font);
4289 if (font)
4290 IDWriteFont_Release(font);
4292 /* same latin text, full length */
4293 g_source = strW;
4294 mappedlength = 0;
4295 scale = 0.0f;
4296 font = NULL;
4297 hr = IDWriteFontFallback_MapCharacters(fallback, &analysissource, 0, 3, NULL, NULL, DWRITE_FONT_WEIGHT_NORMAL,
4298 DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, &mappedlength, &font, &scale);
4299 todo_wine {
4300 ok(hr == S_OK, "got 0x%08x\n", hr);
4301 ok(mappedlength == 3, "got %u\n", mappedlength);
4303 ok(scale == 1.0f, "got %f\n", scale);
4304 todo_wine
4305 ok(font != NULL, "got %p\n", font);
4306 if (font)
4307 IDWriteFont_Release(font);
4309 /* string 'a\x3058b' */
4310 g_source = str2W;
4311 mappedlength = 0;
4312 scale = 0.0f;
4313 font = NULL;
4314 hr = IDWriteFontFallback_MapCharacters(fallback, &analysissource, 0, 3, NULL, NULL, DWRITE_FONT_WEIGHT_NORMAL,
4315 DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, &mappedlength, &font, &scale);
4316 todo_wine {
4317 ok(hr == S_OK, "got 0x%08x\n", hr);
4318 ok(mappedlength == 1, "got %u\n", mappedlength);
4320 ok(scale == 1.0f, "got %f\n", scale);
4321 todo_wine
4322 ok(font != NULL, "got %p\n", font);
4323 if (font)
4324 IDWriteFont_Release(font);
4326 g_source = str2W;
4327 mappedlength = 0;
4328 scale = 0.0f;
4329 font = NULL;
4330 hr = IDWriteFontFallback_MapCharacters(fallback, &analysissource, 1, 2, NULL, NULL, DWRITE_FONT_WEIGHT_NORMAL,
4331 DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, &mappedlength, &font, &scale);
4332 todo_wine {
4333 ok(hr == S_OK, "got 0x%08x\n", hr);
4334 ok(mappedlength == 1, "got %u\n", mappedlength);
4336 ok(scale == 1.0f, "got %f\n", scale);
4337 todo_wine
4338 ok(font != NULL, "got %p\n", font);
4339 if (font) {
4340 /* font returned for Hiragana character, check if it supports Latin too */
4341 exists = FALSE;
4342 hr = IDWriteFont_HasCharacter(font, 'b', &exists);
4343 ok(hr == S_OK, "got 0x%08x\n", hr);
4344 ok(exists, "got %d\n", exists);
4346 IDWriteFont_Release(font);
4348 /* Try with explicit collection, Tahoma will be forced. */
4349 /* 1. Latin part */
4350 g_source = str2W;
4351 mappedlength = 0;
4352 scale = 0.0f;
4353 font = NULL;
4354 hr = IDWriteFontFallback_MapCharacters(fallback, &analysissource, 0, 3, &fallbackcollection, g_blahfontW, DWRITE_FONT_WEIGHT_NORMAL,
4355 DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, &mappedlength, &font, &scale);
4356 ok(hr == S_OK, "got 0x%08x\n", hr);
4357 ok(mappedlength == 1, "got %u\n", mappedlength);
4358 ok(scale == 1.0f, "got %f\n", scale);
4359 ok(font != NULL, "got %p\n", font);
4361 exists = FALSE;
4362 hr = IDWriteFont_GetInformationalStrings(font, DWRITE_INFORMATIONAL_STRING_WIN32_FAMILY_NAMES, &strings, &exists);
4363 ok(hr == S_OK && exists, "got 0x%08x, exists %d\n", hr, exists);
4364 hr = IDWriteLocalizedStrings_GetString(strings, 0, buffW, sizeof(buffW)/sizeof(WCHAR));
4365 ok(hr == S_OK, "got 0x%08x\n", hr);
4366 ok(!lstrcmpW(buffW, tahomaW), "%s\n", wine_dbgstr_w(buffW));
4367 IDWriteLocalizedStrings_Release(strings);
4368 IDWriteFont_Release(font);
4370 /* 2. Hiragana character, force Tahoma font does not support Japanese */
4371 g_source = str2W;
4372 mappedlength = 0;
4373 scale = 0.0f;
4374 font = NULL;
4375 hr = IDWriteFontFallback_MapCharacters(fallback, &analysissource, 1, 1, &fallbackcollection, g_blahfontW, DWRITE_FONT_WEIGHT_NORMAL,
4376 DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, &mappedlength, &font, &scale);
4377 ok(hr == S_OK, "got 0x%08x\n", hr);
4378 ok(mappedlength == 1, "got %u\n", mappedlength);
4379 ok(scale == 1.0f, "got %f\n", scale);
4380 ok(font != NULL, "got %p\n", font);
4382 exists = FALSE;
4383 hr = IDWriteFont_GetInformationalStrings(font, DWRITE_INFORMATIONAL_STRING_WIN32_FAMILY_NAMES, &strings, &exists);
4384 ok(hr == S_OK && exists, "got 0x%08x, exists %d\n", hr, exists);
4385 hr = IDWriteLocalizedStrings_GetString(strings, 0, buffW, sizeof(buffW)/sizeof(WCHAR));
4386 ok(hr == S_OK, "got 0x%08x\n", hr);
4387 todo_wine
4388 ok(lstrcmpW(buffW, tahomaW), "%s\n", wine_dbgstr_w(buffW));
4389 IDWriteLocalizedStrings_Release(strings);
4390 IDWriteFont_Release(font);
4392 IDWriteFontFallback_Release(fallback);
4393 IDWriteFactory2_Release(factory2);
4396 static void test_FontFallbackBuilder(void)
4398 static const WCHAR localeW[] = {'l','o','c','a','l','e',0};
4399 static const WCHAR strW[] = {'A',0};
4400 IDWriteFontFallbackBuilder *builder;
4401 IDWriteFontFallback *fallback;
4402 DWRITE_UNICODE_RANGE range;
4403 IDWriteFactory2 *factory2;
4404 IDWriteFactory *factory;
4405 const WCHAR *familyW;
4406 UINT32 mappedlength;
4407 IDWriteFont *font;
4408 FLOAT scale;
4409 HRESULT hr;
4411 factory = create_factory();
4413 hr = IDWriteFactory_QueryInterface(factory, &IID_IDWriteFactory2, (void**)&factory2);
4414 IDWriteFactory_Release(factory);
4416 if (factory2)
4417 hr = IDWriteFactory2_CreateFontFallbackBuilder(factory2, &builder);
4419 if (hr != S_OK) {
4420 skip("IDWriteFontFallbackBuilder is not supported\n");
4421 return;
4424 hr = IDWriteFontFallbackBuilder_CreateFontFallback(builder, &fallback);
4425 ok(hr == S_OK, "got 0x%08x\n", hr);
4427 hr = IDWriteFontFallbackBuilder_AddMapping(builder, NULL, 0, NULL, 0, NULL, NULL, NULL, 0.0f);
4428 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
4430 range.first = 'A';
4431 range.last = 'B';
4432 hr = IDWriteFontFallbackBuilder_AddMapping(builder, &range, 0, NULL, 0, NULL, NULL, NULL, 0.0f);
4433 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
4435 hr = IDWriteFontFallbackBuilder_AddMapping(builder, &range, 0, NULL, 0, NULL, NULL, NULL, 1.0f);
4436 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
4438 /* negative scaling factor */
4439 range.first = range.last = 0;
4440 familyW = g_blahfontW;
4441 hr = IDWriteFontFallbackBuilder_AddMapping(builder, &range, 1, &familyW, 1, NULL, NULL, NULL, -1.0f);
4442 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
4444 /* empty range */
4445 range.first = range.last = 0;
4446 familyW = g_blahfontW;
4447 hr = IDWriteFontFallbackBuilder_AddMapping(builder, &range, 1, &familyW, 1, NULL, NULL, NULL, 1.0f);
4448 ok(hr == S_OK, "got 0x%08x\n", hr);
4450 range.first = range.last = 0;
4451 hr = IDWriteFontFallbackBuilder_AddMapping(builder, &range, 1, &familyW, 1, NULL, NULL, NULL, 2.0f);
4452 ok(hr == S_OK, "got 0x%08x\n", hr);
4454 range.first = range.last = 'A';
4455 hr = IDWriteFontFallbackBuilder_AddMapping(builder, &range, 1, &familyW, 1, NULL, NULL, NULL, 3.0f);
4456 ok(hr == S_OK, "got 0x%08x\n", hr);
4458 range.first = 'B';
4459 range.last = 'A';
4460 hr = IDWriteFontFallbackBuilder_AddMapping(builder, &range, 1, &familyW, 1, NULL, NULL, NULL, 4.0f);
4461 ok(hr == S_OK, "got 0x%08x\n", hr);
4463 if (0) /* crashes on native */
4464 hr = IDWriteFontFallbackBuilder_CreateFontFallback(builder, NULL);
4466 hr = IDWriteFontFallbackBuilder_CreateFontFallback(builder, &fallback);
4467 ok(hr == S_OK, "got 0x%08x\n", hr);
4469 /* fallback font missing from system collection */
4470 g_source = strW;
4471 mappedlength = 0;
4472 scale = 0.0f;
4473 font = (void*)0xdeadbeef;
4474 hr = IDWriteFontFallback_MapCharacters(fallback, &analysissource, 0, 1, NULL, NULL, DWRITE_FONT_WEIGHT_NORMAL,
4475 DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, &mappedlength, &font, &scale);
4476 ok(hr == S_OK, "got 0x%08x\n", hr);
4477 ok(mappedlength == 1, "got %u\n", mappedlength);
4478 ok(scale == 1.0f, "got %f\n", scale);
4479 ok(font == NULL, "got %p\n", font);
4481 IDWriteFontFallback_Release(fallback);
4483 /* remap with custom collection */
4484 range.first = range.last = 'A';
4485 hr = IDWriteFontFallbackBuilder_AddMapping(builder, &range, 1, &familyW, 1, &fallbackcollection, NULL, NULL, 5.0f);
4486 ok(hr == S_OK, "got 0x%08x\n", hr);
4488 hr = IDWriteFontFallbackBuilder_CreateFontFallback(builder, &fallback);
4489 ok(hr == S_OK, "got 0x%08x\n", hr);
4491 g_source = strW;
4492 mappedlength = 0;
4493 scale = 0.0f;
4494 font = NULL;
4495 hr = IDWriteFontFallback_MapCharacters(fallback, &analysissource, 0, 1, NULL, NULL, DWRITE_FONT_WEIGHT_NORMAL,
4496 DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, &mappedlength, &font, &scale);
4497 ok(hr == S_OK, "got 0x%08x\n", hr);
4498 ok(mappedlength == 1, "got %u\n", mappedlength);
4499 ok(scale == 5.0f, "got %f\n", scale);
4500 ok(font != NULL, "got %p\n", font);
4501 IDWriteFont_Release(font);
4503 IDWriteFontFallback_Release(fallback);
4505 range.first = 'B';
4506 range.last = 'A';
4507 hr = IDWriteFontFallbackBuilder_AddMapping(builder, &range, 1, &familyW, 1, &fallbackcollection, NULL, NULL, 6.0f);
4508 ok(hr == S_OK, "got 0x%08x\n", hr);
4510 hr = IDWriteFontFallbackBuilder_CreateFontFallback(builder, &fallback);
4511 ok(hr == S_OK, "got 0x%08x\n", hr);
4513 g_source = strW;
4514 mappedlength = 0;
4515 scale = 0.0f;
4516 font = NULL;
4517 hr = IDWriteFontFallback_MapCharacters(fallback, &analysissource, 0, 1, NULL, NULL, DWRITE_FONT_WEIGHT_NORMAL,
4518 DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, &mappedlength, &font, &scale);
4519 ok(hr == S_OK, "got 0x%08x\n", hr);
4520 ok(mappedlength == 1, "got %u\n", mappedlength);
4521 ok(scale == 5.0f, "got %f\n", scale);
4522 ok(font != NULL, "got %p\n", font);
4523 IDWriteFont_Release(font);
4525 IDWriteFontFallback_Release(fallback);
4527 /* explicit locale */
4528 range.first = 'A';
4529 range.last = 'B';
4530 hr = IDWriteFontFallbackBuilder_AddMapping(builder, &range, 1, &familyW, 1, &fallbackcollection, localeW, NULL, 6.0f);
4531 ok(hr == S_OK, "got 0x%08x\n", hr);
4533 hr = IDWriteFontFallbackBuilder_CreateFontFallback(builder, &fallback);
4534 ok(hr == S_OK, "got 0x%08x\n", hr);
4536 g_source = strW;
4537 mappedlength = 0;
4538 scale = 0.0f;
4539 font = NULL;
4540 hr = IDWriteFontFallback_MapCharacters(fallback, &analysissource, 0, 1, NULL, NULL, DWRITE_FONT_WEIGHT_NORMAL,
4541 DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, &mappedlength, &font, &scale);
4542 ok(hr == S_OK, "got 0x%08x\n", hr);
4543 ok(mappedlength == 1, "got %u\n", mappedlength);
4544 ok(scale == 5.0f, "got %f\n", scale);
4545 ok(font != NULL, "got %p\n", font);
4546 IDWriteFont_Release(font);
4548 IDWriteFontFallbackBuilder_Release(builder);
4549 IDWriteFactory2_Release(factory2);
4552 static void test_SetTypography(void)
4554 static const WCHAR strW[] = {'a','f','i','b',0};
4555 IDWriteTypography *typography, *typography2;
4556 IDWriteTextFormat *format;
4557 IDWriteTextLayout *layout;
4558 DWRITE_TEXT_RANGE range;
4559 IDWriteFactory *factory;
4560 HRESULT hr;
4562 factory = create_factory();
4564 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
4565 DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format);
4566 ok(hr == S_OK, "got 0x%08x\n", hr);
4568 hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 1000.0, 1000.0, &layout);
4569 ok(hr == S_OK, "got 0x%08x\n", hr);
4570 IDWriteTextFormat_Release(format);
4572 hr = IDWriteFactory_CreateTypography(factory, &typography);
4573 ok(hr == S_OK, "got 0x%08x\n", hr);
4575 EXPECT_REF(typography, 1);
4576 range.startPosition = 0;
4577 range.length = 2;
4578 hr = IDWriteTextLayout_SetTypography(layout, typography, range);
4579 ok(hr == S_OK, "got 0x%08x\n", hr);
4580 EXPECT_REF(typography, 2);
4582 hr = IDWriteTextLayout_GetTypography(layout, 0, &typography2, NULL);
4583 ok(hr == S_OK, "got 0x%08x\n", hr);
4584 ok(typography2 == typography, "got %p, expected %p\n", typography2, typography);
4585 IDWriteTypography_Release(typography2);
4586 IDWriteTypography_Release(typography);
4588 hr = IDWriteFactory_CreateTypography(factory, &typography2);
4589 ok(hr == S_OK, "got 0x%08x\n", hr);
4591 range.startPosition = 0;
4592 range.length = 1;
4593 hr = IDWriteTextLayout_SetTypography(layout, typography2, range);
4594 ok(hr == S_OK, "got 0x%08x\n", hr);
4595 EXPECT_REF(typography2, 2);
4596 IDWriteTypography_Release(typography2);
4598 hr = IDWriteTextLayout_GetTypography(layout, 0, &typography, &range);
4599 ok(hr == S_OK, "got 0x%08x\n", hr);
4600 ok(range.length == 1, "got %u\n", range.length);
4602 IDWriteTypography_Release(typography);
4604 IDWriteTextLayout_Release(layout);
4605 IDWriteFactory_Release(factory);
4608 static void test_SetLastLineWrapping(void)
4610 static const WCHAR strW[] = {'a',0};
4611 IDWriteTextLayout2 *layout2;
4612 IDWriteTextFormat1 *format1;
4613 IDWriteTextLayout *layout;
4614 IDWriteTextFormat *format;
4615 IDWriteFactory *factory;
4616 HRESULT hr;
4617 BOOL ret;
4619 factory = create_factory();
4621 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
4622 DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format);
4623 ok(hr == S_OK, "got 0x%08x\n", hr);
4625 hr = IDWriteTextFormat_QueryInterface(format, &IID_IDWriteTextFormat1, (void**)&format1);
4626 IDWriteTextFormat_Release(format);
4627 if (hr != S_OK) {
4628 win_skip("SetLastLineWrapping() is not supported\n");
4629 IDWriteFactory_Release(factory);
4630 return;
4633 ret = IDWriteTextFormat1_GetLastLineWrapping(format1);
4634 ok(ret, "got %d\n", ret);
4636 hr = IDWriteTextFormat1_SetLastLineWrapping(format1, FALSE);
4637 ok(hr == S_OK, "got 0x%08x\n", hr);
4639 hr = IDWriteFactory_CreateTextLayout(factory, strW, 1, (IDWriteTextFormat*)format1, 1000.0, 1000.0, &layout);
4640 ok(hr == S_OK, "got 0x%08x\n", hr);
4642 hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextLayout2, (void**)&layout2);
4643 ok(hr == S_OK, "got 0x%08x\n", hr);
4644 IDWriteTextLayout_Release(layout);
4646 ret = IDWriteTextLayout2_GetLastLineWrapping(layout2);
4647 ok(!ret, "got %d\n", ret);
4649 hr = IDWriteTextLayout2_SetLastLineWrapping(layout2, TRUE);
4650 ok(hr == S_OK, "got 0x%08x\n", hr);
4652 IDWriteFactory_Release(factory);
4655 static void test_SetOpticalAlignment(void)
4657 static const WCHAR strW[] = {'a',0};
4658 DWRITE_OPTICAL_ALIGNMENT alignment;
4659 IDWriteTextLayout2 *layout2;
4660 IDWriteTextFormat1 *format1;
4661 IDWriteTextLayout *layout;
4662 IDWriteTextFormat *format;
4663 IDWriteFactory *factory;
4664 HRESULT hr;
4666 factory = create_factory();
4668 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
4669 DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format);
4670 ok(hr == S_OK, "got 0x%08x\n", hr);
4672 hr = IDWriteTextFormat_QueryInterface(format, &IID_IDWriteTextFormat1, (void**)&format1);
4673 IDWriteTextFormat_Release(format);
4674 if (hr != S_OK) {
4675 win_skip("SetOpticalAlignment() is not supported\n");
4676 IDWriteFactory_Release(factory);
4677 return;
4680 alignment = IDWriteTextFormat1_GetOpticalAlignment(format1);
4681 ok(alignment == DWRITE_OPTICAL_ALIGNMENT_NONE, "got %d\n", alignment);
4683 hr = IDWriteFactory_CreateTextLayout(factory, strW, 1, (IDWriteTextFormat*)format1, 1000.0, 1000.0, &layout);
4684 ok(hr == S_OK, "got 0x%08x\n", hr);
4686 hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextLayout2, (void**)&layout2);
4687 ok(hr == S_OK, "got 0x%08x\n", hr);
4688 IDWriteTextLayout_Release(layout);
4689 IDWriteTextFormat1_Release(format1);
4691 alignment = IDWriteTextLayout2_GetOpticalAlignment(layout2);
4692 ok(alignment == DWRITE_OPTICAL_ALIGNMENT_NONE, "got %d\n", alignment);
4694 hr = IDWriteTextLayout2_QueryInterface(layout2, &IID_IDWriteTextFormat1, (void**)&format1);
4695 ok(hr == S_OK, "got 0x%08x\n", hr);
4697 alignment = IDWriteTextFormat1_GetOpticalAlignment(format1);
4698 ok(alignment == DWRITE_OPTICAL_ALIGNMENT_NONE, "got %d\n", alignment);
4700 hr = IDWriteTextLayout2_SetOpticalAlignment(layout2, DWRITE_OPTICAL_ALIGNMENT_NO_SIDE_BEARINGS);
4701 ok(hr == S_OK, "got 0x%08x\n", hr);
4703 hr = IDWriteTextLayout2_SetOpticalAlignment(layout2, DWRITE_OPTICAL_ALIGNMENT_NO_SIDE_BEARINGS+1);
4704 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
4706 alignment = IDWriteTextFormat1_GetOpticalAlignment(format1);
4707 ok(alignment == DWRITE_OPTICAL_ALIGNMENT_NO_SIDE_BEARINGS, "got %d\n", alignment);
4709 hr = IDWriteTextFormat1_SetOpticalAlignment(format1, DWRITE_OPTICAL_ALIGNMENT_NONE);
4710 ok(hr == S_OK, "got 0x%08x\n", hr);
4712 hr = IDWriteTextFormat1_SetOpticalAlignment(format1, DWRITE_OPTICAL_ALIGNMENT_NO_SIDE_BEARINGS+1);
4713 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
4715 alignment = IDWriteTextLayout2_GetOpticalAlignment(layout2);
4716 ok(alignment == DWRITE_OPTICAL_ALIGNMENT_NONE, "got %d\n", alignment);
4718 IDWriteTextLayout2_Release(layout2);
4719 IDWriteTextFormat1_Release(format1);
4720 IDWriteFactory_Release(factory);
4723 static const struct drawcall_entry drawunderline_seq[] = {
4724 { DRAW_GLYPHRUN, {'a','e',0x0300,0} }, /* reported runs can't mix different underline values */
4725 { DRAW_GLYPHRUN, {'d',0} },
4726 { DRAW_UNDERLINE, {0}, {'e','n','-','u','s',0} },
4727 { DRAW_LAST_KIND }
4730 static const struct drawcall_entry drawunderline2_seq[] = {
4731 { DRAW_GLYPHRUN, {'a',0} },
4732 { DRAW_GLYPHRUN, {'e',0} },
4733 { DRAW_UNDERLINE, {0}, {'e','n','-','u','s',0} },
4734 { DRAW_LAST_KIND }
4737 static const struct drawcall_entry drawunderline3_seq[] = {
4738 { DRAW_GLYPHRUN, {'a',0} },
4739 { DRAW_GLYPHRUN, {'e',0} },
4740 { DRAW_UNDERLINE, {0}, {'e','n','-','c','a',0} },
4741 { DRAW_UNDERLINE, {0}, {'e','n','-','u','s',0} },
4742 { DRAW_LAST_KIND }
4745 static const struct drawcall_entry drawunderline4_seq[] = {
4746 { DRAW_GLYPHRUN, {'a',0} },
4747 { DRAW_GLYPHRUN, {'e',0} },
4748 { DRAW_UNDERLINE, {0}, {'e','n','-','u','s',0} },
4749 { DRAW_STRIKETHROUGH },
4750 { DRAW_LAST_KIND }
4753 static void test_SetUnderline(void)
4755 static const WCHAR encaW[] = {'e','n','-','C','A',0};
4756 static const WCHAR strW[] = {'a','e',0x0300,'d',0}; /* accent grave */
4757 DWRITE_CLUSTER_METRICS clusters[4];
4758 IDWriteTextFormat *format;
4759 IDWriteTextLayout *layout;
4760 DWRITE_TEXT_RANGE range;
4761 IDWriteFactory *factory;
4762 UINT32 count;
4763 HRESULT hr;
4765 factory = create_factory();
4767 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
4768 DWRITE_FONT_STRETCH_NORMAL, 10.0, enusW, &format);
4769 ok(hr == S_OK, "got 0x%08x\n", hr);
4771 hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 1000.0, 1000.0, &layout);
4772 ok(hr == S_OK, "got 0x%08x\n", hr);
4774 count = 0;
4775 hr = IDWriteTextLayout_GetClusterMetrics(layout, clusters, sizeof(clusters)/sizeof(clusters[0]), &count);
4776 ok(hr == S_OK, "got 0x%08x\n", hr);
4777 todo_wine
4778 ok(count == 3, "got %u\n", count);
4780 range.startPosition = 0;
4781 range.length = 2;
4782 hr = IDWriteTextLayout_SetUnderline(layout, TRUE, range);
4783 ok(hr == S_OK, "got 0x%08x\n", hr);
4785 count = 0;
4786 hr = IDWriteTextLayout_GetClusterMetrics(layout, clusters, sizeof(clusters)/sizeof(clusters[0]), &count);
4787 ok(hr == S_OK, "got 0x%08x\n", hr);
4788 todo_wine
4789 ok(count == 3, "got %u\n", count);
4791 flush_sequence(sequences, RENDERER_ID);
4792 hr = IDWriteTextLayout_Draw(layout, NULL, &testrenderer, 0.0, 0.0);
4793 ok(hr == S_OK, "got 0x%08x\n", hr);
4794 ok_sequence(sequences, RENDERER_ID, drawunderline_seq, "draw underline test", TRUE);
4796 IDWriteTextLayout_Release(layout);
4798 /* 2 characters, same font, significantly different font size. Set underline for both, see how many
4799 underline drawing calls is there. */
4800 hr = IDWriteFactory_CreateTextLayout(factory, strW, 2, format, 1000.0f, 1000.0f, &layout);
4801 ok(hr == S_OK, "got 0x%08x\n", hr);
4803 range.startPosition = 0;
4804 range.length = 2;
4805 hr = IDWriteTextLayout_SetUnderline(layout, TRUE, range);
4806 ok(hr == S_OK, "got 0x%08x\n", hr);
4808 range.startPosition = 0;
4809 range.length = 1;
4810 hr = IDWriteTextLayout_SetFontSize(layout, 100.0f, range);
4811 ok(hr == S_OK, "got 0x%08x\n", hr);
4813 flush_sequence(sequences, RENDERER_ID);
4814 hr = IDWriteTextLayout_Draw(layout, NULL, &testrenderer, 0.0f, 0.0f);
4815 ok(hr == S_OK, "got 0x%08x\n", hr);
4816 ok_sequence(sequences, RENDERER_ID, drawunderline2_seq, "draw underline test 2", FALSE);
4818 /* now set different locale for second char, draw again */
4819 range.startPosition = 0;
4820 range.length = 1;
4821 hr = IDWriteTextLayout_SetLocaleName(layout, encaW, range);
4822 ok(hr == S_OK, "got 0x%08x\n", hr);
4824 flush_sequence(sequences, RENDERER_ID);
4825 hr = IDWriteTextLayout_Draw(layout, NULL, &testrenderer, 0.0f, 0.0f);
4826 ok(hr == S_OK, "got 0x%08x\n", hr);
4827 ok_sequence(sequences, RENDERER_ID, drawunderline3_seq, "draw underline test 2", FALSE);
4829 IDWriteTextLayout_Release(layout);
4831 /* 2 characters, same font properties, first with strikethrough, both underlined */
4832 hr = IDWriteFactory_CreateTextLayout(factory, strW, 2, format, 1000.0f, 1000.0f, &layout);
4833 ok(hr == S_OK, "got 0x%08x\n", hr);
4835 range.startPosition = 0;
4836 range.length = 1;
4837 hr = IDWriteTextLayout_SetStrikethrough(layout, TRUE, range);
4838 ok(hr == S_OK, "got 0x%08x\n", hr);
4840 range.startPosition = 0;
4841 range.length = 2;
4842 hr = IDWriteTextLayout_SetUnderline(layout, TRUE, range);
4843 ok(hr == S_OK, "got 0x%08x\n", hr);
4845 flush_sequence(sequences, RENDERER_ID);
4846 hr = IDWriteTextLayout_Draw(layout, NULL, &testrenderer, 0.0f, 0.0f);
4847 ok(hr == S_OK, "got 0x%08x\n", hr);
4848 ok_sequence(sequences, RENDERER_ID, drawunderline4_seq, "draw underline test 4", FALSE);
4850 IDWriteTextLayout_Release(layout);
4852 IDWriteTextFormat_Release(format);
4853 IDWriteFactory_Release(factory);
4856 static void test_InvalidateLayout(void)
4858 static const WCHAR strW[] = {'a',0};
4859 IDWriteTextLayout3 *layout3;
4860 IDWriteTextLayout *layout;
4861 IDWriteTextFormat *format;
4862 IDWriteFactory *factory;
4863 HRESULT hr;
4865 factory = create_factory();
4867 hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
4868 DWRITE_FONT_STRETCH_NORMAL, 10.0f, enusW, &format);
4869 ok(hr == S_OK, "got 0x%08x\n", hr);
4871 hr = IDWriteFactory_CreateTextLayout(factory, strW, 1, format, 1000.0f, 1000.0f, &layout);
4872 ok(hr == S_OK, "got 0x%08x\n", hr);
4874 hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextLayout3, (void**)&layout3);
4875 if (hr == S_OK) {
4876 hr = IDWriteTextLayout3_InvalidateLayout(layout3);
4877 ok(hr == S_OK, "got 0x%08x\n", hr);
4878 IDWriteTextLayout3_Release(layout3);
4880 else
4881 win_skip("IDWriteTextLayout3::InvalidateLayout() is not supported.\n");
4883 IDWriteTextLayout_Release(layout);
4884 IDWriteTextFormat_Release(format);
4885 IDWriteFactory_Release(factory);
4888 START_TEST(layout)
4890 IDWriteFactory *factory;
4892 if (!(factory = create_factory())) {
4893 win_skip("failed to create factory\n");
4894 return;
4897 init_call_sequences(sequences, NUM_CALL_SEQUENCES);
4898 init_call_sequences(expected_seq, 1);
4900 test_CreateTextLayout();
4901 test_CreateGdiCompatibleTextLayout();
4902 test_CreateTextFormat();
4903 test_GetLocaleName();
4904 test_CreateEllipsisTrimmingSign();
4905 test_fontweight();
4906 test_SetInlineObject();
4907 test_Draw();
4908 test_typography();
4909 test_GetClusterMetrics();
4910 test_SetLocaleName();
4911 test_SetPairKerning();
4912 test_SetVerticalGlyphOrientation();
4913 test_fallback();
4914 test_DetermineMinWidth();
4915 test_SetFontSize();
4916 test_SetFontFamilyName();
4917 test_SetFontStyle();
4918 test_SetFontStretch();
4919 test_SetStrikethrough();
4920 test_GetMetrics();
4921 test_SetFlowDirection();
4922 test_SetDrawingEffect();
4923 test_GetLineMetrics();
4924 test_SetTextAlignment();
4925 test_SetParagraphAlignment();
4926 test_SetReadingDirection();
4927 test_pixelsnapping();
4928 test_SetWordWrapping();
4929 test_MapCharacters();
4930 test_FontFallbackBuilder();
4931 test_SetTypography();
4932 test_SetLastLineWrapping();
4933 test_SetOpticalAlignment();
4934 test_SetUnderline();
4935 test_InvalidateLayout();
4937 IDWriteFactory_Release(factory);