dwrite/tests: Add some fallback tests for Segoe UI Symbol.
[wine.git] / dlls / dwrite / tests / layout.c
blob43b6efa107bff3645a5ac38dce169debb7601d85
1 /*
2 * Text layout/format tests
4 * Copyright 2012, 2014-2020 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 struct testanalysissink
34 IDWriteTextAnalysisSink IDWriteTextAnalysisSink_iface;
35 DWRITE_SCRIPT_ANALYSIS sa; /* last analysis, with SetScriptAnalysis() */
38 static inline struct testanalysissink *impl_from_IDWriteTextAnalysisSink(IDWriteTextAnalysisSink *iface)
40 return CONTAINING_RECORD(iface, struct testanalysissink, IDWriteTextAnalysisSink_iface);
43 /* test IDWriteTextAnalysisSink */
44 static HRESULT WINAPI analysissink_QueryInterface(IDWriteTextAnalysisSink *iface, REFIID riid, void **obj)
46 if (IsEqualIID(riid, &IID_IDWriteTextAnalysisSink) || IsEqualIID(riid, &IID_IUnknown))
48 *obj = iface;
49 return S_OK;
52 *obj = NULL;
53 return E_NOINTERFACE;
56 static ULONG WINAPI analysissink_AddRef(IDWriteTextAnalysisSink *iface)
58 return 2;
61 static ULONG WINAPI analysissink_Release(IDWriteTextAnalysisSink *iface)
63 return 1;
66 static HRESULT WINAPI analysissink_SetScriptAnalysis(IDWriteTextAnalysisSink *iface,
67 UINT32 position, UINT32 length, DWRITE_SCRIPT_ANALYSIS const* sa)
69 struct testanalysissink *sink = impl_from_IDWriteTextAnalysisSink(iface);
70 sink->sa = *sa;
71 return S_OK;
74 static HRESULT WINAPI analysissink_SetLineBreakpoints(IDWriteTextAnalysisSink *iface,
75 UINT32 position, UINT32 length, DWRITE_LINE_BREAKPOINT const* breakpoints)
77 ok(0, "unexpected call\n");
78 return E_NOTIMPL;
81 static HRESULT WINAPI analysissink_SetBidiLevel(IDWriteTextAnalysisSink *iface,
82 UINT32 position, UINT32 length, UINT8 explicitLevel, UINT8 resolvedLevel)
84 ok(0, "unexpected\n");
85 return E_NOTIMPL;
88 static HRESULT WINAPI analysissink_SetNumberSubstitution(IDWriteTextAnalysisSink *iface,
89 UINT32 position, UINT32 length, IDWriteNumberSubstitution* substitution)
91 ok(0, "unexpected\n");
92 return E_NOTIMPL;
95 static IDWriteTextAnalysisSinkVtbl analysissinkvtbl = {
96 analysissink_QueryInterface,
97 analysissink_AddRef,
98 analysissink_Release,
99 analysissink_SetScriptAnalysis,
100 analysissink_SetLineBreakpoints,
101 analysissink_SetBidiLevel,
102 analysissink_SetNumberSubstitution
105 static struct testanalysissink analysissink = {
106 { &analysissinkvtbl },
107 { 0 }
110 /* test IDWriteTextAnalysisSource */
111 static HRESULT WINAPI analysissource_QueryInterface(IDWriteTextAnalysisSource *iface,
112 REFIID riid, void **obj)
114 if (IsEqualIID(riid, &IID_IDWriteTextAnalysisSource) || IsEqualIID(riid, &IID_IUnknown)) {
115 *obj = iface;
116 IDWriteTextAnalysisSource_AddRef(iface);
117 return S_OK;
119 return E_NOINTERFACE;
122 static ULONG WINAPI analysissource_AddRef(IDWriteTextAnalysisSource *iface)
124 return 2;
127 static ULONG WINAPI analysissource_Release(IDWriteTextAnalysisSource *iface)
129 return 1;
132 static const WCHAR *g_source;
134 static HRESULT WINAPI analysissource_GetTextAtPosition(IDWriteTextAnalysisSource *iface,
135 UINT32 position, WCHAR const** text, UINT32* text_len)
137 if (position >= lstrlenW(g_source))
139 *text = NULL;
140 *text_len = 0;
142 else
144 *text = &g_source[position];
145 *text_len = lstrlenW(g_source) - position;
148 return S_OK;
151 static HRESULT WINAPI analysissource_GetTextBeforePosition(IDWriteTextAnalysisSource *iface,
152 UINT32 position, WCHAR const** text, UINT32* text_len)
154 ok(0, "unexpected\n");
155 return E_NOTIMPL;
158 static DWRITE_READING_DIRECTION WINAPI analysissource_GetParagraphReadingDirection(
159 IDWriteTextAnalysisSource *iface)
161 ok(0, "unexpected\n");
162 return DWRITE_READING_DIRECTION_RIGHT_TO_LEFT;
165 static HRESULT WINAPI analysissource_GetLocaleName(IDWriteTextAnalysisSource *iface,
166 UINT32 position, UINT32* text_len, WCHAR const** locale)
168 *locale = NULL;
169 *text_len = 0;
170 return S_OK;
173 static HRESULT WINAPI analysissource_GetNumberSubstitution(IDWriteTextAnalysisSource *iface,
174 UINT32 position, UINT32* text_len, IDWriteNumberSubstitution **substitution)
176 ok(0, "unexpected\n");
177 return E_NOTIMPL;
180 static IDWriteTextAnalysisSourceVtbl analysissourcevtbl = {
181 analysissource_QueryInterface,
182 analysissource_AddRef,
183 analysissource_Release,
184 analysissource_GetTextAtPosition,
185 analysissource_GetTextBeforePosition,
186 analysissource_GetParagraphReadingDirection,
187 analysissource_GetLocaleName,
188 analysissource_GetNumberSubstitution
191 static IDWriteTextAnalysisSource analysissource = { &analysissourcevtbl };
193 static void *create_factory_iid(REFIID riid)
195 IUnknown *factory = NULL;
196 DWriteCreateFactory(DWRITE_FACTORY_TYPE_ISOLATED, riid, &factory);
197 return factory;
200 static IDWriteFactory *create_factory(void)
202 IDWriteFactory *factory = create_factory_iid(&IID_IDWriteFactory);
203 ok(factory != NULL, "Failed to create factory.\n");
204 return factory;
207 /* obvious limitation is that only last script data is returned, so this
208 helper is suitable for single script strings only */
209 static void get_script_analysis(const WCHAR *str, UINT32 len, DWRITE_SCRIPT_ANALYSIS *sa)
211 IDWriteTextAnalyzer *analyzer;
212 IDWriteFactory *factory;
213 HRESULT hr;
215 g_source = str;
217 factory = create_factory();
218 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
219 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
221 hr = IDWriteTextAnalyzer_AnalyzeScript(analyzer, &analysissource, 0, len, &analysissink.IDWriteTextAnalysisSink_iface);
222 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
224 *sa = analysissink.sa;
225 IDWriteFactory_Release(factory);
228 static IDWriteFontFace *get_fontface_from_format(IDWriteTextFormat *format)
230 IDWriteFontCollection *collection;
231 IDWriteFontFamily *family;
232 IDWriteFontFace *fontface;
233 IDWriteFont *font;
234 WCHAR nameW[255];
235 UINT32 index;
236 BOOL exists;
237 HRESULT hr;
239 hr = IDWriteTextFormat_GetFontCollection(format, &collection);
240 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
242 hr = IDWriteTextFormat_GetFontFamilyName(format, nameW, ARRAY_SIZE(nameW));
243 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
245 hr = IDWriteFontCollection_FindFamilyName(collection, nameW, &index, &exists);
246 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
248 hr = IDWriteFontCollection_GetFontFamily(collection, index, &family);
249 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
250 IDWriteFontCollection_Release(collection);
252 hr = IDWriteFontFamily_GetFirstMatchingFont(family,
253 IDWriteTextFormat_GetFontWeight(format),
254 IDWriteTextFormat_GetFontStretch(format),
255 IDWriteTextFormat_GetFontStyle(format),
256 &font);
257 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
259 hr = IDWriteFont_CreateFontFace(font, &fontface);
260 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
262 IDWriteFont_Release(font);
263 IDWriteFontFamily_Release(family);
265 return fontface;
268 #define EXPECT_REF(obj,ref) _expect_ref((IUnknown*)obj, ref, __LINE__)
269 static void _expect_ref(IUnknown* obj, ULONG ref, int line)
271 ULONG rc;
272 IUnknown_AddRef(obj);
273 rc = IUnknown_Release(obj);
274 ok_(__FILE__,line)(rc == ref, "expected refcount %ld, got %ld\n", ref, rc);
277 enum drawcall_modifiers_kind {
278 DRAW_EFFECT = 0x1000
281 enum drawcall_kind {
282 DRAW_GLYPHRUN = 0,
283 DRAW_UNDERLINE = 1,
284 DRAW_STRIKETHROUGH = 2,
285 DRAW_INLINE = 3,
286 DRAW_LAST_KIND = 4,
287 DRAW_TOTAL_KINDS = 5,
288 DRAW_KINDS_MASK = 0xff
291 static const char *get_draw_kind_name(unsigned short kind)
293 static const char *kind_names[] = {
294 "GLYPH_RUN",
295 "UNDERLINE",
296 "STRIKETHROUGH",
297 "INLINE",
298 "END_OF_SEQ",
300 "GLYPH_RUN|EFFECT",
301 "UNDERLINE|EFFECT",
302 "STRIKETHROUGH|EFFECT",
303 "INLINE|EFFECT",
304 "END_OF_SEQ"
306 if ((kind & DRAW_KINDS_MASK) > DRAW_LAST_KIND)
307 return "unknown";
308 return (kind & DRAW_EFFECT) ? kind_names[(kind & DRAW_KINDS_MASK) + DRAW_TOTAL_KINDS] :
309 kind_names[kind];
312 struct drawcall_entry {
313 enum drawcall_kind kind;
314 WCHAR string[10]; /* only meaningful for DrawGlyphRun() */
315 WCHAR locale[LOCALE_NAME_MAX_LENGTH];
316 UINT32 glyphcount; /* only meaningful for DrawGlyphRun() */
317 UINT32 bidilevel;
320 struct drawcall_sequence
322 int count;
323 int size;
324 struct drawcall_entry *sequence;
327 struct drawtestcontext {
328 unsigned short kind;
329 BOOL todo;
330 int *failcount;
331 const char *file;
332 int line;
335 #define NUM_CALL_SEQUENCES 1
336 #define RENDERER_ID 0
337 static struct drawcall_sequence *sequences[NUM_CALL_SEQUENCES];
338 static struct drawcall_sequence *expected_seq[1];
340 static void add_call(struct drawcall_sequence **seq, int sequence_index, const struct drawcall_entry *call)
342 struct drawcall_sequence *call_seq = seq[sequence_index];
344 if (!call_seq->sequence) {
345 call_seq->size = 10;
346 call_seq->sequence = malloc(call_seq->size * sizeof(*call_seq->sequence));
349 if (call_seq->count == call_seq->size) {
350 call_seq->size *= 2;
351 call_seq->sequence = realloc(call_seq->sequence, call_seq->size * sizeof(*call_seq->sequence));
354 assert(call_seq->sequence);
355 call_seq->sequence[call_seq->count++] = *call;
358 static inline void flush_sequence(struct drawcall_sequence **seg, int sequence_index)
360 struct drawcall_sequence *call_seq = seg[sequence_index];
362 free(call_seq->sequence);
363 call_seq->sequence = NULL;
364 call_seq->count = call_seq->size = 0;
367 static void init_call_sequences(struct drawcall_sequence **seq, int n)
369 int i;
371 for (i = 0; i < n; i++)
372 seq[i] = calloc(1, sizeof(*seq[i]));
375 static void ok_sequence_(struct drawcall_sequence **seq, int sequence_index,
376 const struct drawcall_entry *expected, const char *context, BOOL todo,
377 const char *file, int line)
379 static const struct drawcall_entry end_of_sequence = { DRAW_LAST_KIND };
380 struct drawcall_sequence *call_seq = seq[sequence_index];
381 const struct drawcall_entry *actual, *sequence;
382 int failcount = 0;
384 add_call(seq, sequence_index, &end_of_sequence);
386 sequence = call_seq->sequence;
387 actual = sequence;
389 while (expected->kind != DRAW_LAST_KIND && actual->kind != DRAW_LAST_KIND) {
390 if (expected->kind != actual->kind) {
391 if (todo) {
392 failcount++;
393 todo_wine
394 ok_(file, line) (0, "%s: call %s was expected, but got call %s instead\n",
395 context, get_draw_kind_name(expected->kind), get_draw_kind_name(actual->kind));
397 flush_sequence(seq, sequence_index);
398 return;
400 else
401 ok_(file, line) (0, "%s: call %s was expected, but got call %s instead\n",
402 context, get_draw_kind_name(expected->kind), get_draw_kind_name(actual->kind));
404 else if ((expected->kind & DRAW_KINDS_MASK) == DRAW_GLYPHRUN) {
405 int cmp = lstrcmpW(expected->string, actual->string);
406 if (cmp != 0 && todo) {
407 failcount++;
408 todo_wine
409 ok_(file, line) (0, "%s: glyphrun string %s was expected, but got %s instead\n",
410 context, wine_dbgstr_w(expected->string), wine_dbgstr_w(actual->string));
412 else
413 ok_(file, line) (cmp == 0, "%s: glyphrun string %s was expected, but got %s instead\n",
414 context, wine_dbgstr_w(expected->string), wine_dbgstr_w(actual->string));
416 cmp = lstrcmpW(expected->locale, actual->locale);
417 if (cmp != 0 && todo) {
418 failcount++;
419 todo_wine
420 ok_(file, line) (0, "%s: glyph run locale %s was expected, but got %s instead\n",
421 context, wine_dbgstr_w(expected->locale), wine_dbgstr_w(actual->locale));
423 else
424 ok_(file, line) (cmp == 0, "%s: glyph run locale %s was expected, but got %s instead\n",
425 context, wine_dbgstr_w(expected->locale), wine_dbgstr_w(actual->locale));
427 if (expected->glyphcount != actual->glyphcount && todo) {
428 failcount++;
429 todo_wine
430 ok_(file, line) (0, "%s: wrong glyph count, %u was expected, but got %u instead\n",
431 context, expected->glyphcount, actual->glyphcount);
433 else
434 ok_(file, line) (expected->glyphcount == actual->glyphcount,
435 "%s: wrong glyph count, %u was expected, but got %u instead\n",
436 context, expected->glyphcount, actual->glyphcount);
438 if (expected->bidilevel != actual->bidilevel && todo) {
439 failcount++;
440 todo_wine
441 ok_(file, line) (0, "%s: wrong bidi level, %u was expected, but got %u instead\n",
442 context, expected->bidilevel, actual->bidilevel);
444 else
445 ok_(file, line) (expected->bidilevel == actual->bidilevel,
446 "%s: wrong bidi level, %u was expected, but got %u instead\n",
447 context, expected->bidilevel, actual->bidilevel);
449 else if ((expected->kind & DRAW_KINDS_MASK) == DRAW_UNDERLINE) {
450 int cmp = lstrcmpW(expected->locale, actual->locale);
451 if (cmp != 0 && todo) {
452 failcount++;
453 todo_wine
454 ok_(file, line) (0, "%s: underline locale %s was expected, but got %s instead\n",
455 context, wine_dbgstr_w(expected->locale), wine_dbgstr_w(actual->locale));
457 else
458 ok_(file, line) (cmp == 0, "%s: underline locale %s was expected, but got %s instead\n",
459 context, wine_dbgstr_w(expected->locale), wine_dbgstr_w(actual->locale));
461 expected++;
462 actual++;
465 if (todo) {
466 todo_wine {
467 if (expected->kind != DRAW_LAST_KIND || actual->kind != DRAW_LAST_KIND) {
468 failcount++;
469 ok_(file, line) (0, "%s: the call sequence is not complete: expected %s - actual %s\n",
470 context, get_draw_kind_name(expected->kind), get_draw_kind_name(actual->kind));
474 else if (expected->kind != DRAW_LAST_KIND || actual->kind != DRAW_LAST_KIND)
475 ok_(file, line) (0, "%s: the call sequence is not complete: expected %s - actual %s\n",
476 context, get_draw_kind_name(expected->kind), get_draw_kind_name(actual->kind));
478 if (todo && !failcount) /* succeeded yet marked todo */
479 todo_wine
480 ok_(file, line)(1, "%s: marked \"todo_wine\" but succeeds\n", context);
482 flush_sequence(seq, sequence_index);
485 #define ok_sequence(seq, index, exp, contx, todo) \
486 ok_sequence_(seq, index, (exp), (contx), (todo), __FILE__, __LINE__)
488 static HRESULT WINAPI testrenderer_QI(IDWriteTextRenderer *iface, REFIID riid, void **obj)
490 if (IsEqualIID(riid, &IID_IDWriteTextRenderer) ||
491 IsEqualIID(riid, &IID_IDWritePixelSnapping) ||
492 IsEqualIID(riid, &IID_IUnknown)
494 *obj = iface;
495 return S_OK;
498 *obj = NULL;
500 /* IDWriteTextRenderer1 overrides drawing calls, ignore for now */
501 if (IsEqualIID(riid, &IID_IDWriteTextRenderer1))
502 return E_NOINTERFACE;
504 ok(0, "unexpected QI %s\n", wine_dbgstr_guid(riid));
505 return E_NOINTERFACE;
508 static ULONG WINAPI testrenderer_AddRef(IDWriteTextRenderer *iface)
510 return 2;
513 static ULONG WINAPI testrenderer_Release(IDWriteTextRenderer *iface)
515 return 1;
518 struct renderer_context {
519 BOOL gdicompat;
520 BOOL use_gdi_natural;
521 BOOL snapping_disabled;
522 DWRITE_MATRIX m;
523 FLOAT ppdip;
524 FLOAT originX;
525 FLOAT originY;
526 IDWriteTextFormat *format;
527 const WCHAR *familyW;
530 static HRESULT WINAPI testrenderer_IsPixelSnappingDisabled(IDWriteTextRenderer *iface,
531 void *context, BOOL *disabled)
533 struct renderer_context *ctxt = (struct renderer_context*)context;
534 if (ctxt)
535 *disabled = ctxt->snapping_disabled;
536 else
537 *disabled = TRUE;
538 return S_OK;
541 static HRESULT WINAPI testrenderer_GetCurrentTransform(IDWriteTextRenderer *iface,
542 void *context, DWRITE_MATRIX *m)
544 struct renderer_context *ctxt = (struct renderer_context*)context;
545 ok(!ctxt->snapping_disabled, "expected enabled snapping\n");
546 *m = ctxt->m;
547 return S_OK;
550 static HRESULT WINAPI testrenderer_GetPixelsPerDip(IDWriteTextRenderer *iface,
551 void *context, FLOAT *pixels_per_dip)
553 struct renderer_context *ctxt = (struct renderer_context*)context;
554 *pixels_per_dip = ctxt->ppdip;
555 return S_OK;
558 #define TEST_MEASURING_MODE(ctxt, mode) test_measuring_mode(ctxt, mode, __LINE__)
559 static void test_measuring_mode(const struct renderer_context *ctxt, DWRITE_MEASURING_MODE mode, int line)
561 if (ctxt->gdicompat) {
562 if (ctxt->use_gdi_natural)
563 ok_(__FILE__, line)(mode == DWRITE_MEASURING_MODE_GDI_NATURAL, "got %d\n", mode);
564 else
565 ok_(__FILE__, line)(mode == DWRITE_MEASURING_MODE_GDI_CLASSIC, "got %d\n", mode);
567 else
568 ok_(__FILE__, line)(mode == DWRITE_MEASURING_MODE_NATURAL, "got %d\n", mode);
571 static HRESULT WINAPI testrenderer_DrawGlyphRun(IDWriteTextRenderer *iface,
572 void *context,
573 FLOAT baselineOriginX,
574 FLOAT baselineOriginY,
575 DWRITE_MEASURING_MODE mode,
576 DWRITE_GLYPH_RUN const *run,
577 DWRITE_GLYPH_RUN_DESCRIPTION const *descr,
578 IUnknown *effect)
580 struct renderer_context *ctxt = (struct renderer_context*)context;
581 struct drawcall_entry entry;
582 DWRITE_SCRIPT_ANALYSIS sa;
584 if (ctxt) {
585 TEST_MEASURING_MODE(ctxt, mode);
586 ctxt->originX = baselineOriginX;
587 ctxt->originY = baselineOriginY;
590 ok(descr->stringLength < ARRAY_SIZE(entry.string), "string is too long\n");
591 if (descr->stringLength && descr->stringLength < ARRAY_SIZE(entry.string)) {
592 memcpy(entry.string, descr->string, descr->stringLength*sizeof(WCHAR));
593 entry.string[descr->stringLength] = 0;
595 else
596 entry.string[0] = 0;
598 /* see what's reported for control codes runs */
599 get_script_analysis(descr->string, descr->stringLength, &sa);
600 if (sa.shapes == DWRITE_SCRIPT_SHAPES_NO_VISUAL) {
601 UINT32 i;
603 /* glyphs are not reported at all for control code runs */
604 ok(run->glyphCount == 0, "got %u\n", run->glyphCount);
605 ok(run->glyphAdvances != NULL, "advances array %p\n", run->glyphAdvances);
606 ok(run->glyphOffsets != NULL, "offsets array %p\n", run->glyphOffsets);
607 ok(run->fontFace != NULL, "got %p\n", run->fontFace);
608 /* text positions are still valid */
609 ok(descr->string != NULL, "got string %p\n", descr->string);
610 ok(descr->stringLength > 0, "got string length %u\n", descr->stringLength);
611 ok(descr->clusterMap != NULL, "clustermap %p\n", descr->clusterMap);
612 for (i = 0; i < descr->stringLength; i++)
613 ok(descr->clusterMap[i] == i, "got %u\n", descr->clusterMap[i]);
616 entry.kind = DRAW_GLYPHRUN;
617 if (effect)
618 entry.kind |= DRAW_EFFECT;
619 ok(lstrlenW(descr->localeName) < LOCALE_NAME_MAX_LENGTH, "unexpectedly long locale name\n");
620 lstrcpyW(entry.locale, descr->localeName);
621 entry.glyphcount = run->glyphCount;
622 entry.bidilevel = run->bidiLevel;
623 add_call(sequences, RENDERER_ID, &entry);
624 return S_OK;
627 static HRESULT WINAPI testrenderer_DrawUnderline(IDWriteTextRenderer *iface,
628 void *context,
629 FLOAT baselineOriginX,
630 FLOAT baselineOriginY,
631 DWRITE_UNDERLINE const* underline,
632 IUnknown *effect)
634 struct renderer_context *ctxt = (struct renderer_context*)context;
635 struct drawcall_entry entry = { 0 };
637 if (ctxt)
638 TEST_MEASURING_MODE(ctxt, underline->measuringMode);
640 ok(underline->runHeight > 0.0f, "Expected non-zero run height\n");
641 if (ctxt && ctxt->format) {
642 DWRITE_FONT_METRICS metrics;
643 IDWriteFontFace *fontface;
644 FLOAT emsize;
646 fontface = get_fontface_from_format(ctxt->format);
647 emsize = IDWriteTextFormat_GetFontSize(ctxt->format);
648 IDWriteFontFace_GetMetrics(fontface, &metrics);
650 ok(emsize == metrics.designUnitsPerEm, "Unexpected font size %f\n", emsize);
651 /* Expected height is in design units, allow some absolute difference from it. Seems to only happen on Vista */
652 ok(fabs(metrics.capHeight - underline->runHeight) < 2.0f, "Expected runHeight %u, got %f, family %s\n",
653 metrics.capHeight, underline->runHeight, wine_dbgstr_w(ctxt->familyW));
655 IDWriteFontFace_Release(fontface);
658 entry.kind = DRAW_UNDERLINE;
659 if (effect)
660 entry.kind |= DRAW_EFFECT;
661 lstrcpyW(entry.locale, underline->localeName);
662 add_call(sequences, RENDERER_ID, &entry);
663 return S_OK;
666 static HRESULT WINAPI testrenderer_DrawStrikethrough(IDWriteTextRenderer *iface,
667 void *context,
668 FLOAT baselineOriginX,
669 FLOAT baselineOriginY,
670 DWRITE_STRIKETHROUGH const* strikethrough,
671 IUnknown *effect)
673 struct renderer_context *ctxt = (struct renderer_context*)context;
674 struct drawcall_entry entry = { 0 };
676 if (ctxt)
677 TEST_MEASURING_MODE(ctxt, strikethrough->measuringMode);
679 entry.kind = DRAW_STRIKETHROUGH;
680 if (effect)
681 entry.kind |= DRAW_EFFECT;
682 add_call(sequences, RENDERER_ID, &entry);
683 return S_OK;
686 static HRESULT WINAPI testrenderer_DrawInlineObject(IDWriteTextRenderer *iface,
687 void *context,
688 FLOAT originX,
689 FLOAT originY,
690 IDWriteInlineObject *object,
691 BOOL is_sideways,
692 BOOL is_rtl,
693 IUnknown *effect)
695 struct drawcall_entry entry = { 0 };
696 entry.kind = DRAW_INLINE;
697 if (effect)
698 entry.kind |= DRAW_EFFECT;
699 add_call(sequences, RENDERER_ID, &entry);
700 return S_OK;
703 static const IDWriteTextRendererVtbl testrenderervtbl = {
704 testrenderer_QI,
705 testrenderer_AddRef,
706 testrenderer_Release,
707 testrenderer_IsPixelSnappingDisabled,
708 testrenderer_GetCurrentTransform,
709 testrenderer_GetPixelsPerDip,
710 testrenderer_DrawGlyphRun,
711 testrenderer_DrawUnderline,
712 testrenderer_DrawStrikethrough,
713 testrenderer_DrawInlineObject
716 static IDWriteTextRenderer testrenderer = { &testrenderervtbl };
718 /* test IDWriteInlineObject */
719 static HRESULT WINAPI testinlineobj_QI(IDWriteInlineObject *iface, REFIID riid, void **obj)
721 if (IsEqualIID(riid, &IID_IDWriteInlineObject) || IsEqualIID(riid, &IID_IUnknown)) {
722 *obj = iface;
723 IDWriteInlineObject_AddRef(iface);
724 return S_OK;
727 *obj = NULL;
728 return E_NOINTERFACE;
731 static ULONG WINAPI testinlineobj_AddRef(IDWriteInlineObject *iface)
733 return 2;
736 static ULONG WINAPI testinlineobj_Release(IDWriteInlineObject *iface)
738 return 1;
741 static HRESULT WINAPI testinlineobj_Draw(IDWriteInlineObject *iface,
742 void* client_drawingontext, IDWriteTextRenderer* renderer,
743 FLOAT originX, FLOAT originY, BOOL is_sideways, BOOL is_rtl, IUnknown *drawing_effect)
745 ok(0, "unexpected call\n");
746 return E_NOTIMPL;
749 static HRESULT WINAPI testinlineobj_GetMetrics(IDWriteInlineObject *iface, DWRITE_INLINE_OBJECT_METRICS *metrics)
751 metrics->width = 123.0;
752 return 0x8faecafe;
755 static HRESULT WINAPI testinlineobj_GetOverhangMetrics(IDWriteInlineObject *iface, DWRITE_OVERHANG_METRICS *overhangs)
757 ok(0, "unexpected call\n");
758 return E_NOTIMPL;
761 static HRESULT WINAPI testinlineobj_GetBreakConditions(IDWriteInlineObject *iface, DWRITE_BREAK_CONDITION *before,
762 DWRITE_BREAK_CONDITION *after)
764 *before = *after = DWRITE_BREAK_CONDITION_MUST_BREAK;
765 return 0x8feacafe;
768 static HRESULT WINAPI testinlineobj2_GetBreakConditions(IDWriteInlineObject *iface, DWRITE_BREAK_CONDITION *before,
769 DWRITE_BREAK_CONDITION *after)
771 *before = *after = DWRITE_BREAK_CONDITION_MAY_NOT_BREAK;
772 return S_OK;
775 static IDWriteInlineObjectVtbl testinlineobjvtbl = {
776 testinlineobj_QI,
777 testinlineobj_AddRef,
778 testinlineobj_Release,
779 testinlineobj_Draw,
780 testinlineobj_GetMetrics,
781 testinlineobj_GetOverhangMetrics,
782 testinlineobj_GetBreakConditions
785 static IDWriteInlineObjectVtbl testinlineobjvtbl2 = {
786 testinlineobj_QI,
787 testinlineobj_AddRef,
788 testinlineobj_Release,
789 testinlineobj_Draw,
790 testinlineobj_GetMetrics,
791 testinlineobj_GetOverhangMetrics,
792 testinlineobj2_GetBreakConditions
795 static IDWriteInlineObject testinlineobj = { &testinlineobjvtbl };
796 static IDWriteInlineObject testinlineobj2 = { &testinlineobjvtbl };
797 static IDWriteInlineObject testinlineobj3 = { &testinlineobjvtbl2 };
799 struct test_inline_obj
801 IDWriteInlineObject IDWriteInlineObject_iface;
802 DWRITE_INLINE_OBJECT_METRICS metrics;
803 DWRITE_OVERHANG_METRICS overhangs;
806 static inline struct test_inline_obj *impl_from_IDWriteInlineObject(IDWriteInlineObject *iface)
808 return CONTAINING_RECORD(iface, struct test_inline_obj, IDWriteInlineObject_iface);
811 static HRESULT WINAPI testinlineobj3_GetMetrics(IDWriteInlineObject *iface, DWRITE_INLINE_OBJECT_METRICS *metrics)
813 struct test_inline_obj *obj = impl_from_IDWriteInlineObject(iface);
814 *metrics = obj->metrics;
815 return S_OK;
818 static HRESULT WINAPI testinlineobj3_GetOverhangMetrics(IDWriteInlineObject *iface, DWRITE_OVERHANG_METRICS *overhangs)
820 struct test_inline_obj *obj = impl_from_IDWriteInlineObject(iface);
821 *overhangs = obj->overhangs;
822 /* Return value is ignored. */
823 return E_NOTIMPL;
826 static const IDWriteInlineObjectVtbl testinlineobjvtbl3 = {
827 testinlineobj_QI,
828 testinlineobj_AddRef,
829 testinlineobj_Release,
830 testinlineobj_Draw,
831 testinlineobj3_GetMetrics,
832 testinlineobj3_GetOverhangMetrics,
833 testinlineobj_GetBreakConditions,
836 static void test_inline_obj_init(struct test_inline_obj *obj, const DWRITE_INLINE_OBJECT_METRICS *metrics,
837 const DWRITE_OVERHANG_METRICS *overhangs)
839 obj->IDWriteInlineObject_iface.lpVtbl = &testinlineobjvtbl3;
840 obj->metrics = *metrics;
841 obj->overhangs = *overhangs;
844 struct test_effect
846 IUnknown IUnknown_iface;
847 LONG ref;
850 static inline struct test_effect *test_effect_from_IUnknown(IUnknown *iface)
852 return CONTAINING_RECORD(iface, struct test_effect, IUnknown_iface);
855 static HRESULT WINAPI testeffect_QI(IUnknown *iface, REFIID riid, void **obj)
857 if (IsEqualIID(riid, &IID_IUnknown)) {
858 *obj = iface;
859 IUnknown_AddRef(iface);
860 return S_OK;
863 ok(0, "Unexpected riid %s.\n", wine_dbgstr_guid(riid));
864 *obj = NULL;
865 return E_NOINTERFACE;
868 static ULONG WINAPI testeffect_AddRef(IUnknown *iface)
870 struct test_effect *effect = test_effect_from_IUnknown(iface);
871 return InterlockedIncrement(&effect->ref);
874 static ULONG WINAPI testeffect_Release(IUnknown *iface)
876 struct test_effect *effect = test_effect_from_IUnknown(iface);
877 LONG ref = InterlockedDecrement(&effect->ref);
879 if (!ref)
880 free(effect);
882 return ref;
885 static const IUnknownVtbl testeffectvtbl = {
886 testeffect_QI,
887 testeffect_AddRef,
888 testeffect_Release
891 static IUnknown *create_test_effect(void)
893 struct test_effect *effect;
895 effect = calloc(1, sizeof(*effect));
896 effect->IUnknown_iface.lpVtbl = &testeffectvtbl;
897 effect->ref = 1;
899 return &effect->IUnknown_iface;
902 static void test_CreateTextLayout(void)
904 IDWriteTextLayout4 *layout4;
905 IDWriteTextLayout2 *layout2 = NULL;
906 IDWriteTextLayout *layout;
907 IDWriteTextFormat3 *format3;
908 IDWriteTextFormat *format;
909 IDWriteFactory *factory;
910 HRESULT hr;
912 factory = create_factory();
914 layout = (void*)0xdeadbeef;
915 hr = IDWriteFactory_CreateTextLayout(factory, NULL, 0, NULL, 0.0f, 0.0f, &layout);
916 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
917 ok(layout == NULL, "got %p\n", layout);
919 layout = (void*)0xdeadbeef;
920 hr = IDWriteFactory_CreateTextLayout(factory, L"string", 6, NULL, 0.0f, 0.0f, &layout);
921 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
922 ok(layout == NULL, "got %p\n", layout);
924 layout = (void*)0xdeadbeef;
925 hr = IDWriteFactory_CreateTextLayout(factory, L"string", 6, NULL, 1.0f, 0.0f, &layout);
926 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
927 ok(layout == NULL, "got %p\n", layout);
929 layout = (void*)0xdeadbeef;
930 hr = IDWriteFactory_CreateTextLayout(factory, L"string", 6, NULL, 0.0f, 1.0f, &layout);
931 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
932 ok(layout == NULL, "got %p\n", layout);
934 layout = (void*)0xdeadbeef;
935 hr = IDWriteFactory_CreateTextLayout(factory, L"string", 6, NULL, 1000.0f, 1000.0f, &layout);
936 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
937 ok(layout == NULL, "got %p\n", layout);
939 hr = IDWriteFactory_CreateTextFormat(factory, L"Tahoma", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
940 DWRITE_FONT_STRETCH_NORMAL, 10.0f, L"en-us", &format);
941 ok(hr == S_OK, "Failed to create format, hr %#lx.\n", hr);
943 layout = (void*)0xdeadbeef;
944 hr = IDWriteFactory_CreateTextLayout(factory, NULL, 0, format, 100.0f, 100.0f, &layout);
945 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
946 ok(layout == NULL, "got %p\n", layout);
948 layout = (void *)0xdeadbeef;
949 hr = IDWriteFactory_CreateTextLayout(factory, L"string", 6, format, -100.0f, 100.0f, &layout);
950 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
951 ok(layout == NULL, "Unexpected pointer %p.\n", layout);
953 layout = (void *)0xdeadbeef;
954 hr = IDWriteFactory_CreateTextLayout(factory, L"string", 6, format, 100.0f, -100.0f, &layout);
955 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
956 ok(layout == NULL, "Unexpected pointer %p.\n", layout);
958 layout = (void *)0xdeadbeef;
959 hr = IDWriteFactory_CreateTextLayout(factory, L"string", 6, format, -100.0f, -100.0f, &layout);
960 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
961 ok(layout == NULL, "Unexpected pointer %p.\n", layout);
963 hr = IDWriteFactory_CreateTextLayout(factory, L"string", 0, format, 0.0f, 0.0f, &layout);
964 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
965 IDWriteTextLayout_Release(layout);
967 EXPECT_REF(format, 1);
968 hr = IDWriteFactory_CreateTextLayout(factory, L"string", 6, format, 1000.0f, 1000.0f, &layout);
969 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
970 EXPECT_REF(format, 1);
972 hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextLayout2, (void**)&layout2);
973 if (hr == S_OK) {
974 IDWriteTextLayout1 *layout1;
975 IDWriteTextFormat1 *format1;
976 IDWriteTextFormat *format;
978 hr = IDWriteTextLayout2_QueryInterface(layout2, &IID_IDWriteTextLayout1, (void**)&layout1);
979 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
980 IDWriteTextLayout1_Release(layout1);
982 EXPECT_REF(layout2, 2);
983 hr = IDWriteTextLayout2_QueryInterface(layout2, &IID_IDWriteTextFormat1, (void**)&format1);
984 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
985 EXPECT_REF(layout2, 3);
987 hr = IDWriteTextLayout2_QueryInterface(layout2, &IID_IDWriteTextFormat, (void**)&format);
988 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
989 ok(format == (IDWriteTextFormat*)format1, "got %p, %p\n", format, format1);
990 ok(format != (IDWriteTextFormat*)layout2, "got %p, %p\n", format, layout2);
991 EXPECT_REF(layout2, 4);
993 hr = IDWriteTextFormat_QueryInterface(format, &IID_IDWriteTextLayout1, (void**)&layout1);
994 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
995 IDWriteTextLayout1_Release(layout1);
997 IDWriteTextFormat1_Release(format1);
998 IDWriteTextFormat_Release(format);
1000 hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextFormat1, (void**)&format1);
1001 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1002 EXPECT_REF(layout2, 3);
1004 hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextFormat, (void**)&format);
1005 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1006 ok(format == (IDWriteTextFormat*)format1, "got %p, %p\n", format, format1);
1007 EXPECT_REF(layout2, 4);
1009 IDWriteTextFormat1_Release(format1);
1010 IDWriteTextFormat_Release(format);
1012 else
1013 win_skip("IDWriteTextLayout2 is not supported.\n");
1015 if (layout2 && SUCCEEDED(IDWriteTextLayout2_QueryInterface(layout2, &IID_IDWriteTextLayout4, (void **)&layout4)))
1017 hr = IDWriteTextLayout4_QueryInterface(layout4, &IID_IDWriteTextFormat3, (void **)&format3);
1018 ok(hr == S_OK, "Failed to get text format, hr %#lx.\n", hr);
1019 IDWriteTextFormat3_Release(format3);
1021 else
1022 win_skip("IDWriteTextLayout4 is not supported.\n");
1024 if (layout2)
1025 IDWriteTextLayout2_Release(layout2);
1026 IDWriteTextLayout_Release(layout);
1027 IDWriteTextFormat_Release(format);
1028 IDWriteFactory_Release(factory);
1031 static DWRITE_MATRIX layoutcreate_transforms[] = {
1032 { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 },
1033 { 1.0, 0.0, 0.0, 1.0, 0.3, 0.2 },
1034 { 1.0, 0.0, 0.0, 1.0,-0.3,-0.2 },
1036 { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 },
1037 { 1.0, 0.0, 0.0, 0.0, 0.0, 0.0 },
1038 { 1.0, 2.0, 0.5, 1.0, 0.0, 0.0 },
1041 static void test_CreateGdiCompatibleTextLayout(void)
1043 IDWriteTextLayout *layout;
1044 IDWriteTextFormat *format;
1045 IDWriteFactory *factory;
1046 FLOAT dimension;
1047 HRESULT hr;
1048 int i;
1050 factory = create_factory();
1052 layout = (void*)0xdeadbeef;
1053 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, NULL, 0, NULL, 0.0f, 0.0f, 0.0f, NULL, FALSE, &layout);
1054 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
1055 ok(layout == NULL, "got %p\n", layout);
1057 layout = (void*)0xdeadbeef;
1058 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, L"string", 6, NULL, 0.0f, 0.0f, 0.0f, NULL,
1059 FALSE, &layout);
1060 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
1061 ok(layout == NULL, "got %p\n", layout);
1063 layout = (void*)0xdeadbeef;
1064 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, L"string", 6, NULL, 1.0f, 0.0f, 0.0f, NULL,
1065 FALSE, &layout);
1066 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
1067 ok(layout == NULL, "got %p\n", layout);
1069 layout = (void*)0xdeadbeef;
1070 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, L"string", 6, NULL, 1.0f, 0.0f, 1.0f, NULL,
1071 FALSE, &layout);
1072 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
1073 ok(layout == NULL, "got %p\n", layout);
1075 layout = (void*)0xdeadbeef;
1076 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, L"string", 6, NULL, 1000.0f, 1000.0f, 1.0f, NULL,
1077 FALSE, &layout);
1078 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
1079 ok(layout == NULL, "got %p\n", layout);
1081 /* create with text format */
1082 hr = IDWriteFactory_CreateTextFormat(factory, L"Tahoma", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
1083 DWRITE_FONT_STRETCH_NORMAL, 10.0f, L"en-us", &format);
1084 ok(hr == S_OK, "Failed to create text format, hr %#lx.\n", hr);
1085 EXPECT_REF(format, 1);
1087 layout = (void*)0xdeadbeef;
1088 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, NULL, 0, format, 100.0f, 100.0f, 1.0f, NULL, FALSE, &layout);
1089 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
1090 ok(layout == NULL, "got %p\n", layout);
1092 layout = (void *)0xdeadbeef;
1093 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, L"string", 6, format, -100.0f, 100.0f, 1.0f,
1094 NULL, FALSE, &layout);
1095 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
1096 ok(layout == NULL, "Unexpected pointer %p.\n", layout);
1098 layout = (void *)0xdeadbeef;
1099 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, L"string", 6, format, 100.0f, -100.0f, 1.0f,
1100 NULL, FALSE, &layout);
1101 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
1102 ok(layout == NULL, "Unexpected pointer %p.\n", layout);
1104 layout = (void *)0xdeadbeef;
1105 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, L"string", 6, format, -100.0f, -100.0f, 1.0f,
1106 NULL, FALSE, &layout);
1107 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
1108 ok(layout == NULL, "Unexpected pointer %p.\n", layout);
1110 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, L"string", 6, format, 100.0f, 100.0f, 1.0f, NULL,
1111 FALSE, &layout);
1112 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
1113 EXPECT_REF(format, 1);
1114 EXPECT_REF(layout, 1);
1116 IDWriteTextLayout_AddRef(layout);
1117 EXPECT_REF(format, 1);
1118 EXPECT_REF(layout, 2);
1119 IDWriteTextLayout_Release(layout);
1120 IDWriteTextLayout_Release(layout);
1122 /* zero length string is okay */
1123 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, L"string", 0, format, 100.0f, 100.0f, 1.0f, NULL,
1124 FALSE, &layout);
1125 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
1127 dimension = IDWriteTextLayout_GetMaxWidth(layout);
1128 ok(dimension == 100.0, "got %f\n", dimension);
1130 dimension = IDWriteTextLayout_GetMaxHeight(layout);
1131 ok(dimension == 100.0, "got %f\n", dimension);
1133 IDWriteTextLayout_Release(layout);
1135 /* negative, zero ppdip */
1136 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, L"string", 1, format, 100.0f, 100.0f, -1.0f, NULL,
1137 FALSE, &layout);
1138 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
1139 IDWriteTextLayout_Release(layout);
1141 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, L"string", 1, format, 100.0f, 100.0f, 0.0f, NULL,
1142 FALSE, &layout);
1143 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
1144 IDWriteTextLayout_Release(layout);
1146 /* transforms */
1147 for (i = 0; i < ARRAY_SIZE(layoutcreate_transforms); ++i)
1149 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, L"string", 1, format, 100.0f, 100.0f, 1.0f,
1150 &layoutcreate_transforms[i], FALSE, &layout);
1151 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
1152 IDWriteTextLayout_Release(layout);
1155 IDWriteTextFormat_Release(format);
1156 IDWriteFactory_Release(factory);
1159 static void test_CreateTextFormat(void)
1161 IDWriteFontCollection *collection, *syscoll;
1162 DWRITE_PARAGRAPH_ALIGNMENT paralign;
1163 DWRITE_READING_DIRECTION readdir;
1164 DWRITE_WORD_WRAPPING wrapping;
1165 DWRITE_TEXT_ALIGNMENT align;
1166 DWRITE_FLOW_DIRECTION flow;
1167 DWRITE_LINE_SPACING_METHOD method;
1168 DWRITE_TRIMMING trimming;
1169 IDWriteTextFormat *format;
1170 FLOAT spacing, baseline;
1171 IDWriteInlineObject *trimmingsign;
1172 IDWriteFactory *factory;
1173 HRESULT hr;
1175 factory = create_factory();
1177 /* zero/negative font size */
1178 hr = IDWriteFactory_CreateTextFormat(factory, L"Tahoma", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
1179 DWRITE_FONT_STRETCH_NORMAL, 0.0f, L"en-us", &format);
1180 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
1182 hr = IDWriteFactory_CreateTextFormat(factory, L"Tahoma", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
1183 DWRITE_FONT_STRETCH_NORMAL, -10.0f, L"en-us", &format);
1184 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
1186 /* invalid font properties */
1187 hr = IDWriteFactory_CreateTextFormat(factory, L"Tahoma", NULL, 1000, DWRITE_FONT_STYLE_NORMAL,
1188 DWRITE_FONT_STRETCH_NORMAL, 10.0f, L"en-us", &format);
1189 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
1191 hr = IDWriteFactory_CreateTextFormat(factory, L"Tahoma", NULL, DWRITE_FONT_WEIGHT_NORMAL,
1192 DWRITE_FONT_STYLE_ITALIC + 1, DWRITE_FONT_STRETCH_NORMAL, 10.0f, L"en-us", &format);
1193 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
1195 hr = IDWriteFactory_CreateTextFormat(factory, L"Tahoma", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_ITALIC,
1196 10, 10.0f, L"en-us", &format);
1197 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
1199 /* empty family name */
1200 hr = IDWriteFactory_CreateTextFormat(factory, L"", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
1201 DWRITE_FONT_STRETCH_NORMAL, 10.0f, L"en-us", &format);
1202 ok(hr == S_OK, "Failed to create text format, hr %#lx.\n", hr);
1203 IDWriteTextFormat_Release(format);
1205 hr = IDWriteFactory_CreateTextFormat(factory, L"Tahoma", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
1206 DWRITE_FONT_STRETCH_NORMAL, 10.0f, L"en-us", &format);
1207 ok(hr == S_OK, "Failed to create text format, hr %#lx.\n", hr);
1209 if (0) /* crashes on native */
1210 hr = IDWriteTextFormat_GetFontCollection(format, NULL);
1212 collection = NULL;
1213 hr = IDWriteTextFormat_GetFontCollection(format, &collection);
1214 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1215 ok(collection != NULL, "got %p\n", collection);
1217 hr = IDWriteFactory_GetSystemFontCollection(factory, &syscoll, FALSE);
1218 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1219 ok(collection == syscoll, "got %p, was %p\n", syscoll, collection);
1220 IDWriteFontCollection_Release(syscoll);
1221 IDWriteFontCollection_Release(collection);
1223 /* default format properties */
1224 align = IDWriteTextFormat_GetTextAlignment(format);
1225 ok(align == DWRITE_TEXT_ALIGNMENT_LEADING, "got %d\n", align);
1227 paralign = IDWriteTextFormat_GetParagraphAlignment(format);
1228 ok(paralign == DWRITE_PARAGRAPH_ALIGNMENT_NEAR, "got %d\n", paralign);
1230 wrapping = IDWriteTextFormat_GetWordWrapping(format);
1231 ok(wrapping == DWRITE_WORD_WRAPPING_WRAP, "got %d\n", wrapping);
1233 readdir = IDWriteTextFormat_GetReadingDirection(format);
1234 ok(readdir == DWRITE_READING_DIRECTION_LEFT_TO_RIGHT, "got %d\n", readdir);
1236 flow = IDWriteTextFormat_GetFlowDirection(format);
1237 ok(flow == DWRITE_FLOW_DIRECTION_TOP_TO_BOTTOM, "got %d\n", flow);
1239 hr = IDWriteTextFormat_GetLineSpacing(format, &method, &spacing, &baseline);
1240 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1241 ok(spacing == 0.0, "got %f\n", spacing);
1242 ok(baseline == 0.0, "got %f\n", baseline);
1243 ok(method == DWRITE_LINE_SPACING_METHOD_DEFAULT, "got %d\n", method);
1245 trimming.granularity = DWRITE_TRIMMING_GRANULARITY_WORD;
1246 trimming.delimiter = 10;
1247 trimming.delimiterCount = 10;
1248 trimmingsign = (void*)0xdeadbeef;
1249 hr = IDWriteTextFormat_GetTrimming(format, &trimming, &trimmingsign);
1250 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1251 ok(trimming.granularity == DWRITE_TRIMMING_GRANULARITY_NONE, "got %d\n", trimming.granularity);
1252 ok(trimming.delimiter == 0, "got %d\n", trimming.delimiter);
1253 ok(trimming.delimiterCount == 0, "got %d\n", trimming.delimiterCount);
1254 ok(trimmingsign == NULL, "got %p\n", trimmingsign);
1256 /* setters */
1257 hr = IDWriteTextFormat_SetTextAlignment(format, DWRITE_TEXT_ALIGNMENT_LEADING);
1258 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1260 hr = IDWriteTextFormat_SetTextAlignment(format, DWRITE_TEXT_ALIGNMENT_JUSTIFIED+1);
1261 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
1263 hr = IDWriteTextFormat_SetParagraphAlignment(format, DWRITE_PARAGRAPH_ALIGNMENT_NEAR);
1264 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1266 hr = IDWriteTextFormat_SetParagraphAlignment(format, DWRITE_PARAGRAPH_ALIGNMENT_CENTER+1);
1267 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
1269 hr = IDWriteTextFormat_SetWordWrapping(format, DWRITE_WORD_WRAPPING_WRAP);
1270 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1272 hr = IDWriteTextFormat_SetWordWrapping(format, DWRITE_WORD_WRAPPING_CHARACTER+1);
1273 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
1275 hr = IDWriteTextFormat_SetReadingDirection(format, DWRITE_READING_DIRECTION_LEFT_TO_RIGHT);
1276 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1278 hr = IDWriteTextFormat_SetFlowDirection(format, DWRITE_FLOW_DIRECTION_TOP_TO_BOTTOM);
1279 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1282 hr = IDWriteTextFormat_SetTrimming(format, &trimming, NULL);
1283 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1285 /* invalid granularity */
1286 trimming.granularity = 10;
1287 trimming.delimiter = 0;
1288 trimming.delimiterCount = 0;
1289 hr = IDWriteTextFormat_SetTrimming(format, &trimming, NULL);
1290 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
1292 IDWriteTextFormat_Release(format);
1293 IDWriteFactory_Release(factory);
1296 static void test_GetLocaleName(void)
1298 IDWriteTextFormat *format, *format2;
1299 IDWriteTextLayout *layout;
1300 IDWriteFactory *factory;
1301 WCHAR buff[10];
1302 UINT32 len;
1303 HRESULT hr;
1305 factory = create_factory();
1307 hr = IDWriteFactory_CreateTextFormat(factory, L"Tahoma", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
1308 DWRITE_FONT_STRETCH_NORMAL, 10.0f, L"ru", &format);
1309 ok(hr == S_OK, "Failed to create text format, hr %#lx.\n", hr);
1311 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, L"string", 0, format, 100.0f, 100.0f, 1.0f, NULL,
1312 FALSE, &layout);
1313 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
1315 hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextFormat, (void**)&format2);
1316 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1318 len = IDWriteTextFormat_GetLocaleNameLength(format2);
1319 ok(len == 2, "got %u\n", len);
1320 len = IDWriteTextFormat_GetLocaleNameLength(format);
1321 ok(len == 2, "got %u\n", len);
1322 hr = IDWriteTextFormat_GetLocaleName(format2, buff, len);
1323 ok(hr == E_NOT_SUFFICIENT_BUFFER, "Unexpected hr %#lx.\n", hr);
1324 hr = IDWriteTextFormat_GetLocaleName(format2, buff, len+1);
1325 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1326 ok(!lstrcmpW(buff, L"ru"), "Unexpected locale name %s.\n", wine_dbgstr_w(buff));
1327 hr = IDWriteTextFormat_GetLocaleName(format, buff, len);
1328 ok(hr == E_NOT_SUFFICIENT_BUFFER, "Unexpected hr %#lx.\n", hr);
1329 hr = IDWriteTextFormat_GetLocaleName(format, buff, len+1);
1330 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1331 ok(!lstrcmpW(buff, L"ru"), "Unexpected locale name %s.\n", wine_dbgstr_w(buff));
1333 IDWriteTextLayout_Release(layout);
1334 IDWriteTextFormat_Release(format);
1335 IDWriteTextFormat_Release(format2);
1336 IDWriteFactory_Release(factory);
1339 static const struct drawcall_entry drawellipsis_seq[] = {
1340 { DRAW_GLYPHRUN, {0x2026, 0}, {'e','n','-','g','b',0}, 1 },
1341 { DRAW_LAST_KIND }
1344 static void test_CreateEllipsisTrimmingSign(void)
1346 DWRITE_INLINE_OBJECT_METRICS metrics;
1347 DWRITE_BREAK_CONDITION before, after;
1348 struct renderer_context ctxt;
1349 IDWriteTextFormat *format;
1350 IDWriteInlineObject *sign;
1351 IDWriteFactory *factory;
1352 IUnknown *unk, *effect;
1353 HRESULT hr;
1355 factory = create_factory();
1357 hr = IDWriteFactory_CreateTextFormat(factory, L"Tahoma", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
1358 DWRITE_FONT_STRETCH_NORMAL, 10.0f, L"en-GB", &format);
1359 ok(hr == S_OK, "Failed to create text format, hr %#lx.\n", hr);
1361 sign = (void *)0xdeadbeef;
1362 hr = IDWriteFactory_CreateEllipsisTrimmingSign(factory, NULL, &sign);
1363 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
1364 ok(!sign, "Unexpected pointer %p.\n", sign);
1366 EXPECT_REF(format, 1);
1367 hr = IDWriteFactory_CreateEllipsisTrimmingSign(factory, format, &sign);
1368 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1369 EXPECT_REF(format, 1);
1371 hr = IDWriteInlineObject_QueryInterface(sign, &IID_IDWriteTextLayout, (void**)&unk);
1372 ok(hr == E_NOINTERFACE, "Unexpected hr %#lx.\n", hr);
1374 if (0) {/* crashes on native */
1375 hr = IDWriteInlineObject_GetBreakConditions(sign, NULL, NULL);
1376 hr = IDWriteInlineObject_GetMetrics(sign, NULL);
1378 metrics.width = 0.0;
1379 metrics.height = 123.0;
1380 metrics.baseline = 123.0;
1381 metrics.supportsSideways = TRUE;
1382 hr = IDWriteInlineObject_GetMetrics(sign, &metrics);
1383 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1384 ok(metrics.width > 0.0, "got %.2f\n", metrics.width);
1385 ok(metrics.height == 0.0, "got %.2f\n", metrics.height);
1386 ok(metrics.baseline == 0.0, "got %.2f\n", metrics.baseline);
1387 ok(!metrics.supportsSideways, "got %d\n", metrics.supportsSideways);
1389 before = after = DWRITE_BREAK_CONDITION_CAN_BREAK;
1390 hr = IDWriteInlineObject_GetBreakConditions(sign, &before, &after);
1391 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1392 ok(before == DWRITE_BREAK_CONDITION_NEUTRAL, "got %d\n", before);
1393 ok(after == DWRITE_BREAK_CONDITION_NEUTRAL, "got %d\n", after);
1395 /* Draw tests */
1396 flush_sequence(sequences, RENDERER_ID);
1397 hr = IDWriteInlineObject_Draw(sign, NULL, &testrenderer, 0.0, 0.0, FALSE, FALSE, NULL);
1398 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1399 ok_sequence(sequences, RENDERER_ID, drawellipsis_seq, "ellipsis sign draw test", FALSE);
1401 effect = create_test_effect();
1403 EXPECT_REF(effect, 1);
1404 flush_sequence(sequences, RENDERER_ID);
1405 hr = IDWriteInlineObject_Draw(sign, NULL, &testrenderer, 0.0f, 0.0f, FALSE, FALSE, effect);
1406 ok(hr == S_OK, "Failed to draw trimming sign, hr %#lx.\n", hr);
1407 ok_sequence(sequences, RENDERER_ID, drawellipsis_seq, "ellipsis sign draw with effect test", FALSE);
1408 EXPECT_REF(effect, 1);
1410 IUnknown_Release(effect);
1412 flush_sequence(sequences, RENDERER_ID);
1413 hr = IDWriteInlineObject_Draw(sign, NULL, &testrenderer, 0.0f, 0.0f, FALSE, FALSE, (void *)0xdeadbeef);
1414 ok(hr == S_OK, "Failed to draw trimming sign, hr %#lx.\n", hr);
1415 ok_sequence(sequences, RENDERER_ID, drawellipsis_seq, "ellipsis sign draw with effect test", FALSE);
1417 memset(&ctxt, 0, sizeof(ctxt));
1418 hr = IDWriteInlineObject_Draw(sign, &ctxt, &testrenderer, 123.0f, 456.0f, FALSE, FALSE, NULL);
1419 ok(hr == S_OK, "Failed to draw trimming sign, hr %#lx.\n", hr);
1420 ok(ctxt.originX == 123.0f && ctxt.originY == 456.0f, "Unexpected drawing origin\n");
1422 IDWriteInlineObject_Release(sign);
1424 /* Centered format */
1425 hr = IDWriteTextFormat_SetTextAlignment(format, DWRITE_TEXT_ALIGNMENT_CENTER);
1426 ok(hr == S_OK, "Failed to set text alignment, hr %#lx.\n", hr);
1428 hr = IDWriteFactory_CreateEllipsisTrimmingSign(factory, format, &sign);
1429 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1431 memset(&ctxt, 0, sizeof(ctxt));
1432 hr = IDWriteInlineObject_Draw(sign, &ctxt, &testrenderer, 123.0f, 456.0f, FALSE, FALSE, NULL);
1433 ok(hr == S_OK, "Failed to draw trimming sign, hr %#lx.\n", hr);
1434 ok(ctxt.originX == 123.0f && ctxt.originY == 456.0f, "Unexpected drawing origin\n");
1436 IDWriteInlineObject_Release(sign);
1438 /* non-orthogonal flow/reading combination */
1439 hr = IDWriteTextFormat_SetReadingDirection(format, DWRITE_READING_DIRECTION_LEFT_TO_RIGHT);
1440 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1442 hr = IDWriteTextFormat_SetFlowDirection(format, DWRITE_FLOW_DIRECTION_LEFT_TO_RIGHT);
1443 ok(hr == S_OK || broken(hr == E_INVALIDARG) /* vista, win7 */, "Unexpected hr %#lx.\n", hr);
1444 if (hr == S_OK) {
1445 hr = IDWriteFactory_CreateEllipsisTrimmingSign(factory, format, &sign);
1446 ok(hr == DWRITE_E_FLOWDIRECTIONCONFLICTS, "Unexpected hr %#lx.\n", hr);
1449 IDWriteTextFormat_Release(format);
1450 IDWriteFactory_Release(factory);
1453 static void test_fontweight(void)
1455 IDWriteTextFormat *format, *fmt2;
1456 IDWriteTextLayout *layout;
1457 DWRITE_FONT_WEIGHT weight;
1458 DWRITE_TEXT_RANGE range;
1459 IDWriteFactory *factory;
1460 FLOAT size;
1461 HRESULT hr;
1463 factory = create_factory();
1465 hr = IDWriteFactory_CreateTextFormat(factory, L"Tahoma", NULL, DWRITE_FONT_WEIGHT_BOLD, DWRITE_FONT_STYLE_NORMAL,
1466 DWRITE_FONT_STRETCH_NORMAL, 10.0, L"ru", &format);
1467 ok(hr == S_OK, "Failed to create text format, hr %#lx.\n", hr);
1469 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, L"string", 6, format, 100.0f, 100.0f, 1.0f, NULL,
1470 FALSE, &layout);
1471 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
1473 hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextFormat, (void**)&fmt2);
1474 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1476 weight = IDWriteTextFormat_GetFontWeight(fmt2);
1477 ok(weight == DWRITE_FONT_WEIGHT_BOLD, "got %u\n", weight);
1479 range.startPosition = range.length = 0;
1480 hr = IDWriteTextLayout_GetFontWeight(layout, 0, &weight, &range);
1481 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1482 ok(range.startPosition == 0 && range.length == ~0u, "got %u, %u\n", range.startPosition, range.length);
1484 range.startPosition = 0;
1485 range.length = 6;
1486 hr = IDWriteTextLayout_SetFontWeight(layout, DWRITE_FONT_WEIGHT_NORMAL, range);
1487 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1489 range.startPosition = range.length = 0;
1490 hr = IDWriteTextLayout_GetFontWeight(layout, 0, &weight, &range);
1491 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1492 ok(range.startPosition == 0 && range.length == 6, "got %u, %u\n", range.startPosition, range.length);
1494 /* IDWriteTextFormat methods output doesn't reflect layout changes */
1495 weight = IDWriteTextFormat_GetFontWeight(fmt2);
1496 ok(weight == DWRITE_FONT_WEIGHT_BOLD, "got %u\n", weight);
1498 range.length = 0;
1499 weight = DWRITE_FONT_WEIGHT_BOLD;
1500 hr = IDWriteTextLayout_GetFontWeight(layout, 0, &weight, &range);
1501 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1502 ok(weight == DWRITE_FONT_WEIGHT_NORMAL, "got %d\n", weight);
1503 ok(range.length == 6, "got %d\n", range.length);
1505 range.startPosition = 0;
1506 range.length = 6;
1507 hr = IDWriteTextLayout_SetFontWeight(layout, 1000, range);
1508 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
1510 size = IDWriteTextLayout_GetMaxWidth(layout);
1511 ok(size == 100.0, "got %.2f\n", size);
1513 hr = IDWriteTextLayout_SetMaxWidth(layout, 0.0);
1514 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1516 size = IDWriteTextLayout_GetMaxWidth(layout);
1517 ok(size == 0.0, "got %.2f\n", size);
1519 hr = IDWriteTextLayout_SetMaxWidth(layout, -1.0);
1520 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
1522 size = IDWriteTextLayout_GetMaxWidth(layout);
1523 ok(size == 0.0, "got %.2f\n", size);
1525 hr = IDWriteTextLayout_SetMaxWidth(layout, 100.0);
1526 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1528 size = IDWriteTextLayout_GetMaxWidth(layout);
1529 ok(size == 100.0, "got %.2f\n", size);
1531 size = IDWriteTextLayout_GetMaxHeight(layout);
1532 ok(size == 100.0, "got %.2f\n", size);
1534 hr = IDWriteTextLayout_SetMaxHeight(layout, 0.0);
1535 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1537 size = IDWriteTextLayout_GetMaxHeight(layout);
1538 ok(size == 0.0, "got %.2f\n", size);
1540 hr = IDWriteTextLayout_SetMaxHeight(layout, -1.0);
1541 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
1543 size = IDWriteTextLayout_GetMaxHeight(layout);
1544 ok(size == 0.0, "got %.2f\n", size);
1546 hr = IDWriteTextLayout_SetMaxHeight(layout, 100.0);
1547 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1549 size = IDWriteTextLayout_GetMaxHeight(layout);
1550 ok(size == 100.0, "got %.2f\n", size);
1552 IDWriteTextLayout_Release(layout);
1553 IDWriteTextFormat_Release(fmt2);
1554 IDWriteTextFormat_Release(format);
1555 IDWriteFactory_Release(factory);
1558 static void test_SetInlineObject(void)
1560 IDWriteInlineObject *inlineobj, *inlineobj2, *inlinetest;
1561 DWRITE_TEXT_RANGE range, r2;
1562 IDWriteTextFormat *format;
1563 IDWriteTextLayout *layout;
1564 IDWriteFactory *factory;
1565 HRESULT hr;
1567 factory = create_factory();
1569 hr = IDWriteFactory_CreateTextFormat(factory, L"Tahoma", NULL, DWRITE_FONT_WEIGHT_BOLD, DWRITE_FONT_STYLE_NORMAL,
1570 DWRITE_FONT_STRETCH_NORMAL, 10.0f, L"ru", &format);
1571 ok(hr == S_OK, "Failed to create text format, hr %#lx.\n", hr);
1573 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, L"string", 6, format, 100.0f, 100.0f, 1.0f, NULL,
1574 FALSE, &layout);
1575 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
1577 hr = IDWriteFactory_CreateEllipsisTrimmingSign(factory, format, &inlineobj);
1578 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1580 hr = IDWriteFactory_CreateEllipsisTrimmingSign(factory, format, &inlineobj2);
1581 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1583 EXPECT_REF(inlineobj, 1);
1584 EXPECT_REF(inlineobj2, 1);
1586 inlinetest = (void*)0x1;
1587 hr = IDWriteTextLayout_GetInlineObject(layout, 0, &inlinetest, NULL);
1588 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1589 ok(inlinetest == NULL, "got %p\n", inlinetest);
1591 range.startPosition = 0;
1592 range.length = 2;
1593 hr = IDWriteTextLayout_SetInlineObject(layout, inlineobj, range);
1594 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1596 EXPECT_REF(inlineobj, 2);
1598 inlinetest = (void*)0x1;
1599 hr = IDWriteTextLayout_GetInlineObject(layout, 2, &inlinetest, NULL);
1600 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1601 ok(inlinetest == NULL, "got %p\n", inlinetest);
1603 inlinetest = NULL;
1604 r2.startPosition = r2.length = 100;
1605 hr = IDWriteTextLayout_GetInlineObject(layout, 0, &inlinetest, &r2);
1606 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1607 ok(inlinetest == inlineobj, "got %p\n", inlinetest);
1608 ok(r2.startPosition == 0 && r2.length == 2, "got %d, %d\n", r2.startPosition, r2.length);
1609 IDWriteInlineObject_Release(inlinetest);
1611 EXPECT_REF(inlineobj, 2);
1613 /* get from somewhere inside a range */
1614 inlinetest = NULL;
1615 r2.startPosition = r2.length = 100;
1616 hr = IDWriteTextLayout_GetInlineObject(layout, 1, &inlinetest, &r2);
1617 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1618 ok(inlinetest == inlineobj, "got %p\n", inlinetest);
1619 ok(r2.startPosition == 0 && r2.length == 2, "got %d, %d\n", r2.startPosition, r2.length);
1620 IDWriteInlineObject_Release(inlinetest);
1622 EXPECT_REF(inlineobj, 2);
1624 range.startPosition = 1;
1625 range.length = 1;
1626 hr = IDWriteTextLayout_SetInlineObject(layout, inlineobj2, range);
1627 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1629 inlinetest = NULL;
1630 r2.startPosition = r2.length = 100;
1631 hr = IDWriteTextLayout_GetInlineObject(layout, 1, &inlinetest, &r2);
1632 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1633 ok(inlinetest == inlineobj2, "got %p\n", inlinetest);
1634 ok(r2.startPosition == 1 && r2.length == 1, "got %d, %d\n", r2.startPosition, r2.length);
1635 IDWriteInlineObject_Release(inlinetest);
1637 EXPECT_REF(inlineobj, 2);
1638 EXPECT_REF(inlineobj2, 2);
1640 inlinetest = NULL;
1641 r2.startPosition = r2.length = 100;
1642 hr = IDWriteTextLayout_GetInlineObject(layout, 0, &inlinetest, &r2);
1643 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1644 ok(inlinetest == inlineobj, "got %p\n", inlinetest);
1645 ok(r2.startPosition == 0 && r2.length == 1, "got %d, %d\n", r2.startPosition, r2.length);
1646 IDWriteInlineObject_Release(inlinetest);
1648 EXPECT_REF(inlineobj, 2);
1650 range.startPosition = 1;
1651 range.length = 1;
1652 hr = IDWriteTextLayout_SetInlineObject(layout, inlineobj, range);
1653 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1655 r2.startPosition = r2.length = 100;
1656 hr = IDWriteTextLayout_GetInlineObject(layout, 0, &inlinetest, &r2);
1657 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1658 ok(inlinetest == inlineobj, "got %p\n", inlinetest);
1659 ok(r2.startPosition == 0 && r2.length == 2, "got %d, %d\n", r2.startPosition, r2.length);
1660 IDWriteInlineObject_Release(inlinetest);
1662 EXPECT_REF(inlineobj, 2);
1664 range.startPosition = 1;
1665 range.length = 2;
1666 hr = IDWriteTextLayout_SetInlineObject(layout, inlineobj, range);
1667 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1669 EXPECT_REF(inlineobj, 2);
1671 r2.startPosition = r2.length = 100;
1672 hr = IDWriteTextLayout_GetInlineObject(layout, 0, &inlinetest, &r2);
1673 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1674 ok(inlinetest == inlineobj, "got %p\n", inlinetest);
1675 ok(r2.startPosition == 0 && r2.length == 3, "got %d, %d\n", r2.startPosition, r2.length);
1676 IDWriteInlineObject_Release(inlinetest);
1678 EXPECT_REF(inlineobj, 2);
1679 EXPECT_REF(inlineobj2, 1);
1681 IDWriteTextLayout_Release(layout);
1683 EXPECT_REF(inlineobj, 1);
1685 IDWriteInlineObject_Release(inlineobj);
1686 IDWriteInlineObject_Release(inlineobj2);
1687 IDWriteTextFormat_Release(format);
1688 IDWriteFactory_Release(factory);
1691 /* drawing calls sequence doesn't depend on run order, instead all runs are
1692 drawn first, inline objects next and then underline/strikes */
1693 static const struct drawcall_entry draw_seq[] = {
1694 { DRAW_GLYPHRUN, {'s',0}, {'r','u',0}, 1 },
1695 { DRAW_GLYPHRUN, {'r','i',0}, {'r','u',0}, 2 },
1696 { DRAW_GLYPHRUN|DRAW_EFFECT, {'n',0}, {'r','u',0}, 1 },
1697 { DRAW_GLYPHRUN, {'g',0}, {'r','u',0}, 1 },
1698 { DRAW_INLINE },
1699 { DRAW_UNDERLINE, {0}, {'r','u',0} },
1700 { DRAW_STRIKETHROUGH },
1701 { DRAW_LAST_KIND }
1704 static const struct drawcall_entry draw_trimmed_seq[] = {
1705 { DRAW_GLYPHRUN, {'a',0}, {'e','n','-','u','s',0}, 1 },
1706 { DRAW_LAST_KIND }
1709 static const struct drawcall_entry draw_seq2[] = {
1710 { DRAW_GLYPHRUN, {'s',0}, {'r','u',0}, 1 },
1711 { DRAW_GLYPHRUN, {'t',0}, {'r','u',0}, 1 },
1712 { DRAW_GLYPHRUN, {'r',0}, {'r','u',0}, 1 },
1713 { DRAW_GLYPHRUN, {'i',0}, {'r','u',0}, 1 },
1714 { DRAW_GLYPHRUN, {'n',0}, {'r','u',0}, 1 },
1715 { DRAW_GLYPHRUN, {'g',0}, {'r','u',0}, 1 },
1716 { DRAW_LAST_KIND }
1719 static const struct drawcall_entry draw_seq3[] = {
1720 { DRAW_GLYPHRUN, {0x202a,0x202c,0}, {'r','u',0}, 0 },
1721 { DRAW_GLYPHRUN, {'a','b',0}, {'r','u',0}, 2 },
1722 { DRAW_LAST_KIND }
1725 static const struct drawcall_entry draw_seq4[] = {
1726 { DRAW_GLYPHRUN, {'s','t','r',0}, {'r','u',0}, 3 },
1727 { DRAW_GLYPHRUN, {'i','n','g',0}, {'r','u',0}, 3 },
1728 { DRAW_STRIKETHROUGH },
1729 { DRAW_LAST_KIND }
1732 static const struct drawcall_entry draw_seq5[] = {
1733 { DRAW_GLYPHRUN, {'s','t',0}, {'r','u',0}, 2 },
1734 { DRAW_GLYPHRUN, {'r','i',0}, {'r','u',0}, 2 },
1735 { DRAW_GLYPHRUN, {'n','g',0}, {'r','u',0}, 2 },
1736 { DRAW_STRIKETHROUGH },
1737 { DRAW_LAST_KIND }
1740 static const struct drawcall_entry empty_seq[] = {
1741 { DRAW_LAST_KIND }
1744 static const struct drawcall_entry draw_single_run_seq[] = {
1745 { DRAW_GLYPHRUN, {'s','t','r','i','n','g',0}, {'r','u',0}, 6 },
1746 { DRAW_LAST_KIND }
1749 static const struct drawcall_entry draw_ltr_reordered_run_seq[] = {
1750 { DRAW_GLYPHRUN, {'1','2','3','-','5','2',0}, {'r','u',0}, 6 },
1751 { DRAW_GLYPHRUN, {0x64a,0x64f,0x633,0x627,0x648,0x650,0x64a,0}, {'r','u',0}, 7, 1 },
1752 { DRAW_GLYPHRUN, {'7','1',0}, {'r','u',0}, 2, 2 },
1753 { DRAW_GLYPHRUN, {'.',0}, {'r','u',0}, 1 },
1754 { DRAW_LAST_KIND }
1757 static void test_Draw(void)
1759 static const WCHAR str3W[] = {'1','2','3','-','5','2',0x64a,0x64f,0x633,0x627,0x648,0x650,
1760 0x64a,'7','1','.',0};
1761 static const WCHAR str2W[] = {0x202a,0x202c,'a','b',0};
1762 IDWriteInlineObject *inlineobj;
1763 struct renderer_context ctxt;
1764 IDWriteTextFormat *format;
1765 IDWriteTextLayout *layout;
1766 DWRITE_TEXT_RANGE range;
1767 IDWriteFactory *factory;
1768 DWRITE_TEXT_METRICS tm;
1769 DWRITE_MATRIX m;
1770 HRESULT hr;
1772 factory = create_factory();
1774 memset(&ctxt, 0, sizeof(ctxt));
1775 ctxt.snapping_disabled = TRUE;
1777 hr = IDWriteFactory_CreateTextFormat(factory, L"Tahoma", NULL, DWRITE_FONT_WEIGHT_BOLD, DWRITE_FONT_STYLE_NORMAL,
1778 DWRITE_FONT_STRETCH_NORMAL, 10.0f, L"ru", &format);
1779 ok(hr == S_OK, "Failed to create text format, hr %#lx.\n", hr);
1781 hr = IDWriteFactory_CreateTextLayout(factory, L"string", 6, format, 100.0f, 100.0f, &layout);
1782 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
1784 hr = IDWriteFactory_CreateEllipsisTrimmingSign(factory, format, &inlineobj);
1785 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1787 range.startPosition = 5;
1788 range.length = 1;
1789 hr = IDWriteTextLayout_SetStrikethrough(layout, TRUE, range);
1790 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1792 range.startPosition = 1;
1793 range.length = 1;
1794 hr = IDWriteTextLayout_SetInlineObject(layout, inlineobj, range);
1795 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1797 range.startPosition = 4;
1798 range.length = 1;
1799 hr = IDWriteTextLayout_SetDrawingEffect(layout, (IUnknown*)inlineobj, range);
1800 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1802 range.startPosition = 0;
1803 range.length = 1;
1804 hr = IDWriteTextLayout_SetUnderline(layout, TRUE, range);
1805 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1807 flush_sequence(sequences, RENDERER_ID);
1808 hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0, 0.0);
1809 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1810 ok_sequence(sequences, RENDERER_ID, draw_seq, "draw test", FALSE);
1811 IDWriteTextLayout_Release(layout);
1813 /* with reduced width DrawGlyphRun() is called for every line */
1814 hr = IDWriteFactory_CreateTextLayout(factory, L"string", 6, format, 5.0f, 100.0f, &layout);
1815 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
1816 flush_sequence(sequences, RENDERER_ID);
1817 hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0, 0.0);
1818 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1819 ok_sequence(sequences, RENDERER_ID, draw_seq2, "draw test 2", TRUE);
1820 hr = IDWriteTextLayout_GetMetrics(layout, &tm);
1821 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1822 todo_wine
1823 ok(tm.lineCount == 6, "got %u\n", tm.lineCount);
1824 IDWriteTextLayout_Release(layout);
1826 /* string with control characters */
1827 hr = IDWriteFactory_CreateTextLayout(factory, str2W, 4, format, 500.0, 100.0, &layout);
1828 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1829 flush_sequence(sequences, RENDERER_ID);
1830 hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0, 0.0);
1831 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1832 ok_sequence(sequences, RENDERER_ID, draw_seq3, "draw test 3", FALSE);
1833 IDWriteTextLayout_Release(layout);
1835 /* strikethrough splits ranges from renderer point of view, but doesn't break
1836 shaping */
1837 hr = IDWriteFactory_CreateTextLayout(factory, L"string", 6, format, 500.0f, 100.0f, &layout);
1838 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
1839 flush_sequence(sequences, RENDERER_ID);
1841 range.startPosition = 0;
1842 range.length = 3;
1843 hr = IDWriteTextLayout_SetStrikethrough(layout, TRUE, range);
1844 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1846 hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0, 0.0);
1847 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1848 ok_sequence(sequences, RENDERER_ID, draw_seq4, "draw test 4", FALSE);
1849 IDWriteTextLayout_Release(layout);
1851 /* Strike through somewhere in the middle */
1852 hr = IDWriteFactory_CreateTextLayout(factory, L"string", 6, format, 500.0f, 100.0f, &layout);
1853 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
1854 flush_sequence(sequences, RENDERER_ID);
1856 range.startPosition = 2;
1857 range.length = 2;
1858 hr = IDWriteTextLayout_SetStrikethrough(layout, TRUE, range);
1859 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1861 hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0, 0.0);
1862 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1863 ok_sequence(sequences, RENDERER_ID, draw_seq5, "draw test 5", FALSE);
1864 IDWriteTextLayout_Release(layout);
1866 /* empty string */
1867 hr = IDWriteFactory_CreateTextLayout(factory, L"string", 0, format, 500.0f, 100.0f, &layout);
1868 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
1870 flush_sequence(sequences, RENDERER_ID);
1871 hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0, 0.0);
1872 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1873 ok_sequence(sequences, RENDERER_ID, empty_seq, "draw test 6", FALSE);
1874 IDWriteTextLayout_Release(layout);
1876 ctxt.gdicompat = TRUE;
1877 ctxt.use_gdi_natural = TRUE;
1879 /* different parameter combinations with gdi-compatible layout */
1880 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, L"string", 6, format, 100.0f, 100.0f, 1.0f, NULL,
1881 TRUE, &layout);
1882 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
1883 flush_sequence(sequences, RENDERER_ID);
1884 hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0, 0.0);
1885 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1886 ok_sequence(sequences, RENDERER_ID, draw_single_run_seq, "draw test 7", FALSE);
1888 /* text alignment keeps pixel-aligned origin */
1889 hr = IDWriteTextLayout_GetMetrics(layout, &tm);
1890 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1891 ok(tm.width == floorf(tm.width), "got %f\n", tm.width);
1893 hr = IDWriteTextLayout_SetMaxWidth(layout, tm.width + 3.0);
1894 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1895 hr = IDWriteTextLayout_SetTextAlignment(layout, DWRITE_TEXT_ALIGNMENT_CENTER);
1896 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1898 ctxt.originX = ctxt.originY = 0.0;
1899 flush_sequence(sequences, RENDERER_ID);
1900 hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0, 0.0);
1901 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1902 ok_sequence(sequences, RENDERER_ID, draw_single_run_seq, "draw test 7", FALSE);
1903 ok(ctxt.originX != 0.0 && ctxt.originX == floorf(ctxt.originX), "got %f\n", ctxt.originX);
1905 IDWriteTextLayout_Release(layout);
1907 ctxt.gdicompat = TRUE;
1908 ctxt.use_gdi_natural = FALSE;
1910 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, L"string", 6, format, 100.0f, 100.0f, 1.0f, NULL,
1911 FALSE, &layout);
1912 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
1913 flush_sequence(sequences, RENDERER_ID);
1914 hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0, 0.0);
1915 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1916 ok_sequence(sequences, RENDERER_ID, draw_single_run_seq, "draw test 8", FALSE);
1917 IDWriteTextLayout_Release(layout);
1919 ctxt.gdicompat = TRUE;
1920 ctxt.use_gdi_natural = TRUE;
1922 m.m11 = m.m22 = 2.0;
1923 m.m12 = m.m21 = m.dx = m.dy = 0.0;
1924 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, L"string", 6, format, 100.0f, 100.0f, 1.0f, &m,
1925 TRUE, &layout);
1926 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
1927 flush_sequence(sequences, RENDERER_ID);
1928 hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0, 0.0);
1929 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1930 ok_sequence(sequences, RENDERER_ID, draw_single_run_seq, "draw test 9", FALSE);
1931 IDWriteTextLayout_Release(layout);
1933 ctxt.gdicompat = TRUE;
1934 ctxt.use_gdi_natural = FALSE;
1936 m.m11 = m.m22 = 2.0;
1937 m.m12 = m.m21 = m.dx = m.dy = 0.0;
1938 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, L"string", 6, format, 100.0f, 100.0f, 1.0f, &m,
1939 FALSE, &layout);
1940 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
1941 flush_sequence(sequences, RENDERER_ID);
1942 hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0, 0.0);
1943 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1944 ok_sequence(sequences, RENDERER_ID, draw_single_run_seq, "draw test 10", FALSE);
1945 IDWriteTextLayout_Release(layout);
1947 IDWriteInlineObject_Release(inlineobj);
1949 /* text that triggers bidi run reordering */
1950 hr = IDWriteFactory_CreateTextLayout(factory, str3W, lstrlenW(str3W), format, 1000.0f, 100.0f, &layout);
1951 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1953 ctxt.gdicompat = FALSE;
1954 ctxt.use_gdi_natural = FALSE;
1955 ctxt.snapping_disabled = TRUE;
1957 hr = IDWriteTextLayout_SetReadingDirection(layout, DWRITE_READING_DIRECTION_LEFT_TO_RIGHT);
1958 ok(hr == S_OK, "Failed to set reading direction, hr %#lx.\n", hr);
1960 flush_sequence(sequences, RENDERER_ID);
1961 hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0f, 0.0f);
1962 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1963 ok_sequence(sequences, RENDERER_ID, draw_ltr_reordered_run_seq, "draw test 11", FALSE);
1965 IDWriteTextLayout_Release(layout);
1967 IDWriteTextFormat_Release(format);
1968 IDWriteFactory_Release(factory);
1971 static void test_typography(void)
1973 DWRITE_FONT_FEATURE feature;
1974 IDWriteTypography *typography;
1975 IDWriteFactory *factory;
1976 UINT32 count;
1977 HRESULT hr;
1979 factory = create_factory();
1981 hr = IDWriteFactory_CreateTypography(factory, &typography);
1982 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1984 feature.nameTag = DWRITE_FONT_FEATURE_TAG_KERNING;
1985 feature.parameter = 1;
1986 hr = IDWriteTypography_AddFontFeature(typography, feature);
1987 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1989 count = IDWriteTypography_GetFontFeatureCount(typography);
1990 ok(count == 1, "got %u\n", count);
1992 /* duplicated features work just fine */
1993 feature.nameTag = DWRITE_FONT_FEATURE_TAG_KERNING;
1994 feature.parameter = 0;
1995 hr = IDWriteTypography_AddFontFeature(typography, feature);
1996 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1998 count = IDWriteTypography_GetFontFeatureCount(typography);
1999 ok(count == 2, "got %u\n", count);
2001 memset(&feature, 0xcc, sizeof(feature));
2002 hr = IDWriteTypography_GetFontFeature(typography, 0, &feature);
2003 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2004 ok(feature.nameTag == DWRITE_FONT_FEATURE_TAG_KERNING, "got tag %x\n", feature.nameTag);
2005 ok(feature.parameter == 1, "got %u\n", feature.parameter);
2007 memset(&feature, 0xcc, sizeof(feature));
2008 hr = IDWriteTypography_GetFontFeature(typography, 1, &feature);
2009 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2010 ok(feature.nameTag == DWRITE_FONT_FEATURE_TAG_KERNING, "got tag %x\n", feature.nameTag);
2011 ok(feature.parameter == 0, "got %u\n", feature.parameter);
2013 hr = IDWriteTypography_GetFontFeature(typography, 2, &feature);
2014 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
2016 /* duplicated with same parameter value */
2017 feature.nameTag = DWRITE_FONT_FEATURE_TAG_KERNING;
2018 feature.parameter = 0;
2019 hr = IDWriteTypography_AddFontFeature(typography, feature);
2020 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2022 count = IDWriteTypography_GetFontFeatureCount(typography);
2023 ok(count == 3, "got %u\n", count);
2025 memset(&feature, 0xcc, sizeof(feature));
2026 hr = IDWriteTypography_GetFontFeature(typography, 2, &feature);
2027 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2028 ok(feature.nameTag == DWRITE_FONT_FEATURE_TAG_KERNING, "got tag %x\n", feature.nameTag);
2029 ok(feature.parameter == 0, "got %u\n", feature.parameter);
2031 IDWriteTypography_Release(typography);
2032 IDWriteFactory_Release(factory);
2035 static void test_GetClusterMetrics(void)
2037 static const WCHAR str_white_spaceW[] = {
2038 /* BK - FORM FEED, LINE TABULATION, LINE SEP, PARA SEP */ 0xc, 0xb, 0x2028, 0x2029,
2039 /* ZW - ZERO WIDTH SPACE */ 0x200b,
2040 /* SP - SPACE */ 0x20
2042 static const WCHAR str5W[] = {'a','\r','b','\n','c','\n','\r','d','\r','\n','e',0xb,'f',0xc,
2043 'g',0x0085,'h',0x2028,'i',0x2029,0xad,0xa,0};
2044 static const WCHAR str3W[] = {0x2066,')',')',0x661,'(',0x627,')',0};
2045 static const WCHAR str2W[] = {0x202a,0x202c,'a',0};
2046 DWRITE_INLINE_OBJECT_METRICS inline_metrics;
2047 DWRITE_CLUSTER_METRICS metrics[22];
2048 DWRITE_TEXT_METRICS text_metrics;
2049 DWRITE_TRIMMING trimming_options;
2050 IDWriteTextLayout1 *layout1;
2051 IDWriteInlineObject *trimm;
2052 IDWriteTextFormat *format;
2053 IDWriteTextLayout *layout;
2054 DWRITE_LINE_METRICS line;
2055 DWRITE_TEXT_RANGE range;
2056 IDWriteFactory *factory;
2057 UINT32 count, i;
2058 FLOAT width;
2059 HRESULT hr;
2061 factory = create_factory();
2063 hr = IDWriteFactory_CreateTextFormat(factory, L"Tahoma", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
2064 DWRITE_FONT_STRETCH_NORMAL, 10.0f, L"en-us", &format);
2065 ok(hr == S_OK, "Failed to create text format, hr %#lx.\n", hr);
2067 hr = IDWriteFactory_CreateTextLayout(factory, str3W, 7, format, 1000.0, 1000.0, &layout);
2068 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2069 hr = IDWriteTextLayout_GetClusterMetrics(layout, NULL, 0, &count);
2070 ok(hr == E_NOT_SUFFICIENT_BUFFER, "Unexpected hr %#lx.\n", hr);
2071 ok(count == 7, "got %u\n", count);
2072 IDWriteTextLayout_Release(layout);
2074 hr = IDWriteFactory_CreateTextLayout(factory, L"abcd", 4, format, 1000.0f, 1000.0f, &layout);
2075 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
2077 count = 0;
2078 hr = IDWriteTextLayout_GetClusterMetrics(layout, NULL, 0, &count);
2079 ok(hr == E_NOT_SUFFICIENT_BUFFER, "Unexpected hr %#lx.\n", hr);
2080 ok(count == 4, "got %u\n", count);
2082 /* check every cluster width */
2083 count = 0;
2084 hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, ARRAY_SIZE(metrics), &count);
2085 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2086 ok(count == 4, "got %u\n", count);
2087 for (i = 0; i < count; i++) {
2088 ok(metrics[i].width > 0.0, "%u: got width %.2f\n", i, metrics[i].width);
2089 ok(metrics[i].length == 1, "%u: got length %u\n", i, metrics[i].length);
2092 /* apply spacing and check widths again */
2093 if (IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextLayout1, (void**)&layout1) == S_OK) {
2094 DWRITE_CLUSTER_METRICS metrics2[4];
2095 FLOAT leading, trailing, min_advance;
2096 DWRITE_TEXT_RANGE r;
2098 leading = trailing = min_advance = 2.0;
2099 hr = IDWriteTextLayout1_GetCharacterSpacing(layout1, 500, &leading, &trailing,
2100 &min_advance, &r);
2101 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2102 ok(leading == 0.0 && trailing == 0.0 && min_advance == 0.0,
2103 "got %.2f, %.2f, %.2f\n", leading, trailing, min_advance);
2104 ok(r.startPosition == 0 && r.length == ~0u, "got %u, %u\n", r.startPosition, r.length);
2106 leading = trailing = min_advance = 2.0;
2107 hr = IDWriteTextLayout1_GetCharacterSpacing(layout1, 0, &leading, &trailing,
2108 &min_advance, NULL);
2109 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2110 ok(leading == 0.0 && trailing == 0.0 && min_advance == 0.0,
2111 "got %.2f, %.2f, %.2f\n", leading, trailing, min_advance);
2113 r.startPosition = 0;
2114 r.length = 4;
2115 hr = IDWriteTextLayout1_SetCharacterSpacing(layout1, 10.0, 15.0, 0.0, r);
2116 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2118 count = 0;
2119 hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics2, ARRAY_SIZE(metrics2), &count);
2120 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2121 ok(count == 4, "got %u\n", count);
2122 for (i = 0; i < count; ++i)
2124 ok(metrics2[i].width > metrics[i].width, "%u: got width %.2f, was %.2f\n", i, metrics2[i].width,
2125 metrics[i].width);
2126 ok(metrics2[i].length == 1, "%u: got length %u\n", i, metrics2[i].length);
2129 /* back to defaults */
2130 r.startPosition = 0;
2131 r.length = 4;
2132 hr = IDWriteTextLayout1_SetCharacterSpacing(layout1, 0.0, 0.0, 0.0, r);
2133 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2135 /* negative advance limit */
2136 r.startPosition = 0;
2137 r.length = 4;
2138 hr = IDWriteTextLayout1_SetCharacterSpacing(layout1, 0.0, 0.0, -10.0, r);
2139 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
2141 IDWriteTextLayout1_Release(layout1);
2143 else
2144 win_skip("IDWriteTextLayout1 is not supported, cluster spacing test skipped.\n");
2146 hr = IDWriteFactory_CreateEllipsisTrimmingSign(factory, format, &trimm);
2147 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2149 range.startPosition = 0;
2150 range.length = 2;
2151 hr = IDWriteTextLayout_SetInlineObject(layout, trimm, range);
2152 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2154 /* inline object takes a separate cluster, replaced codepoints number doesn't matter */
2155 count = 0;
2156 hr = IDWriteTextLayout_GetClusterMetrics(layout, NULL, 0, &count);
2157 ok(hr == E_NOT_SUFFICIENT_BUFFER, "Unexpected hr %#lx.\n", hr);
2158 ok(count == 3, "got %u\n", count);
2160 count = 0;
2161 memset(&metrics, 0, sizeof(metrics));
2162 hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 1, &count);
2163 ok(hr == E_NOT_SUFFICIENT_BUFFER, "Unexpected hr %#lx.\n", hr);
2164 ok(count == 3, "got %u\n", count);
2165 ok(metrics[0].length == 2, "got %u\n", metrics[0].length);
2167 hr = IDWriteInlineObject_GetMetrics(trimm, &inline_metrics);
2168 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2169 ok(inline_metrics.width > 0.0 && inline_metrics.width == metrics[0].width, "got %.2f, expected %.2f\n",
2170 inline_metrics.width, metrics[0].width);
2172 IDWriteTextLayout_Release(layout);
2174 /* text with non-visual control codes */
2175 hr = IDWriteFactory_CreateTextLayout(factory, str2W, 3, format, 1000.0, 1000.0, &layout);
2176 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2178 /* bidi control codes take a separate cluster */
2179 count = 0;
2180 memset(metrics, 0, sizeof(metrics));
2181 hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 3, &count);
2182 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2183 ok(count == 3, "got %u\n", count);
2185 ok(metrics[0].width == 0.0, "got %.2f\n", metrics[0].width);
2186 ok(metrics[0].length == 1, "got %d\n", metrics[0].length);
2187 ok(metrics[0].canWrapLineAfter == 0, "got %d\n", metrics[0].canWrapLineAfter);
2188 ok(metrics[0].isWhitespace == 0, "got %d\n", metrics[0].isWhitespace);
2189 ok(metrics[0].isNewline == 0, "got %d\n", metrics[0].isNewline);
2190 ok(metrics[0].isSoftHyphen == 0, "got %d\n", metrics[0].isSoftHyphen);
2191 ok(metrics[0].isRightToLeft == 0, "got %d\n", metrics[0].isRightToLeft);
2193 ok(metrics[1].width == 0.0, "got %.2f\n", metrics[1].width);
2194 ok(metrics[1].length == 1, "got %d\n", metrics[1].length);
2195 ok(metrics[1].canWrapLineAfter == 0, "got %d\n", metrics[1].canWrapLineAfter);
2196 ok(metrics[1].isWhitespace == 0, "got %d\n", metrics[1].isWhitespace);
2197 ok(metrics[1].isNewline == 0, "got %d\n", metrics[1].isNewline);
2198 ok(metrics[1].isSoftHyphen == 0, "got %d\n", metrics[1].isSoftHyphen);
2199 ok(metrics[1].isRightToLeft == 0, "got %d\n", metrics[1].isRightToLeft);
2201 ok(metrics[2].width > 0.0, "got %.2f\n", metrics[2].width);
2202 ok(metrics[2].length == 1, "got %d\n", metrics[2].length);
2203 ok(metrics[2].canWrapLineAfter == 1, "got %d\n", metrics[2].canWrapLineAfter);
2204 ok(metrics[2].isWhitespace == 0, "got %d\n", metrics[2].isWhitespace);
2205 ok(metrics[2].isNewline == 0, "got %d\n", metrics[2].isNewline);
2206 ok(metrics[2].isSoftHyphen == 0, "got %d\n", metrics[2].isSoftHyphen);
2207 ok(metrics[2].isRightToLeft == 0, "got %d\n", metrics[2].isRightToLeft);
2209 IDWriteTextLayout_Release(layout);
2211 /* single inline object that fails to report its metrics */
2212 hr = IDWriteFactory_CreateTextLayout(factory, L"abcd", 4, format, 1000.0f, 1000.0f, &layout);
2213 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
2215 range.startPosition = 0;
2216 range.length = 4;
2217 hr = IDWriteTextLayout_SetInlineObject(layout, &testinlineobj, range);
2218 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2220 count = 0;
2221 memset(metrics, 0, sizeof(metrics));
2222 hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 3, &count);
2223 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2224 ok(count == 1, "got %u\n", count);
2226 /* object sets a width to 123.0, but returns failure from GetMetrics() */
2227 ok(metrics[0].width == 0.0, "got %.2f\n", metrics[0].width);
2228 ok(metrics[0].length == 4, "got %d\n", metrics[0].length);
2229 ok(metrics[0].canWrapLineAfter == 1, "got %d\n", metrics[0].canWrapLineAfter);
2230 ok(metrics[0].isWhitespace == 0, "got %d\n", metrics[0].isWhitespace);
2231 ok(metrics[0].isNewline == 0, "got %d\n", metrics[0].isNewline);
2232 ok(metrics[0].isSoftHyphen == 0, "got %d\n", metrics[0].isSoftHyphen);
2233 ok(metrics[0].isRightToLeft == 0, "got %d\n", metrics[0].isRightToLeft);
2235 /* now set two inline object for [0,1] and [2,3], both fail to report break conditions */
2236 range.startPosition = 2;
2237 range.length = 2;
2238 hr = IDWriteTextLayout_SetInlineObject(layout, &testinlineobj2, range);
2239 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2241 count = 0;
2242 memset(metrics, 0, sizeof(metrics));
2243 hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 3, &count);
2244 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2245 ok(count == 2, "got %u\n", count);
2247 ok(metrics[0].width == 0.0, "got %.2f\n", metrics[0].width);
2248 ok(metrics[0].length == 2, "got %d\n", metrics[0].length);
2249 ok(metrics[0].canWrapLineAfter == 0, "got %d\n", metrics[0].canWrapLineAfter);
2250 ok(metrics[0].isWhitespace == 0, "got %d\n", metrics[0].isWhitespace);
2251 ok(metrics[0].isNewline == 0, "got %d\n", metrics[0].isNewline);
2252 ok(metrics[0].isSoftHyphen == 0, "got %d\n", metrics[0].isSoftHyphen);
2253 ok(metrics[0].isRightToLeft == 0, "got %d\n", metrics[0].isRightToLeft);
2255 ok(metrics[1].width == 0.0, "got %.2f\n", metrics[1].width);
2256 ok(metrics[1].length == 2, "got %d\n", metrics[1].length);
2257 ok(metrics[1].canWrapLineAfter == 1, "got %d\n", metrics[1].canWrapLineAfter);
2258 ok(metrics[1].isWhitespace == 0, "got %d\n", metrics[1].isWhitespace);
2259 ok(metrics[1].isNewline == 0, "got %d\n", metrics[1].isNewline);
2260 ok(metrics[1].isSoftHyphen == 0, "got %d\n", metrics[1].isSoftHyphen);
2261 ok(metrics[1].isRightToLeft == 0, "got %d\n", metrics[1].isRightToLeft);
2263 IDWriteTextLayout_Release(layout);
2265 /* zero length string */
2266 hr = IDWriteFactory_CreateTextLayout(factory, L"abcd", 0, format, 1000.0f, 1000.0f, &layout);
2267 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
2269 count = 1;
2270 memset(metrics, 0, sizeof(metrics));
2271 hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 3, &count);
2272 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2273 ok(count == 0, "got %u\n", count);
2274 IDWriteTextLayout_Release(layout);
2276 /* Whitespace */
2277 hr = IDWriteFactory_CreateTextLayout(factory, L"a ", 2, format, 1000.0f, 1000.0f, &layout);
2278 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
2280 count = 0;
2281 memset(metrics, 0, sizeof(metrics));
2282 hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 2, &count);
2283 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2284 ok(count == 2, "got %u\n", count);
2285 ok(metrics[0].isWhitespace == 0, "got %d\n", metrics[0].isWhitespace);
2286 ok(metrics[0].canWrapLineAfter == 0, "got %d\n", metrics[0].canWrapLineAfter);
2287 ok(metrics[1].isWhitespace == 1, "got %d\n", metrics[1].isWhitespace);
2288 ok(metrics[1].canWrapLineAfter == 1, "got %d\n", metrics[1].canWrapLineAfter);
2289 IDWriteTextLayout_Release(layout);
2291 /* Layout is fully covered by inline object with after condition DWRITE_BREAK_CONDITION_MAY_NOT_BREAK. */
2292 hr = IDWriteFactory_CreateTextLayout(factory, L"a ", 2, format, 1000.0f, 1000.0f, &layout);
2293 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
2295 range.startPosition = 0;
2296 range.length = ~0u;
2297 hr = IDWriteTextLayout_SetInlineObject(layout, &testinlineobj3, range);
2298 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2300 count = 0;
2301 memset(metrics, 0, sizeof(metrics));
2302 hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 2, &count);
2303 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2304 ok(count == 1, "got %u\n", count);
2305 ok(metrics[0].canWrapLineAfter == 1, "got %d\n", metrics[0].canWrapLineAfter);
2307 IDWriteTextLayout_Release(layout);
2309 /* compare natural cluster width with gdi layout */
2310 hr = IDWriteFactory_CreateTextLayout(factory, L"a ", 1, format, 100.0f, 100.0f, &layout);
2311 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
2313 count = 0;
2314 memset(metrics, 0, sizeof(metrics));
2315 hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 1, &count);
2316 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2317 ok(count == 1, "got %u\n", count);
2318 ok(metrics[0].width != floorf(metrics[0].width), "got %f\n", metrics[0].width);
2320 IDWriteTextLayout_Release(layout);
2322 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, L"a ", 1, format, 100.0f, 100.0f, 1.0f, NULL,
2323 FALSE, &layout);
2324 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
2326 count = 0;
2327 memset(metrics, 0, sizeof(metrics));
2328 hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 1, &count);
2329 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2330 ok(count == 1, "got %u\n", count);
2331 ok(metrics[0].width == floorf(metrics[0].width), "got %f\n", metrics[0].width);
2333 IDWriteTextLayout_Release(layout);
2335 /* isNewline tests */
2336 hr = IDWriteFactory_CreateTextLayout(factory, str5W, lstrlenW(str5W), format, 100.0f, 200.0f, &layout);
2337 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2339 count = 0;
2340 memset(metrics, 0, sizeof(metrics));
2341 hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, ARRAY_SIZE(metrics), &count);
2342 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2343 ok(count == 22, "got %u\n", count);
2345 ok(metrics[1].isNewline == 1, "got %d\n", metrics[1].isNewline);
2346 ok(metrics[3].isNewline == 1, "got %d\n", metrics[3].isNewline);
2347 ok(metrics[5].isNewline == 1, "got %d\n", metrics[5].isNewline);
2348 ok(metrics[6].isNewline == 1, "got %d\n", metrics[6].isNewline);
2349 ok(metrics[9].isNewline == 1, "got %d\n", metrics[9].isNewline);
2350 ok(metrics[11].isNewline == 1, "got %d\n", metrics[11].isNewline);
2351 ok(metrics[13].isNewline == 1, "got %d\n", metrics[13].isNewline);
2352 ok(metrics[15].isNewline == 1, "got %d\n", metrics[15].isNewline);
2353 ok(metrics[17].isNewline == 1, "got %d\n", metrics[17].isNewline);
2354 ok(metrics[19].isNewline == 1, "got %d\n", metrics[19].isNewline);
2355 ok(metrics[21].isNewline == 1, "got %d\n", metrics[21].isNewline);
2357 ok(metrics[0].isNewline == 0, "got %d\n", metrics[0].isNewline);
2358 ok(metrics[2].isNewline == 0, "got %d\n", metrics[2].isNewline);
2359 ok(metrics[4].isNewline == 0, "got %d\n", metrics[4].isNewline);
2360 ok(metrics[7].isNewline == 0, "got %d\n", metrics[7].isNewline);
2361 ok(metrics[8].isNewline == 0, "got %d\n", metrics[8].isNewline);
2362 ok(metrics[10].isNewline == 0, "got %d\n", metrics[10].isNewline);
2363 ok(metrics[12].isNewline == 0, "got %d\n", metrics[12].isNewline);
2364 ok(metrics[14].isNewline == 0, "got %d\n", metrics[14].isNewline);
2365 ok(metrics[16].isNewline == 0, "got %d\n", metrics[16].isNewline);
2366 ok(metrics[18].isNewline == 0, "got %d\n", metrics[18].isNewline);
2367 ok(metrics[20].isNewline == 0, "got %d\n", metrics[20].isNewline);
2369 for (i = 0; i < count; i++) {
2370 ok(metrics[i].length == 1, "%d: got %d\n", i, metrics[i].length);
2371 ok(metrics[i].isSoftHyphen == (i == count - 2), "%d: got %d\n", i, metrics[i].isSoftHyphen);
2372 if (metrics[i].isSoftHyphen)
2373 ok(!metrics[i].isWhitespace, "%u: got %d\n", i, metrics[i].isWhitespace);
2374 if (metrics[i].isNewline) {
2375 ok(metrics[i].width == 0.0f, "%u: got width %f\n", i, metrics[i].width);
2376 ok(metrics[i].isWhitespace == 1, "%u: got %d\n", i, metrics[i].isWhitespace);
2377 ok(metrics[i].canWrapLineAfter == 1, "%u: got %d\n", i, metrics[i].canWrapLineAfter);
2381 IDWriteTextLayout_Release(layout);
2383 /* Test whitespace resolution from linebreaking classes BK, ZW, and SP */
2384 hr = IDWriteFactory_CreateTextLayout(factory, str_white_spaceW, ARRAY_SIZE(str_white_spaceW), format,
2385 100.0f, 200.0f, &layout);
2386 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2388 count = 0;
2389 memset(metrics, 0, sizeof(metrics));
2390 hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 20, &count);
2391 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2392 ok(count == 6, "got %u\n", count);
2394 ok(metrics[0].isWhitespace == 1, "got %d\n", metrics[0].isWhitespace);
2395 ok(metrics[1].isWhitespace == 1, "got %d\n", metrics[1].isWhitespace);
2396 ok(metrics[2].isWhitespace == 1, "got %d\n", metrics[2].isWhitespace);
2397 ok(metrics[3].isWhitespace == 1, "got %d\n", metrics[3].isWhitespace);
2398 ok(metrics[4].isWhitespace == 0, "got %d\n", metrics[4].isWhitespace);
2399 ok(metrics[5].isWhitespace == 1, "got %d\n", metrics[5].isWhitespace);
2401 IDWriteTextLayout_Release(layout);
2403 /* Trigger line trimming. */
2404 hr = IDWriteFactory_CreateTextLayout(factory, L"abcd", 4, format, 100.0f, 200.0f, &layout);
2405 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
2407 count = 0;
2408 memset(metrics, 0, sizeof(metrics));
2409 hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 4, &count);
2410 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2411 ok(count == 4, "got %u\n", count);
2413 hr = IDWriteTextLayout_GetMetrics(layout, &text_metrics);
2414 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2416 width = metrics[0].width + inline_metrics.width;
2417 ok(width < text_metrics.width, "unexpected trimming sign width\n");
2419 /* enable trimming, reduce layout width so only first cluster and trimming sign fits */
2420 trimming_options.granularity = DWRITE_TRIMMING_GRANULARITY_CHARACTER;
2421 trimming_options.delimiter = 0;
2422 trimming_options.delimiterCount = 0;
2423 hr = IDWriteTextLayout_SetTrimming(layout, &trimming_options, trimm);
2424 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2426 hr = IDWriteTextLayout_SetMaxWidth(layout, width);
2427 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2429 count = 0;
2430 memset(metrics, 0, sizeof(metrics));
2431 hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 4, &count);
2432 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2433 ok(count == 4, "got %u\n", count);
2435 hr = IDWriteTextLayout_GetLineMetrics(layout, &line, 1, &count);
2436 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2437 ok(count == 1, "got %u\n", count);
2438 ok(line.length == 4, "got %u\n", line.length);
2439 ok(line.isTrimmed, "got %d\n", line.isTrimmed);
2441 IDWriteTextLayout_Release(layout);
2443 /* NO_WRAP, check cluster wrapping attribute. */
2444 hr = IDWriteTextFormat_SetWordWrapping(format, DWRITE_WORD_WRAPPING_NO_WRAP);
2445 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2447 hr = IDWriteFactory_CreateTextLayout(factory, L"a b", 3, format, 1000.0f, 200.0f, &layout);
2448 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
2450 count = 0;
2451 memset(metrics, 0, sizeof(metrics));
2452 hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 3, &count);
2453 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2454 ok(count == 3, "got %u\n", count);
2456 ok(metrics[0].canWrapLineAfter == 0, "got %d\n", metrics[0].canWrapLineAfter);
2457 ok(metrics[1].canWrapLineAfter == 1, "got %d\n", metrics[1].canWrapLineAfter);
2458 ok(metrics[2].canWrapLineAfter == 1, "got %d\n", metrics[2].canWrapLineAfter);
2460 IDWriteTextLayout_Release(layout);
2462 /* Single cluster layout, trigger trimming. */
2463 hr = IDWriteFactory_CreateTextLayout(factory, L"a b", 1, format, 1000.0f, 200.0f, &layout);
2464 ok(hr == S_OK, "Failed to create layout, hr %#lx.\n", hr);
2466 count = 0;
2467 memset(metrics, 0, sizeof(metrics));
2468 hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 1, &count);
2469 ok(hr == S_OK, "Failed to get cluster metrics, hr %#lx.\n", hr);
2470 ok(count == 1, "Unexpected cluster count %u.\n", count);
2472 hr = IDWriteTextLayout_SetMaxWidth(layout, metrics[0].width / 2.0f);
2473 ok(hr == S_OK, "Failed to set layout width, hr %#lx.\n", hr);
2475 trimming_options.granularity = DWRITE_TRIMMING_GRANULARITY_CHARACTER;
2476 trimming_options.delimiter = 0;
2477 trimming_options.delimiterCount = 0;
2478 hr = IDWriteTextLayout_SetTrimming(layout, &trimming_options, trimm);
2479 ok(hr == S_OK, "Failed to set trimming options, hr %#lx.\n", hr);
2481 count = 0;
2482 memset(metrics, 0, sizeof(metrics));
2483 hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 1, &count);
2484 ok(hr == S_OK, "Failed to get cluster metrics, hr %#lx.\n", hr);
2485 ok(count == 1, "Unexpected cluster count %u.\n", count);
2487 hr = IDWriteTextLayout_GetLineMetrics(layout, &line, 1, &count);
2488 ok(hr == S_OK, "Failed to get line metrics, hr %#lx.\n", hr);
2489 ok(count == 1, "Unexpected line count %u.\n", count);
2490 ok(line.length == 1, "Unexpected line length %u.\n", line.length);
2491 ok(line.isTrimmed, "Unexpected trimming flag %x.\n", line.isTrimmed);
2493 flush_sequence(sequences, RENDERER_ID);
2494 hr = IDWriteTextLayout_Draw(layout, NULL, &testrenderer, 0.0f, 0.0f);
2495 ok(hr == S_OK, "Draw() failed, hr %#lx.\n", hr);
2496 ok_sequence(sequences, RENDERER_ID, draw_trimmed_seq, "Trimmed draw test", FALSE);
2498 IDWriteTextLayout_Release(layout);
2500 IDWriteInlineObject_Release(trimm);
2501 IDWriteTextFormat_Release(format);
2502 IDWriteFactory_Release(factory);
2505 static void test_SetLocaleName(void)
2507 WCHAR buffW[LOCALE_NAME_MAX_LENGTH + 5];
2508 IDWriteTextFormat *format, *format2;
2509 IDWriteTextLayout *layout;
2510 DWRITE_TEXT_RANGE range;
2511 IDWriteFactory *factory;
2512 HRESULT hr;
2514 factory = create_factory();
2516 /* create format with mixed case locale name, get it back */
2517 hr = IDWriteFactory_CreateTextFormat(factory, L"Tahoma", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
2518 DWRITE_FONT_STRETCH_NORMAL, 10.0f, L"eN-uS", &format);
2519 ok(hr == S_OK, "Failed to create text format, hr %#lx.\n", hr);
2521 hr = IDWriteTextFormat_GetLocaleName(format, buffW, ARRAY_SIZE(buffW));
2522 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2523 ok(!lstrcmpW(buffW, L"en-us"), "Unexpected locale name %s.\n", wine_dbgstr_w(buffW));
2525 hr = IDWriteFactory_CreateTextLayout(factory, L"abcd", 4, format, 1000.0f, 1000.0f, &layout);
2526 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
2528 hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextFormat, (void**)&format2);
2529 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2531 hr = IDWriteTextFormat_GetLocaleName(format2, buffW, ARRAY_SIZE(buffW));
2532 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2533 ok(!lstrcmpW(buffW, L"en-us"), "Unexpected locale name %s.\n", wine_dbgstr_w(buffW));
2535 hr = IDWriteTextLayout_GetLocaleName(layout, 0, buffW, ARRAY_SIZE(buffW), NULL);
2536 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2537 ok(!lstrcmpW(buffW, L"en-us"), "Unexpected locale name %s.\n", wine_dbgstr_w(buffW));
2539 IDWriteTextFormat_Release(format2);
2540 IDWriteTextLayout_Release(layout);
2541 IDWriteTextFormat_Release(format);
2543 hr = IDWriteFactory_CreateTextFormat(factory, L"Tahoma", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
2544 DWRITE_FONT_STRETCH_NORMAL, 10.0f, L"en-us", &format);
2545 ok(hr == S_OK, "Failed to create text format, hr %#lx.\n", hr);
2547 hr = IDWriteFactory_CreateTextLayout(factory, L"abcd", 4, format, 1000.0f, 1000.0f, &layout);
2548 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
2550 range.startPosition = 0;
2551 range.length = 1;
2552 hr = IDWriteTextLayout_SetLocaleName(layout, L"en-us", range);
2553 ok(hr == S_OK, "Failed to set locale name, hr %#lx.\n", hr);
2555 hr = IDWriteTextLayout_SetLocaleName(layout, NULL, range);
2556 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
2558 /* invalid locale name is allowed */
2559 hr = IDWriteTextLayout_SetLocaleName(layout, L"abcd", range);
2560 ok(hr == S_OK, "Failed to set locale name, hr %#lx.\n", hr);
2562 hr = IDWriteTextLayout_GetLocaleName(layout, 0, NULL, 0, NULL);
2563 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
2565 if (0) /* crashes on native */
2566 hr = IDWriteTextLayout_GetLocaleName(layout, 0, NULL, 1, NULL);
2568 buffW[0] = 0;
2569 range.length = 0;
2570 hr = IDWriteTextLayout_GetLocaleName(layout, 0, buffW, ARRAY_SIZE(buffW), &range);
2571 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2572 ok(!lstrcmpW(buffW, L"abcd"), "Unexpected locale name %s.\n", wine_dbgstr_w(buffW));
2573 ok(range.startPosition == 0 && range.length == 1, "got %u,%u\n", range.startPosition, range.length);
2575 /* get with a shorter buffer */
2576 buffW[0] = 0xa;
2577 hr = IDWriteTextLayout_GetLocaleName(layout, 0, buffW, 1, NULL);
2578 ok(hr == E_NOT_SUFFICIENT_BUFFER, "Unexpected hr %#lx.\n", hr);
2579 ok(buffW[0] == 0, "got %x\n", buffW[0]);
2581 /* name is too long */
2582 lstrcpyW(buffW, L"abcd");
2583 while (lstrlenW(buffW) <= LOCALE_NAME_MAX_LENGTH)
2584 lstrcatW(buffW, L"abcd");
2586 range.startPosition = 0;
2587 range.length = 1;
2588 hr = IDWriteTextLayout_SetLocaleName(layout, buffW, range);
2589 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
2591 buffW[0] = 0;
2592 hr = IDWriteTextLayout_GetLocaleName(layout, 0, buffW, ARRAY_SIZE(buffW), NULL);
2593 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2594 ok(!lstrcmpW(buffW, L"abcd"), "Unexpected locale name %s.\n", wine_dbgstr_w(buffW));
2596 /* set initial locale name for whole text, except with a different casing */
2597 range.startPosition = 0;
2598 range.length = 4;
2599 hr = IDWriteTextLayout_SetLocaleName(layout, L"eN-uS", range);
2600 ok(hr == S_OK, "Failed to set locale name, hr %#lx.\n", hr);
2602 buffW[0] = 0;
2603 range.length = 0;
2604 hr = IDWriteTextLayout_GetLocaleName(layout, 0, buffW, ARRAY_SIZE(buffW), &range);
2605 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2606 ok(!lstrcmpW(buffW, L"en-us"), "Unexpected locale name %s.\n", wine_dbgstr_w(buffW));
2607 ok((range.startPosition == 0 && range.length == ~0u) ||
2608 broken(range.startPosition == 0 && range.length == 4) /* vista/win7 */, "got %u,%u\n", range.startPosition, range.length);
2610 /* check what's returned for positions after the text */
2611 buffW[0] = 0;
2612 range.length = 0;
2613 hr = IDWriteTextLayout_GetLocaleName(layout, 100, buffW, ARRAY_SIZE(buffW), &range);
2614 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2615 ok(!lstrcmpW(buffW, L"en-us"), "Unexpected locale name %s.\n", wine_dbgstr_w(buffW));
2616 ok((range.startPosition == 0 && range.length == ~0u) ||
2617 broken(range.startPosition == 4 && range.length == ~0u-4) /* vista/win7 */, "got %u,%u\n", range.startPosition, range.length);
2619 IDWriteTextLayout_Release(layout);
2620 IDWriteTextFormat_Release(format);
2621 IDWriteFactory_Release(factory);
2624 static void test_SetPairKerning(void)
2626 static const WCHAR strW[] = {'a','e',0x0300,'d',0}; /* accent grave */
2627 DWRITE_CLUSTER_METRICS clusters[4];
2628 IDWriteTextLayout1 *layout1;
2629 IDWriteTextFormat *format;
2630 IDWriteTextLayout *layout;
2631 DWRITE_TEXT_RANGE range;
2632 IDWriteFactory *factory;
2633 BOOL kerning;
2634 UINT32 count;
2635 HRESULT hr;
2637 factory = create_factory();
2639 hr = IDWriteFactory_CreateTextFormat(factory, L"Tahoma", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
2640 DWRITE_FONT_STRETCH_NORMAL, 10.0f, L"en-us", &format);
2641 ok(hr == S_OK, "Failed to create text format, hr %#lx.\n", hr);
2643 hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 1000.0, 1000.0, &layout);
2644 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2645 IDWriteTextFormat_Release(format);
2647 hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextLayout1, (void**)&layout1);
2648 IDWriteTextLayout_Release(layout);
2650 if (hr != S_OK) {
2651 win_skip("SetPairKerning() is not supported.\n");
2652 IDWriteFactory_Release(factory);
2653 return;
2656 if (0) { /* crashes on native */
2657 hr = IDWriteTextLayout1_GetPairKerning(layout1, 0, NULL, NULL);
2658 hr = IDWriteTextLayout1_GetPairKerning(layout1, 0, NULL, &range);
2661 hr = IDWriteTextLayout1_GetPairKerning(layout1, 0, &kerning, NULL);
2662 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2664 range.startPosition = 0;
2665 range.length = 0;
2666 kerning = TRUE;
2667 hr = IDWriteTextLayout1_GetPairKerning(layout1, 0, &kerning, &range);
2668 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2669 ok(!kerning, "got %d\n", kerning);
2670 ok(range.length == ~0u, "got %u\n", range.length);
2672 count = 0;
2673 hr = IDWriteTextLayout1_GetClusterMetrics(layout1, clusters, 4, &count);
2674 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2675 ok(count == 3, "Unexpected cluster count %u.\n", count);
2676 ok(clusters[0].length == 1, "got %u\n", clusters[0].length);
2677 ok(clusters[1].length == 2, "got %u\n", clusters[1].length);
2678 ok(clusters[2].length == 1, "got %u\n", clusters[2].length);
2680 /* pair kerning flag participates in itemization - combining characters
2681 breaks */
2682 range.startPosition = 0;
2683 range.length = 2;
2684 hr = IDWriteTextLayout1_SetPairKerning(layout1, 2, range);
2685 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2687 kerning = FALSE;
2688 hr = IDWriteTextLayout1_GetPairKerning(layout1, 0, &kerning, &range);
2689 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2690 ok(kerning == TRUE, "got %d\n", kerning);
2692 count = 0;
2693 hr = IDWriteTextLayout1_GetClusterMetrics(layout1, clusters, 4, &count);
2694 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2695 ok(count == 4, "got %u\n", count);
2696 ok(clusters[0].length == 1, "got %u\n", clusters[0].length);
2697 ok(clusters[1].length == 1, "got %u\n", clusters[1].length);
2698 ok(clusters[2].length == 1, "got %u\n", clusters[2].length);
2699 ok(clusters[3].length == 1, "got %u\n", clusters[3].length);
2701 IDWriteTextLayout1_Release(layout1);
2702 IDWriteFactory_Release(factory);
2705 static void test_SetVerticalGlyphOrientation(void)
2707 DWRITE_VERTICAL_GLYPH_ORIENTATION orientation;
2708 IDWriteTextLayout2 *layout2;
2709 IDWriteTextFormat1 *format1;
2710 IDWriteTextFormat *format;
2711 IDWriteTextLayout *layout;
2712 IDWriteFactory *factory;
2713 HRESULT hr;
2715 factory = create_factory();
2717 hr = IDWriteFactory_CreateTextFormat(factory, L"Tahoma", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
2718 DWRITE_FONT_STRETCH_NORMAL, 10.0f, L"en-us", &format);
2719 ok(hr == S_OK, "Failed to create text format, hr %#lx.\n", hr);
2721 hr = IDWriteFactory_CreateTextLayout(factory, L"abcd", 4, format, 1000.0f, 1000.0f, &layout);
2722 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
2723 IDWriteTextFormat_Release(format);
2725 hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextLayout2, (void**)&layout2);
2726 IDWriteTextLayout_Release(layout);
2728 if (hr != S_OK) {
2729 win_skip("SetVerticalGlyphOrientation() is not supported.\n");
2730 IDWriteFactory_Release(factory);
2731 return;
2734 orientation = IDWriteTextLayout2_GetVerticalGlyphOrientation(layout2);
2735 ok(orientation == DWRITE_VERTICAL_GLYPH_ORIENTATION_DEFAULT, "got %d\n", orientation);
2737 hr = IDWriteTextLayout2_SetVerticalGlyphOrientation(layout2, DWRITE_VERTICAL_GLYPH_ORIENTATION_STACKED+1);
2738 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
2740 hr = IDWriteTextLayout2_QueryInterface(layout2, &IID_IDWriteTextFormat1, (void **)&format1);
2741 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2743 orientation = IDWriteTextFormat1_GetVerticalGlyphOrientation(format1);
2744 ok(orientation == DWRITE_VERTICAL_GLYPH_ORIENTATION_DEFAULT, "Unexpected orientation %d.\n", orientation);
2746 hr = IDWriteTextLayout2_SetVerticalGlyphOrientation(layout2, DWRITE_VERTICAL_GLYPH_ORIENTATION_STACKED);
2747 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2749 orientation = IDWriteTextLayout2_GetVerticalGlyphOrientation(layout2);
2750 ok(orientation == DWRITE_VERTICAL_GLYPH_ORIENTATION_STACKED, "Unexpected orientation %d.\n", orientation);
2752 orientation = IDWriteTextFormat1_GetVerticalGlyphOrientation(format1);
2753 ok(orientation == DWRITE_VERTICAL_GLYPH_ORIENTATION_STACKED, "Unexpected orientation %d.\n", orientation);
2755 hr = IDWriteTextFormat1_SetVerticalGlyphOrientation(format1, DWRITE_VERTICAL_GLYPH_ORIENTATION_DEFAULT);
2756 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2758 orientation = IDWriteTextLayout2_GetVerticalGlyphOrientation(layout2);
2759 ok(orientation == DWRITE_VERTICAL_GLYPH_ORIENTATION_DEFAULT, "Unexpected orientation %d.\n", orientation);
2761 IDWriteTextFormat1_Release(format1);
2763 IDWriteTextLayout2_Release(layout2);
2764 IDWriteFactory_Release(factory);
2767 static void test_DetermineMinWidth(void)
2769 struct minwidth_test {
2770 const WCHAR text[10]; /* text to create a layout for */
2771 const WCHAR mintext[10]; /* text that represents sequence of minimal width */
2772 } minwidth_tests[] = {
2773 { {' ','a','b',' ',0}, {'a','b',0} },
2774 { {'a','\n',' ',' ',0}, {'a',0} },
2775 { {'a','\n',' ',' ','b',0}, {'b',0} },
2776 { {'a','b','c','\n',' ',' ','b',0}, {'a','b','c',0} },
2778 DWRITE_CLUSTER_METRICS metrics[10];
2779 IDWriteTextFormat *format;
2780 IDWriteTextLayout *layout;
2781 IDWriteFactory *factory;
2782 UINT32 count, i, j;
2783 FLOAT minwidth;
2784 HRESULT hr;
2786 factory = create_factory();
2788 hr = IDWriteFactory_CreateTextFormat(factory, L"Tahoma", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
2789 DWRITE_FONT_STRETCH_NORMAL, 10.0f, L"en-us", &format);
2790 ok(hr == S_OK, "Failed to create text format, hr %#lx.\n", hr);
2792 hr = IDWriteFactory_CreateTextLayout(factory, L"abcd", 4, format, 1000.0f, 1000.0f, &layout);
2793 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
2795 hr = IDWriteTextLayout_DetermineMinWidth(layout, NULL);
2796 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
2797 IDWriteTextLayout_Release(layout);
2799 /* empty string */
2800 hr = IDWriteFactory_CreateTextLayout(factory, L"abcd", 0, format, 100.0f, 100.0f, &layout);
2801 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
2803 minwidth = 1.0f;
2804 hr = IDWriteTextLayout_DetermineMinWidth(layout, &minwidth);
2805 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2806 ok(minwidth == 0.0f, "got %f\n", minwidth);
2807 IDWriteTextLayout_Release(layout);
2809 for (i = 0; i < ARRAY_SIZE(minwidth_tests); i++) {
2810 FLOAT width = 0.0f;
2812 /* measure expected width */
2813 hr = IDWriteFactory_CreateTextLayout(factory, minwidth_tests[i].mintext, lstrlenW(minwidth_tests[i].mintext), format, 1000.0f, 1000.0f, &layout);
2814 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2816 hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, ARRAY_SIZE(metrics), &count);
2817 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2819 for (j = 0; j < count; j++)
2820 width += metrics[j].width;
2822 IDWriteTextLayout_Release(layout);
2824 hr = IDWriteFactory_CreateTextLayout(factory, minwidth_tests[i].text, lstrlenW(minwidth_tests[i].text), format, 1000.0f, 1000.0f, &layout);
2825 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2827 minwidth = 0.0f;
2828 hr = IDWriteTextLayout_DetermineMinWidth(layout, &minwidth);
2829 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2830 ok(minwidth == width, "test %u: expected width %f, got %f\n", i, width, minwidth);
2832 IDWriteTextLayout_Release(layout);
2835 IDWriteTextFormat_Release(format);
2836 IDWriteFactory_Release(factory);
2839 static void test_SetFontSize(void)
2841 IDWriteTextFormat *format;
2842 IDWriteTextLayout *layout;
2843 IDWriteFactory *factory;
2844 DWRITE_TEXT_RANGE r;
2845 FLOAT size;
2846 HRESULT hr;
2848 factory = create_factory();
2850 hr = IDWriteFactory_CreateTextFormat(factory, L"Tahoma", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
2851 DWRITE_FONT_STRETCH_NORMAL, 10.0f, L"en-us", &format);
2852 ok(hr == S_OK, "Failed to create text format, hr %#lx.\n", hr);
2854 hr = IDWriteFactory_CreateTextLayout(factory, L"abcd", 4, format, 1000.0f, 1000.0f, &layout);
2855 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
2857 /* negative/zero size */
2858 r.startPosition = 1;
2859 r.length = 1;
2860 hr = IDWriteTextLayout_SetFontSize(layout, -15.0, r);
2861 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
2863 hr = IDWriteTextLayout_SetFontSize(layout, 0.0, r);
2864 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
2866 r.startPosition = 1;
2867 r.length = 0;
2868 size = 0.0;
2869 hr = IDWriteTextLayout_GetFontSize(layout, 0, &size, &r);
2870 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2871 ok(r.startPosition == 0 && r.length == ~0u, "got %u, %u\n", r.startPosition, r.length);
2872 ok(size == 10.0, "got %.2f\n", size);
2874 r.startPosition = 1;
2875 r.length = 1;
2876 hr = IDWriteTextLayout_SetFontSize(layout, 15.0, r);
2877 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2879 /* zero length range */
2880 r.startPosition = 1;
2881 r.length = 0;
2882 hr = IDWriteTextLayout_SetFontSize(layout, 123.0, r);
2883 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2885 size = 0.0;
2886 hr = IDWriteTextLayout_GetFontSize(layout, 1, &size, &r);
2887 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2888 ok(size == 15.0, "got %.2f\n", size);
2890 r.startPosition = 0;
2891 r.length = 4;
2892 hr = IDWriteTextLayout_SetFontSize(layout, 15.0, r);
2893 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2895 size = 0.0;
2896 hr = IDWriteTextLayout_GetFontSize(layout, 1, &size, &r);
2897 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2898 ok(size == 15.0, "got %.2f\n", size);
2900 size = 0.0;
2901 hr = IDWriteTextLayout_GetFontSize(layout, 0, &size, &r);
2902 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2903 ok(r.startPosition == 0 && r.length == 4, "got %u, %u\n", r.startPosition, r.length);
2904 ok(size == 15.0, "got %.2f\n", size);
2906 size = 15.0;
2907 r.startPosition = r.length = 0;
2908 hr = IDWriteTextLayout_GetFontSize(layout, 20, &size, &r);
2909 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2910 ok(r.startPosition == 4 && r.length == ~0u-4, "got %u, %u\n", r.startPosition, r.length);
2911 ok(size == 10.0, "got %.2f\n", size);
2913 r.startPosition = 100;
2914 r.length = 4;
2915 hr = IDWriteTextLayout_SetFontSize(layout, 25.0, r);
2916 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2918 size = 15.0;
2919 r.startPosition = r.length = 0;
2920 hr = IDWriteTextLayout_GetFontSize(layout, 100, &size, &r);
2921 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2922 ok(r.startPosition == 100 && r.length == 4, "got %u, %u\n", r.startPosition, r.length);
2923 ok(size == 25.0, "got %.2f\n", size);
2925 IDWriteTextLayout_Release(layout);
2926 IDWriteTextFormat_Release(format);
2927 IDWriteFactory_Release(factory);
2930 static void test_SetFontFamilyName(void)
2932 IDWriteTextFormat *format;
2933 IDWriteTextLayout *layout;
2934 IDWriteFactory *factory;
2935 DWRITE_TEXT_RANGE r;
2936 WCHAR nameW[50];
2937 HRESULT hr;
2939 factory = create_factory();
2941 hr = IDWriteFactory_CreateTextFormat(factory, L"Tahoma", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
2942 DWRITE_FONT_STRETCH_NORMAL, 10.0f, L"en-us", &format);
2943 ok(hr == S_OK, "Failed to create text format, hr %#lx.\n", hr);
2945 hr = IDWriteFactory_CreateTextLayout(factory, L"abcd", 4, format, 1000.0f, 1000.0f, &layout);
2946 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
2948 /* NULL name */
2949 r.startPosition = 1;
2950 r.length = 1;
2951 hr = IDWriteTextLayout_SetFontFamilyName(layout, NULL, r);
2952 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
2954 r.startPosition = 1;
2955 r.length = 0;
2956 nameW[0] = 0;
2957 hr = IDWriteTextLayout_GetFontFamilyName(layout, 1, nameW, ARRAY_SIZE(nameW), &r);
2958 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2959 ok(r.startPosition == 0 && r.length == ~0u, "got %u, %u\n", r.startPosition, r.length);
2961 /* set name only different in casing */
2962 r.startPosition = 1;
2963 r.length = 1;
2964 hr = IDWriteTextLayout_SetFontFamilyName(layout, L"TaHoma", r);
2965 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2967 /* zero length range */
2968 r.startPosition = 1;
2969 r.length = 0;
2970 hr = IDWriteTextLayout_SetFontFamilyName(layout, L"Arial", r);
2971 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2973 r.startPosition = 0;
2974 r.length = 0;
2975 nameW[0] = 0;
2976 hr = IDWriteTextLayout_GetFontFamilyName(layout, 1, nameW, ARRAY_SIZE(nameW), &r);
2977 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2978 ok(!lstrcmpW(nameW, L"TaHoma"), "Unexpected family name %s.\n", wine_dbgstr_w(nameW));
2979 ok(r.startPosition == 1 && r.length == 1, "got %u, %u\n", r.startPosition, r.length);
2981 r.startPosition = 1;
2982 r.length = 1;
2983 hr = IDWriteTextLayout_SetFontFamilyName(layout, L"Arial", r);
2984 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2986 r.startPosition = 1;
2987 r.length = 0;
2988 hr = IDWriteTextLayout_GetFontFamilyName(layout, 1, nameW, ARRAY_SIZE(nameW), &r);
2989 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2990 ok(r.startPosition == 1 && r.length == 1, "got %u, %u\n", r.startPosition, r.length);
2992 r.startPosition = 0;
2993 r.length = 4;
2994 hr = IDWriteTextLayout_SetFontFamilyName(layout, L"Arial", r);
2995 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2997 nameW[0] = 0;
2998 hr = IDWriteTextLayout_GetFontFamilyName(layout, 1, nameW, ARRAY_SIZE(nameW), &r);
2999 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3000 ok(r.startPosition == 0 && r.length == 4, "got %u, %u\n", r.startPosition, r.length);
3001 ok(!lstrcmpW(nameW, L"Arial"), "Unexpected family name %s.\n", wine_dbgstr_w(nameW));
3003 IDWriteTextLayout_Release(layout);
3004 IDWriteTextFormat_Release(format);
3005 IDWriteFactory_Release(factory);
3008 static void test_SetFontStyle(void)
3010 IDWriteTextFormat *format;
3011 IDWriteTextLayout *layout;
3012 IDWriteFactory *factory;
3013 DWRITE_FONT_STYLE style;
3014 DWRITE_TEXT_RANGE r;
3015 HRESULT hr;
3017 factory = create_factory();
3019 hr = IDWriteFactory_CreateTextFormat(factory, L"Tahoma", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
3020 DWRITE_FONT_STRETCH_NORMAL, 10.0f, L"en-us", &format);
3021 ok(hr == S_OK, "Failed to create text format, hr %#lx.\n", hr);
3023 hr = IDWriteFactory_CreateTextLayout(factory, L"abcd", 4, format, 1000.0f, 1000.0f, &layout);
3024 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
3026 /* invalid style value */
3027 r.startPosition = 1;
3028 r.length = 1;
3029 hr = IDWriteTextLayout_SetFontStyle(layout, DWRITE_FONT_STYLE_ITALIC+1, r);
3030 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
3032 r.startPosition = 1;
3033 r.length = 0;
3034 hr = IDWriteTextLayout_GetFontStyle(layout, 0, &style, &r);
3035 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3036 ok(r.startPosition == 0 && r.length == ~0u, "got %u, %u\n", r.startPosition, r.length);
3037 ok(style == DWRITE_FONT_STYLE_NORMAL, "got %d\n", style);
3039 r.startPosition = 1;
3040 r.length = 1;
3041 hr = IDWriteTextLayout_SetFontStyle(layout, DWRITE_FONT_STYLE_ITALIC, r);
3042 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3044 /* zero length range */
3045 r.startPosition = 1;
3046 r.length = 0;
3047 hr = IDWriteTextLayout_SetFontStyle(layout, DWRITE_FONT_STYLE_NORMAL, r);
3048 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3050 style = DWRITE_FONT_STYLE_NORMAL;
3051 hr = IDWriteTextLayout_GetFontStyle(layout, 1, &style, &r);
3052 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3053 ok(style == DWRITE_FONT_STYLE_ITALIC, "got %d\n", style);
3055 r.startPosition = 0;
3056 r.length = 4;
3057 hr = IDWriteTextLayout_SetFontStyle(layout, DWRITE_FONT_STYLE_OBLIQUE, r);
3058 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3060 style = DWRITE_FONT_STYLE_ITALIC;
3061 hr = IDWriteTextLayout_GetFontStyle(layout, 1, &style, &r);
3062 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3063 ok(style == DWRITE_FONT_STYLE_OBLIQUE, "got %d\n", style);
3065 style = DWRITE_FONT_STYLE_ITALIC;
3066 hr = IDWriteTextLayout_GetFontStyle(layout, 0, &style, &r);
3067 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3068 ok(r.startPosition == 0 && r.length == 4, "got %u, %u\n", r.startPosition, r.length);
3069 ok(style == DWRITE_FONT_STYLE_OBLIQUE, "got %d\n", style);
3071 style = DWRITE_FONT_STYLE_ITALIC;
3072 r.startPosition = r.length = 0;
3073 hr = IDWriteTextLayout_GetFontStyle(layout, 20, &style, &r);
3074 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3075 ok(r.startPosition == 4 && r.length == ~0u-4, "got %u, %u\n", r.startPosition, r.length);
3076 ok(style == DWRITE_FONT_STYLE_NORMAL, "got %d\n", style);
3078 r.startPosition = 100;
3079 r.length = 4;
3080 hr = IDWriteTextLayout_SetFontStyle(layout, DWRITE_FONT_STYLE_OBLIQUE, r);
3081 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3083 style = DWRITE_FONT_STYLE_NORMAL;
3084 r.startPosition = r.length = 0;
3085 hr = IDWriteTextLayout_GetFontStyle(layout, 100, &style, &r);
3086 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3087 ok(r.startPosition == 100 && r.length == 4, "got %u, %u\n", r.startPosition, r.length);
3088 ok(style == DWRITE_FONT_STYLE_OBLIQUE, "got %d\n", style);
3090 IDWriteTextLayout_Release(layout);
3091 IDWriteTextFormat_Release(format);
3092 IDWriteFactory_Release(factory);
3095 static void test_SetFontStretch(void)
3097 DWRITE_FONT_STRETCH stretch;
3098 IDWriteTextFormat *format;
3099 IDWriteTextLayout *layout;
3100 IDWriteFactory *factory;
3101 DWRITE_TEXT_RANGE r;
3102 HRESULT hr;
3104 factory = create_factory();
3106 hr = IDWriteFactory_CreateTextFormat(factory, L"Tahoma", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
3107 DWRITE_FONT_STRETCH_NORMAL, 10.0f, L"en-us", &format);
3108 ok(hr == S_OK, "Failed to create text format, hr %#lx.\n", hr);
3110 hr = IDWriteFactory_CreateTextLayout(factory, L"abcd", 4, format, 1000.0f, 1000.0f, &layout);
3111 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
3113 /* invalid stretch value */
3114 r.startPosition = 1;
3115 r.length = 1;
3116 hr = IDWriteTextLayout_SetFontStretch(layout, DWRITE_FONT_STRETCH_ULTRA_EXPANDED+1, r);
3117 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
3119 r.startPosition = 1;
3120 r.length = 0;
3121 stretch = DWRITE_FONT_STRETCH_UNDEFINED;
3122 hr = IDWriteTextLayout_GetFontStretch(layout, 0, &stretch, &r);
3123 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3124 ok(r.startPosition == 0 && r.length == ~0u, "got %u, %u\n", r.startPosition, r.length);
3125 ok(stretch == DWRITE_FONT_STRETCH_NORMAL, "got %d\n", stretch);
3127 r.startPosition = 1;
3128 r.length = 1;
3129 hr = IDWriteTextLayout_SetFontStretch(layout, DWRITE_FONT_STRETCH_CONDENSED, r);
3130 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3132 /* zero length range */
3133 r.startPosition = 1;
3134 r.length = 0;
3135 hr = IDWriteTextLayout_SetFontStretch(layout, DWRITE_FONT_STRETCH_NORMAL, r);
3136 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3138 stretch = DWRITE_FONT_STRETCH_UNDEFINED;
3139 hr = IDWriteTextLayout_GetFontStretch(layout, 1, &stretch, &r);
3140 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3141 ok(stretch == DWRITE_FONT_STRETCH_CONDENSED, "got %d\n", stretch);
3143 r.startPosition = 0;
3144 r.length = 4;
3145 hr = IDWriteTextLayout_SetFontStretch(layout, DWRITE_FONT_STRETCH_EXPANDED, r);
3146 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3148 stretch = DWRITE_FONT_STRETCH_UNDEFINED;
3149 hr = IDWriteTextLayout_GetFontStretch(layout, 1, &stretch, &r);
3150 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3151 ok(stretch == DWRITE_FONT_STRETCH_EXPANDED, "got %d\n", stretch);
3153 stretch = DWRITE_FONT_STRETCH_UNDEFINED;
3154 hr = IDWriteTextLayout_GetFontStretch(layout, 0, &stretch, &r);
3155 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3156 ok(r.startPosition == 0 && r.length == 4, "got %u, %u\n", r.startPosition, r.length);
3157 ok(stretch == DWRITE_FONT_STRETCH_EXPANDED, "got %d\n", stretch);
3159 stretch = DWRITE_FONT_STRETCH_UNDEFINED;
3160 r.startPosition = r.length = 0;
3161 hr = IDWriteTextLayout_GetFontStretch(layout, 20, &stretch, &r);
3162 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3163 ok(r.startPosition == 4 && r.length == ~0u-4, "got %u, %u\n", r.startPosition, r.length);
3164 ok(stretch == DWRITE_FONT_STRETCH_NORMAL, "got %d\n", stretch);
3166 r.startPosition = 100;
3167 r.length = 4;
3168 hr = IDWriteTextLayout_SetFontStretch(layout, DWRITE_FONT_STRETCH_EXPANDED, r);
3169 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3171 stretch = DWRITE_FONT_STRETCH_UNDEFINED;
3172 r.startPosition = r.length = 0;
3173 hr = IDWriteTextLayout_GetFontStretch(layout, 100, &stretch, &r);
3174 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3175 ok(r.startPosition == 100 && r.length == 4, "got %u, %u\n", r.startPosition, r.length);
3176 ok(stretch == DWRITE_FONT_STRETCH_EXPANDED, "got %d\n", stretch);
3178 /* trying to set undefined value */
3179 r.startPosition = 0;
3180 r.length = 2;
3181 hr = IDWriteTextLayout_SetFontStretch(layout, DWRITE_FONT_STRETCH_UNDEFINED, r);
3182 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
3184 IDWriteTextLayout_Release(layout);
3185 IDWriteTextFormat_Release(format);
3186 IDWriteFactory_Release(factory);
3189 static void test_SetStrikethrough(void)
3191 IDWriteTextFormat *format;
3192 IDWriteTextLayout *layout;
3193 IDWriteFactory *factory;
3194 DWRITE_TEXT_RANGE r;
3195 BOOL value;
3196 HRESULT hr;
3198 factory = create_factory();
3200 hr = IDWriteFactory_CreateTextFormat(factory, L"Tahoma", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
3201 DWRITE_FONT_STRETCH_NORMAL, 10.0f, L"en-us", &format);
3202 ok(hr == S_OK, "Failed to create text format, hr %#lx.\n", hr);
3204 hr = IDWriteFactory_CreateTextLayout(factory, L"abcd", 4, format, 1000.0f, 1000.0f, &layout);
3205 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
3207 r.startPosition = 1;
3208 r.length = 0;
3209 value = TRUE;
3210 hr = IDWriteTextLayout_GetStrikethrough(layout, 0, &value, &r);
3211 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3212 ok(r.startPosition == 0 && r.length == ~0u, "got %u, %u\n", r.startPosition, r.length);
3213 ok(value == FALSE, "got %d\n", value);
3215 r.startPosition = 1;
3216 r.length = 1;
3217 hr = IDWriteTextLayout_SetStrikethrough(layout, TRUE, r);
3218 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3220 value = FALSE;
3221 hr = IDWriteTextLayout_GetStrikethrough(layout, 1, &value, &r);
3222 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3223 ok(value == TRUE, "got %d\n", value);
3224 ok(r.startPosition == 1 && r.length == 1, "got %u, %u\n", r.startPosition, r.length);
3226 value = TRUE;
3227 r.startPosition = r.length = 0;
3228 hr = IDWriteTextLayout_GetStrikethrough(layout, 20, &value, &r);
3229 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3230 ok(r.startPosition == 2 && r.length == ~0u-2, "got %u, %u\n", r.startPosition, r.length);
3231 ok(value == FALSE, "got %d\n", value);
3233 r.startPosition = 100;
3234 r.length = 4;
3235 hr = IDWriteTextLayout_SetStrikethrough(layout, TRUE, r);
3236 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3238 value = FALSE;
3239 r.startPosition = r.length = 0;
3240 hr = IDWriteTextLayout_GetStrikethrough(layout, 100, &value, &r);
3241 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3242 ok(r.startPosition == 100 && r.length == 4, "got %u, %u\n", r.startPosition, r.length);
3243 ok(value == TRUE, "got %d\n", value);
3245 IDWriteTextLayout_Release(layout);
3246 IDWriteTextFormat_Release(format);
3247 IDWriteFactory_Release(factory);
3250 static void test_GetMetrics(void)
3252 static const WCHAR str2W[] = {0x2066,')',')',0x661,'(',0x627,')',0};
3253 DWRITE_CLUSTER_METRICS clusters[4];
3254 DWRITE_TEXT_METRICS metrics;
3255 IDWriteTextFormat *format;
3256 IDWriteTextLayout *layout;
3257 IDWriteFactory *factory;
3258 UINT32 count, i;
3259 FLOAT width;
3260 HRESULT hr;
3262 factory = create_factory();
3264 hr = IDWriteFactory_CreateTextFormat(factory, L"Tahoma", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
3265 DWRITE_FONT_STRETCH_NORMAL, 10.0f, L"en-us", &format);
3266 ok(hr == S_OK, "Failed to create text format, hr %#lx.\n", hr);
3268 hr = IDWriteFactory_CreateTextLayout(factory, L"abcd", 4, format, 500.0f, 1000.0f, &layout);
3269 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
3271 count = 0;
3272 hr = IDWriteTextLayout_GetClusterMetrics(layout, clusters, 4, &count);
3273 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3274 ok(count == 4, "got %u\n", count);
3275 for (i = 0, width = 0.0; i < count; i++)
3276 width += clusters[i].width;
3278 memset(&metrics, 0xcc, sizeof(metrics));
3279 hr = IDWriteTextLayout_GetMetrics(layout, &metrics);
3280 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3281 ok(metrics.left == 0.0, "got %.2f\n", metrics.left);
3282 ok(metrics.top == 0.0, "got %.2f\n", metrics.top);
3283 ok(metrics.width == width, "got %.2f, expected %.2f\n", metrics.width, width);
3284 ok(metrics.widthIncludingTrailingWhitespace == width, "got %.2f, expected %.2f\n",
3285 metrics.widthIncludingTrailingWhitespace, width);
3286 ok(metrics.height > 0.0, "got %.2f\n", metrics.height);
3287 ok(metrics.layoutWidth == 500.0, "got %.2f\n", metrics.layoutWidth);
3288 ok(metrics.layoutHeight == 1000.0, "got %.2f\n", metrics.layoutHeight);
3289 ok(metrics.maxBidiReorderingDepth == 1, "got %u\n", metrics.maxBidiReorderingDepth);
3290 ok(metrics.lineCount == 1, "got %u\n", metrics.lineCount);
3292 IDWriteTextLayout_Release(layout);
3294 /* a string with more complex bidi sequence */
3295 hr = IDWriteFactory_CreateTextLayout(factory, str2W, 7, format, 500.0, 1000.0, &layout);
3296 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3298 memset(&metrics, 0xcc, sizeof(metrics));
3299 metrics.maxBidiReorderingDepth = 0;
3300 hr = IDWriteTextLayout_GetMetrics(layout, &metrics);
3301 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3302 ok(metrics.left == 0.0, "got %.2f\n", metrics.left);
3303 ok(metrics.top == 0.0, "got %.2f\n", metrics.top);
3304 ok(metrics.width > 0.0, "got %.2f\n", metrics.width);
3305 ok(metrics.widthIncludingTrailingWhitespace > 0.0, "got %.2f\n", metrics.widthIncludingTrailingWhitespace);
3306 ok(metrics.height > 0.0, "got %.2f\n", metrics.height);
3307 ok(metrics.layoutWidth == 500.0, "got %.2f\n", metrics.layoutWidth);
3308 ok(metrics.layoutHeight == 1000.0, "got %.2f\n", metrics.layoutHeight);
3309 todo_wine
3310 ok(metrics.maxBidiReorderingDepth > 1, "got %u\n", metrics.maxBidiReorderingDepth);
3311 ok(metrics.lineCount == 1, "got %u\n", metrics.lineCount);
3313 IDWriteTextLayout_Release(layout);
3315 /* single cluster layout */
3316 hr = IDWriteFactory_CreateTextLayout(factory, L"a", 1, format, 500.0f, 1000.0f, &layout);
3317 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
3319 count = 0;
3320 hr = IDWriteTextLayout_GetClusterMetrics(layout, clusters, 1, &count);
3321 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3322 ok(count == 1, "got %u\n", count);
3324 memset(&metrics, 0xcc, sizeof(metrics));
3325 hr = IDWriteTextLayout_GetMetrics(layout, &metrics);
3326 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3327 ok(metrics.left == 0.0, "got %.2f\n", metrics.left);
3328 ok(metrics.top == 0.0, "got %.2f\n", metrics.top);
3329 ok(metrics.width == clusters[0].width, "got %.2f, expected %.2f\n", metrics.width, clusters[0].width);
3330 ok(metrics.widthIncludingTrailingWhitespace == clusters[0].width, "got %.2f\n", metrics.widthIncludingTrailingWhitespace);
3331 ok(metrics.height > 0.0, "got %.2f\n", metrics.height);
3332 ok(metrics.layoutWidth == 500.0, "got %.2f\n", metrics.layoutWidth);
3333 ok(metrics.layoutHeight == 1000.0, "got %.2f\n", metrics.layoutHeight);
3334 ok(metrics.maxBidiReorderingDepth == 1, "got %u\n", metrics.maxBidiReorderingDepth);
3335 ok(metrics.lineCount == 1, "got %u\n", metrics.lineCount);
3336 IDWriteTextLayout_Release(layout);
3338 /* Whitespace only. */
3339 hr = IDWriteFactory_CreateTextLayout(factory, L" ", 1, format, 500.0f, 1000.0f, &layout);
3340 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
3342 memset(&metrics, 0xcc, sizeof(metrics));
3343 hr = IDWriteTextLayout_GetMetrics(layout, &metrics);
3344 ok(hr == S_OK, "Failed to get layout metrics, hr %#lx.\n", hr);
3345 ok(metrics.left == 0.0f, "Unexpected value for left %f.\n", metrics.left);
3346 ok(metrics.top == 0.0f, "Unexpected value for top %f.\n", metrics.top);
3347 ok(metrics.width == 0.0f, "Unexpected width %f.\n", metrics.width);
3348 ok(metrics.widthIncludingTrailingWhitespace > 0.0f, "Unexpected full width %f.\n",
3349 metrics.widthIncludingTrailingWhitespace);
3350 ok(metrics.height > 0.0, "Unexpected height %f.\n", metrics.height);
3351 ok(metrics.layoutWidth == 500.0, "Unexpected box width %f.\n", metrics.layoutWidth);
3352 ok(metrics.layoutHeight == 1000.0, "Unexpected box height %f.\n", metrics.layoutHeight);
3353 ok(metrics.maxBidiReorderingDepth == 1, "Unexpected reordering depth %u.\n", metrics.maxBidiReorderingDepth);
3354 ok(metrics.lineCount == 1, "Unexpected line count %u.\n", metrics.lineCount);
3355 IDWriteTextLayout_Release(layout);
3357 IDWriteTextFormat_Release(format);
3358 IDWriteFactory_Release(factory);
3361 static void test_SetFlowDirection(void)
3363 DWRITE_READING_DIRECTION reading;
3364 DWRITE_FLOW_DIRECTION flow;
3365 IDWriteTextFormat *format;
3366 IDWriteTextLayout *layout;
3367 IDWriteFactory *factory;
3368 HRESULT hr;
3370 factory = create_factory();
3372 hr = IDWriteFactory_CreateTextFormat(factory, L"Tahoma", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
3373 DWRITE_FONT_STRETCH_NORMAL, 10.0f, L"en-us", &format);
3374 ok(hr == S_OK, "Failed to create text format, hr %#lx.\n", hr);
3376 flow = IDWriteTextFormat_GetFlowDirection(format);
3377 ok(flow == DWRITE_FLOW_DIRECTION_TOP_TO_BOTTOM, "got %d\n", flow);
3379 reading = IDWriteTextFormat_GetReadingDirection(format);
3380 ok(reading == DWRITE_READING_DIRECTION_LEFT_TO_RIGHT, "got %d\n", reading);
3382 hr = IDWriteFactory_CreateTextLayout(factory, L"abcd", 4, format, 500.0f, 1000.0f, &layout);
3383 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
3384 IDWriteTextLayout_Release(layout);
3386 hr = IDWriteTextFormat_SetFlowDirection(format, DWRITE_FLOW_DIRECTION_LEFT_TO_RIGHT);
3387 ok(hr == S_OK || broken(hr == E_INVALIDARG) /* vista,win7 */, "Unexpected hr %#lx.\n", hr);
3388 if (hr == S_OK)
3390 hr = IDWriteFactory_CreateTextLayout(factory, L"abcd", 4, format, 500.0f, 1000.0f, &layout);
3391 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
3392 IDWriteTextLayout_Release(layout);
3394 hr = IDWriteTextFormat_SetReadingDirection(format, DWRITE_READING_DIRECTION_TOP_TO_BOTTOM);
3395 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3397 hr = IDWriteTextFormat_SetFlowDirection(format, DWRITE_FLOW_DIRECTION_TOP_TO_BOTTOM);
3398 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3400 hr = IDWriteFactory_CreateTextLayout(factory, L"abcd", 4, format, 500.0f, 1000.0f, &layout);
3401 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
3402 IDWriteTextLayout_Release(layout);
3404 else
3405 win_skip("DWRITE_FLOW_DIRECTION_LEFT_TO_RIGHT is not supported\n");
3407 IDWriteTextFormat_Release(format);
3408 IDWriteFactory_Release(factory);
3411 static const struct drawcall_entry draweffect_seq[] = {
3412 { DRAW_GLYPHRUN|DRAW_EFFECT, {'a','e',0x0300,0}, {'e','n','-','u','s',0}, 2 },
3413 { DRAW_GLYPHRUN, {'d',0}, {'e','n','-','u','s',0}, 1 },
3414 { DRAW_LAST_KIND }
3417 static const struct drawcall_entry draweffect2_seq[] = {
3418 { DRAW_GLYPHRUN|DRAW_EFFECT, {'a','e',0}, {'e','n','-','u','s',0}, 2 },
3419 { DRAW_GLYPHRUN, {'c','d',0}, {'e','n','-','u','s',0}, 2 },
3420 { DRAW_LAST_KIND }
3423 static const struct drawcall_entry draweffect3_seq[] = {
3424 { DRAW_INLINE|DRAW_EFFECT },
3425 { DRAW_LAST_KIND }
3428 static const struct drawcall_entry draweffect4_seq[] = {
3429 { DRAW_INLINE },
3430 { DRAW_LAST_KIND }
3433 static void test_SetDrawingEffect(void)
3435 static const WCHAR strW[] = {'a','e',0x0300,'d',0}; /* accent grave */
3436 IDWriteInlineObject *sign;
3437 IDWriteTextFormat *format;
3438 IDWriteTextLayout *layout;
3439 IDWriteFactory *factory;
3440 IUnknown *unk, *effect;
3441 DWRITE_TEXT_RANGE r;
3442 HRESULT hr;
3443 LONG ref;
3445 factory = create_factory();
3447 effect = create_test_effect();
3449 hr = IDWriteFactory_CreateTextFormat(factory, L"Tahoma", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
3450 DWRITE_FONT_STRETCH_NORMAL, 10.0f, L"en-us", &format);
3451 ok(hr == S_OK, "Failed to create text format, hr %#lx.\n", hr);
3453 /* string with combining mark */
3454 hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 500.0, 1000.0, &layout);
3455 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3457 /* set effect past the end of text */
3458 r.startPosition = 100;
3459 r.length = 10;
3460 hr = IDWriteTextLayout_SetDrawingEffect(layout, effect, r);
3461 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3463 r.startPosition = r.length = 0;
3464 hr = IDWriteTextLayout_GetDrawingEffect(layout, 101, &unk, &r);
3465 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3466 ok(r.startPosition == 100 && r.length == 10, "got %u, %u\n", r.startPosition, r.length);
3467 IUnknown_Release(unk);
3469 r.startPosition = r.length = 0;
3470 unk = (void*)0xdeadbeef;
3471 hr = IDWriteTextLayout_GetDrawingEffect(layout, 1000, &unk, &r);
3472 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3473 ok(r.startPosition == 110 && r.length == ~0u-110, "got %u, %u\n", r.startPosition, r.length);
3474 ok(unk == NULL, "got %p\n", unk);
3476 /* effect is applied to clusters, not individual text positions */
3477 r.startPosition = 0;
3478 r.length = 2;
3479 hr = IDWriteTextLayout_SetDrawingEffect(layout, effect, r);
3480 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3482 flush_sequence(sequences, RENDERER_ID);
3483 hr = IDWriteTextLayout_Draw(layout, NULL, &testrenderer, 0.0, 0.0);
3484 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3485 ok_sequence(sequences, RENDERER_ID, draweffect_seq, "effect draw test", TRUE);
3486 IDWriteTextLayout_Release(layout);
3488 /* simple string */
3489 hr = IDWriteFactory_CreateTextLayout(factory, L"aecd", 4, format, 500.0f, 1000.0f, &layout);
3490 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
3492 r.startPosition = 0;
3493 r.length = 2;
3494 hr = IDWriteTextLayout_SetDrawingEffect(layout, effect, r);
3495 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3497 flush_sequence(sequences, RENDERER_ID);
3498 hr = IDWriteTextLayout_Draw(layout, NULL, &testrenderer, 0.0, 0.0);
3499 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3500 ok_sequence(sequences, RENDERER_ID, draweffect2_seq, "effect draw test 2", FALSE);
3501 IDWriteTextLayout_Release(layout);
3503 /* Inline object - effect set for same range */
3504 hr = IDWriteFactory_CreateEllipsisTrimmingSign(factory, format, &sign);
3505 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3507 hr = IDWriteFactory_CreateTextLayout(factory, L"aecd", 4, format, 500.0f, 1000.0f, &layout);
3508 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
3510 r.startPosition = 0;
3511 r.length = 4;
3512 hr = IDWriteTextLayout_SetInlineObject(layout, sign, r);
3513 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3515 hr = IDWriteTextLayout_SetDrawingEffect(layout, effect, r);
3516 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3518 flush_sequence(sequences, RENDERER_ID);
3519 hr = IDWriteTextLayout_Draw(layout, NULL, &testrenderer, 0.0, 0.0);
3520 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3521 ok_sequence(sequences, RENDERER_ID, draweffect3_seq, "effect draw test 3", FALSE);
3523 /* now set effect somewhere inside a range replaced by inline object */
3524 hr = IDWriteTextLayout_SetDrawingEffect(layout, NULL, r);
3525 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3527 r.startPosition = 1;
3528 r.length = 1;
3529 hr = IDWriteTextLayout_SetDrawingEffect(layout, effect, r);
3530 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3532 /* no effect is reported in this case */
3533 flush_sequence(sequences, RENDERER_ID);
3534 hr = IDWriteTextLayout_Draw(layout, NULL, &testrenderer, 0.0, 0.0);
3535 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3536 ok_sequence(sequences, RENDERER_ID, draweffect4_seq, "effect draw test 4", FALSE);
3538 r.startPosition = 0;
3539 r.length = 4;
3540 hr = IDWriteTextLayout_SetDrawingEffect(layout, NULL, r);
3541 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3543 r.startPosition = 0;
3544 r.length = 1;
3545 hr = IDWriteTextLayout_SetDrawingEffect(layout, effect, r);
3546 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3548 /* first range position is all that matters for inline ranges */
3549 flush_sequence(sequences, RENDERER_ID);
3550 hr = IDWriteTextLayout_Draw(layout, NULL, &testrenderer, 0.0, 0.0);
3551 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3552 ok_sequence(sequences, RENDERER_ID, draweffect3_seq, "effect draw test 5", FALSE);
3554 IDWriteTextLayout_Release(layout);
3556 ref = IUnknown_Release(effect);
3557 ok(ref == 0, "Unexpected effect refcount %lu\n", ref);
3558 IDWriteInlineObject_Release(sign);
3559 IDWriteTextFormat_Release(format);
3560 IDWriteFactory_Release(factory);
3563 static BOOL get_enus_string(IDWriteLocalizedStrings *strings, WCHAR *buff, UINT32 size)
3565 UINT32 index;
3566 BOOL exists = FALSE;
3567 HRESULT hr;
3569 hr = IDWriteLocalizedStrings_FindLocaleName(strings, L"en-us", &index, &exists);
3570 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3572 if (exists) {
3573 hr = IDWriteLocalizedStrings_GetString(strings, index, buff, size);
3574 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3576 else
3577 *buff = 0;
3579 return exists;
3582 static void test_GetLineMetrics(void)
3584 static const WCHAR str3W[] = {'a','\r','b','\n','c','\n','\r','d','\r','\n',0};
3585 static const WCHAR strW[] = {'a','b','c','d',' ',0};
3586 static const WCHAR str2W[] = {'a','b','\r','c','d',0};
3587 static const WCHAR str4W[] = {'a','\r',0};
3588 IDWriteFontCollection *syscollection;
3589 DWRITE_FONT_METRICS fontmetrics;
3590 DWRITE_LINE_METRICS metrics[6];
3591 UINT32 count, i, familycount;
3592 IDWriteTextFormat *format;
3593 IDWriteTextLayout *layout;
3594 IDWriteFontFace *fontface;
3595 IDWriteFactory *factory;
3596 DWRITE_TEXT_RANGE range;
3597 WCHAR nameW[256];
3598 HRESULT hr;
3600 factory = create_factory();
3602 hr = IDWriteFactory_CreateTextFormat(factory, L"Tahoma", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
3603 DWRITE_FONT_STRETCH_NORMAL, 2048.0f, L"en-us", &format);
3604 ok(hr == S_OK, "Failed to create text format, hr %#lx.\n", hr);
3606 hr = IDWriteFactory_CreateTextLayout(factory, strW, 5, format, 30000.0, 1000.0, &layout);
3607 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3609 count = 0;
3610 hr = IDWriteTextLayout_GetLineMetrics(layout, metrics, 0, &count);
3611 ok(hr == E_NOT_SUFFICIENT_BUFFER, "Unexpected hr %#lx.\n", hr);
3612 ok(count == 1, "got count %u\n", count);
3614 memset(metrics, 0, sizeof(metrics));
3615 hr = IDWriteTextLayout_GetLineMetrics(layout, metrics, 1, &count);
3616 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3617 ok(metrics[0].length == 5, "got %u\n", metrics[0].length);
3618 ok(metrics[0].trailingWhitespaceLength == 1, "got %u\n", metrics[0].trailingWhitespaceLength);
3620 ok(metrics[0].newlineLength == 0, "got %u\n", metrics[0].newlineLength);
3621 ok(metrics[0].isTrimmed == FALSE, "got %d\n", metrics[0].isTrimmed);
3623 IDWriteTextLayout_Release(layout);
3624 IDWriteTextFormat_Release(format);
3626 /* Test line height and baseline calculation */
3627 hr = IDWriteFactory_GetSystemFontCollection(factory, &syscollection, FALSE);
3628 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3629 familycount = IDWriteFontCollection_GetFontFamilyCount(syscollection);
3631 for (i = 0; i < familycount; i++) {
3632 IDWriteLocalizedStrings *names;
3633 IDWriteFontFamily *family;
3634 IDWriteFont *font;
3635 BOOL exists;
3637 format = NULL;
3638 layout = NULL;
3640 hr = IDWriteFontCollection_GetFontFamily(syscollection, i, &family);
3641 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3643 hr = IDWriteFontFamily_GetFirstMatchingFont(family, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STRETCH_NORMAL,
3644 DWRITE_FONT_STYLE_NORMAL, &font);
3645 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3647 hr = IDWriteFont_CreateFontFace(font, &fontface);
3648 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3650 hr = IDWriteFontFamily_GetFamilyNames(family, &names);
3651 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3653 if (!(exists = get_enus_string(names, nameW, ARRAY_SIZE(nameW)))) {
3654 IDWriteLocalFontFileLoader *localloader;
3655 IDWriteFontFileLoader *loader;
3656 IDWriteFontFile *file;
3657 const void *key;
3658 UINT32 keysize;
3659 UINT32 count;
3661 count = 1;
3662 hr = IDWriteFontFace_GetFiles(fontface, &count, &file);
3663 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3665 hr = IDWriteFontFile_GetLoader(file, &loader);
3666 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3668 hr = IDWriteFontFileLoader_QueryInterface(loader, &IID_IDWriteLocalFontFileLoader, (void**)&localloader);
3669 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3670 IDWriteFontFileLoader_Release(loader);
3672 hr = IDWriteFontFile_GetReferenceKey(file, &key, &keysize);
3673 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3675 hr = IDWriteLocalFontFileLoader_GetFilePathFromKey(localloader, key, keysize, nameW, ARRAY_SIZE(nameW));
3676 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3678 skip("Failed to get English family name, font file %s\n", wine_dbgstr_w(nameW));
3680 IDWriteLocalFontFileLoader_Release(localloader);
3681 IDWriteFontFile_Release(file);
3684 IDWriteLocalizedStrings_Release(names);
3685 IDWriteFont_Release(font);
3687 if (!exists)
3688 goto cleanup;
3690 IDWriteFontFace_GetMetrics(fontface, &fontmetrics);
3691 hr = IDWriteFactory_CreateTextFormat(factory, nameW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
3692 DWRITE_FONT_STRETCH_NORMAL, fontmetrics.designUnitsPerEm, L"en-us", &format);
3693 ok(hr == S_OK, "Failed to create text format, hr %#lx.\n", hr);
3695 hr = IDWriteFactory_CreateTextLayout(factory, L"", 1, format, 30000.0f, 100.0f, &layout);
3696 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
3698 memset(metrics, 0, sizeof(metrics));
3699 count = 0;
3700 hr = IDWriteTextLayout_GetLineMetrics(layout, metrics, ARRAY_SIZE(metrics), &count);
3701 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3702 ok(count == 1, "got %u\n", count);
3704 ok(metrics[0].baseline == fontmetrics.ascent + fontmetrics.lineGap, "%s: got %.2f, expected %d, "
3705 "linegap %d\n", wine_dbgstr_w(nameW), metrics[0].baseline, fontmetrics.ascent + fontmetrics.lineGap,
3706 fontmetrics.lineGap);
3707 ok(metrics[0].height == fontmetrics.ascent + fontmetrics.descent + fontmetrics.lineGap,
3708 "%s: got %.2f, expected %d, linegap %d\n", wine_dbgstr_w(nameW), metrics[0].height,
3709 fontmetrics.ascent + fontmetrics.descent + fontmetrics.lineGap, fontmetrics.lineGap);
3711 cleanup:
3712 if (layout)
3713 IDWriteTextLayout_Release(layout);
3714 if (format)
3715 IDWriteTextFormat_Release(format);
3716 IDWriteFontFace_Release(fontface);
3717 IDWriteFontFamily_Release(family);
3719 IDWriteFontCollection_Release(syscollection);
3721 hr = IDWriteFactory_CreateTextFormat(factory, L"Tahoma", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
3722 DWRITE_FONT_STRETCH_NORMAL, 2048.0f, L"en-us", &format);
3723 ok(hr == S_OK, "Failed to create text format, hr %#lx.\n", hr);
3725 fontface = get_fontface_from_format(format);
3726 ok(fontface != NULL, "got %p\n", fontface);
3728 /* force 2 lines */
3729 hr = IDWriteFactory_CreateTextLayout(factory, str2W, 5, format, 10000.0, 1000.0, &layout);
3730 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3732 memset(metrics, 0, sizeof(metrics));
3733 count = 0;
3734 hr = IDWriteTextLayout_GetLineMetrics(layout, metrics, ARRAY_SIZE(metrics), &count);
3735 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3736 ok(count == 2, "got %u\n", count);
3737 /* baseline is relative to a line, and is not accumulated */
3738 ok(metrics[0].baseline == metrics[1].baseline, "got %.2f, %.2f\n", metrics[0].baseline,
3739 metrics[1].baseline);
3741 IDWriteTextLayout_Release(layout);
3742 IDWriteTextFormat_Release(format);
3744 /* line breaks */
3745 hr = IDWriteFactory_CreateTextFormat(factory, L"Tahoma", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
3746 DWRITE_FONT_STRETCH_NORMAL, 12.0f, L"en-us", &format);
3747 ok(hr == S_OK, "Failed to create text format, hr %#lx.\n", hr);
3749 hr = IDWriteFactory_CreateTextLayout(factory, str3W, 10, format, 100.0, 300.0, &layout);
3750 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3752 memset(metrics, 0xcc, sizeof(metrics));
3753 count = 0;
3754 hr = IDWriteTextLayout_GetLineMetrics(layout, metrics, ARRAY_SIZE(metrics), &count);
3755 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3756 ok(count == 6, "got %u\n", count);
3758 ok(metrics[0].length == 2, "got %u\n", metrics[0].length);
3759 ok(metrics[1].length == 2, "got %u\n", metrics[1].length);
3760 ok(metrics[2].length == 2, "got %u\n", metrics[2].length);
3761 ok(metrics[3].length == 1, "got %u\n", metrics[3].length);
3762 ok(metrics[4].length == 3, "got %u\n", metrics[4].length);
3763 ok(metrics[5].length == 0, "got %u\n", metrics[5].length);
3765 ok(metrics[0].newlineLength == 1, "got %u\n", metrics[0].newlineLength);
3766 ok(metrics[1].newlineLength == 1, "got %u\n", metrics[1].newlineLength);
3767 ok(metrics[2].newlineLength == 1, "got %u\n", metrics[2].newlineLength);
3768 ok(metrics[3].newlineLength == 1, "got %u\n", metrics[3].newlineLength);
3769 ok(metrics[4].newlineLength == 2, "got %u\n", metrics[4].newlineLength);
3770 ok(metrics[5].newlineLength == 0, "got %u\n", metrics[5].newlineLength);
3772 ok(metrics[0].trailingWhitespaceLength == 1, "got %u\n", metrics[0].newlineLength);
3773 ok(metrics[1].trailingWhitespaceLength == 1, "got %u\n", metrics[1].newlineLength);
3774 ok(metrics[2].trailingWhitespaceLength == 1, "got %u\n", metrics[2].newlineLength);
3775 ok(metrics[3].trailingWhitespaceLength == 1, "got %u\n", metrics[3].newlineLength);
3776 ok(metrics[4].trailingWhitespaceLength == 2, "got %u\n", metrics[4].newlineLength);
3777 ok(metrics[5].trailingWhitespaceLength == 0, "got %u\n", metrics[5].newlineLength);
3779 IDWriteTextLayout_Release(layout);
3781 /* empty text layout */
3782 hr = IDWriteFactory_CreateTextLayout(factory, strW, 0, format, 100.0f, 300.0f, &layout);
3783 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3785 count = 0;
3786 memset(metrics, 0, sizeof(metrics));
3787 hr = IDWriteTextLayout_GetLineMetrics(layout, metrics, 1, &count);
3788 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3789 ok(count == 1, "got %u\n", count);
3790 ok(metrics[0].length == 0, "got %u\n", metrics[0].length);
3791 ok(metrics[0].trailingWhitespaceLength == 0, "got %u\n", metrics[0].trailingWhitespaceLength);
3792 ok(metrics[0].newlineLength == 0, "got %u\n", metrics[0].newlineLength);
3793 ok(metrics[0].height > 0.0f, "got %f\n", metrics[0].height);
3794 ok(metrics[0].baseline > 0.0f, "got %f\n", metrics[0].baseline);
3795 ok(!metrics[0].isTrimmed, "got %d\n", metrics[0].isTrimmed);
3797 /* change font size at first position, see if metrics changed */
3798 range.startPosition = 0;
3799 range.length = 1;
3800 hr = IDWriteTextLayout_SetFontSize(layout, 80.0f, range);
3801 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3803 count = 0;
3804 hr = IDWriteTextLayout_GetLineMetrics(layout, metrics + 1, 1, &count);
3805 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3806 ok(count == 1, "got %u\n", count);
3807 ok(metrics[1].height > metrics[0].height, "got %f\n", metrics[1].height);
3808 ok(metrics[1].baseline > metrics[0].baseline, "got %f\n", metrics[1].baseline);
3810 /* revert font size back to format value, set different size for position 1 */
3811 hr = IDWriteTextLayout_SetFontSize(layout, 12.0f, range);
3812 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3814 range.startPosition = 1;
3815 range.length = 1;
3816 hr = IDWriteTextLayout_SetFontSize(layout, 80.0f, range);
3817 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3819 memset(metrics + 1, 0, sizeof(*metrics));
3820 count = 0;
3821 hr = IDWriteTextLayout_GetLineMetrics(layout, metrics + 1, 1, &count);
3822 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3823 ok(count == 1, "got %u\n", count);
3824 ok(metrics[1].height == metrics[0].height, "got %f\n", metrics[1].height);
3825 ok(metrics[1].baseline == metrics[0].baseline, "got %f\n", metrics[1].baseline);
3827 IDWriteTextLayout_Release(layout);
3829 /* text is "a\r" */
3830 hr = IDWriteFactory_CreateTextLayout(factory, str4W, 2, format, 100.0f, 300.0f, &layout);
3831 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3833 count = 0;
3834 memset(metrics, 0, sizeof(metrics));
3835 hr = IDWriteTextLayout_GetLineMetrics(layout, metrics, ARRAY_SIZE(metrics), &count);
3836 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3837 ok(count == 2, "got %u\n", count);
3838 ok(metrics[0].length == 2, "got %u\n", metrics[0].length);
3839 ok(metrics[0].newlineLength == 1, "got %u\n", metrics[0].newlineLength);
3840 ok(metrics[0].height > 0.0f, "got %f\n", metrics[0].height);
3841 ok(metrics[0].baseline > 0.0f, "got %f\n", metrics[0].baseline);
3842 ok(metrics[1].length == 0, "got %u\n", metrics[1].length);
3843 ok(metrics[1].newlineLength == 0, "got %u\n", metrics[1].newlineLength);
3844 ok(metrics[1].height > 0.0f, "got %f\n", metrics[1].height);
3845 ok(metrics[1].baseline > 0.0f, "got %f\n", metrics[1].baseline);
3847 range.startPosition = 1;
3848 range.length = 1;
3849 hr = IDWriteTextLayout_SetFontSize(layout, 80.0f, range);
3850 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3852 hr = IDWriteTextLayout_GetLineMetrics(layout, metrics + 2, 2, &count);
3853 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3854 ok(count == 2, "got %u\n", count);
3855 ok(metrics[3].height > metrics[1].height, "got %f, old %f\n", metrics[3].height, metrics[1].height);
3856 ok(metrics[3].baseline > metrics[1].baseline, "got %f, old %f\n", metrics[3].baseline, metrics[1].baseline);
3858 /* revert to original format */
3859 hr = IDWriteTextLayout_SetFontSize(layout, 12.0f, range);
3860 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3861 hr = IDWriteTextLayout_GetLineMetrics(layout, metrics + 2, 2, &count);
3862 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3863 ok(count == 2, "got %u\n", count);
3864 ok(metrics[3].height == metrics[1].height, "got %f, old %f\n", metrics[3].height, metrics[1].height);
3865 ok(metrics[3].baseline == metrics[1].baseline, "got %f, old %f\n", metrics[3].baseline, metrics[1].baseline);
3867 /* Switch to uniform spacing */
3868 hr = IDWriteTextLayout_SetLineSpacing(layout, DWRITE_LINE_SPACING_METHOD_UNIFORM, 456.0f, 123.0f);
3869 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3871 hr = IDWriteTextLayout_GetLineMetrics(layout, metrics, ARRAY_SIZE(metrics), &count);
3872 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3873 ok(count == 2, "got %u\n", count);
3875 for (i = 0; i < count; i++) {
3876 ok(metrics[i].height == 456.0f, "%u: got line height %f\n", i, metrics[i].height);
3877 ok(metrics[i].baseline == 123.0f, "%u: got line baseline %f\n", i, metrics[i].baseline);
3880 IDWriteTextLayout_Release(layout);
3882 /* Switch to proportional */
3883 hr = IDWriteTextFormat_SetLineSpacing(format, DWRITE_LINE_SPACING_METHOD_PROPORTIONAL, 2.0f, 4.0f);
3884 if (hr == S_OK) {
3885 hr = IDWriteFactory_CreateTextLayout(factory, str4W, 1, format, 100.0f, 300.0f, &layout);
3886 ok(hr == S_OK, "Failed to create layout, hr %#lx.\n", hr);
3888 hr = IDWriteTextLayout_GetLineMetrics(layout, metrics, ARRAY_SIZE(metrics), &count);
3889 ok(hr == S_OK, "Failed to get line metrics, hr %#lx.\n", hr);
3890 ok(count == 1, "Unexpected line count %u\n", count);
3892 /* Back to default mode. */
3893 hr = IDWriteTextLayout_SetLineSpacing(layout, DWRITE_LINE_SPACING_METHOD_DEFAULT, 0.0f, 0.0f);
3894 ok(hr == S_OK, "Failed to set spacing method, hr %#lx.\n", hr);
3896 hr = IDWriteTextLayout_GetLineMetrics(layout, metrics + 1, 1, &count);
3897 ok(hr == S_OK, "Failed to get line metrics, hr %#lx.\n", hr);
3898 ok(count == 1, "Unexpected line count %u\n", count);
3900 /* Proportional spacing applies multipliers to default, content based spacing. */
3901 ok(metrics[0].height == 2.0f * metrics[1].height, "Unexpected line height %f.\n", metrics[0].height);
3902 ok(metrics[0].baseline == 4.0f * metrics[1].baseline, "Unexpected line baseline %f.\n", metrics[0].baseline);
3904 IDWriteTextLayout_Release(layout);
3906 else
3907 win_skip("Proportional spacing is not supported.\n");
3909 IDWriteTextFormat_Release(format);
3910 IDWriteFontFace_Release(fontface);
3911 IDWriteFactory_Release(factory);
3914 static void test_SetTextAlignment(void)
3916 static const WCHAR stringsW[][10] = {
3917 {'a',0},
3921 DWRITE_CLUSTER_METRICS clusters[10];
3922 DWRITE_TEXT_METRICS metrics;
3923 IDWriteTextFormat1 *format1;
3924 IDWriteTextFormat *format;
3925 IDWriteTextLayout *layout;
3926 IDWriteFactory *factory;
3927 DWRITE_TEXT_ALIGNMENT v;
3928 UINT32 count, i;
3929 HRESULT hr;
3931 factory = create_factory();
3933 hr = IDWriteFactory_CreateTextFormat(factory, L"Tahoma", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
3934 DWRITE_FONT_STRETCH_NORMAL, 12.0f, L"en-us", &format);
3935 ok(hr == S_OK, "Failed to create text format, hr %#lx.\n", hr);
3937 v = IDWriteTextFormat_GetTextAlignment(format);
3938 ok(v == DWRITE_TEXT_ALIGNMENT_LEADING, "got %d\n", v);
3940 hr = IDWriteFactory_CreateTextLayout(factory, L"a", 1, format, 500.0f, 100.0f, &layout);
3941 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
3943 v = IDWriteTextLayout_GetTextAlignment(layout);
3944 ok(v == DWRITE_TEXT_ALIGNMENT_LEADING, "got %d\n", v);
3946 hr = IDWriteTextLayout_SetTextAlignment(layout, DWRITE_TEXT_ALIGNMENT_TRAILING);
3947 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3949 hr = IDWriteTextLayout_SetTextAlignment(layout, DWRITE_TEXT_ALIGNMENT_TRAILING);
3950 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3952 v = IDWriteTextFormat_GetTextAlignment(format);
3953 ok(v == DWRITE_TEXT_ALIGNMENT_LEADING, "got %d\n", v);
3955 v = IDWriteTextLayout_GetTextAlignment(layout);
3956 ok(v == DWRITE_TEXT_ALIGNMENT_TRAILING, "got %d\n", v);
3958 hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextFormat1, (void**)&format1);
3959 if (hr == S_OK) {
3960 hr = IDWriteTextFormat1_SetTextAlignment(format1, DWRITE_TEXT_ALIGNMENT_CENTER);
3961 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3963 v = IDWriteTextFormat_GetTextAlignment(format);
3964 ok(v == DWRITE_TEXT_ALIGNMENT_LEADING, "got %d\n", v);
3966 v = IDWriteTextLayout_GetTextAlignment(layout);
3967 ok(v == DWRITE_TEXT_ALIGNMENT_CENTER, "got %d\n", v);
3969 v = IDWriteTextFormat1_GetTextAlignment(format1);
3970 ok(v == DWRITE_TEXT_ALIGNMENT_CENTER, "got %d\n", v);
3972 IDWriteTextFormat1_Release(format1);
3974 else
3975 win_skip("IDWriteTextFormat1 is not supported\n");
3977 IDWriteTextLayout_Release(layout);
3979 for (i = 0; i < ARRAY_SIZE(stringsW); i++) {
3980 FLOAT text_width;
3982 hr = IDWriteTextFormat_SetTextAlignment(format, DWRITE_TEXT_ALIGNMENT_LEADING);
3983 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3985 hr = IDWriteFactory_CreateTextLayout(factory, stringsW[i], lstrlenW(stringsW[i]), format, 500.0f, 100.0f, &layout);
3986 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3988 hr = IDWriteTextLayout_SetWordWrapping(layout, DWRITE_WORD_WRAPPING_NO_WRAP);
3989 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3991 count = 0;
3992 hr = IDWriteTextLayout_GetClusterMetrics(layout, clusters, ARRAY_SIZE(clusters), &count);
3993 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
3994 if (stringsW[i][0])
3995 ok(count > 0, "got %u\n", count);
3996 else
3997 ok(count == 0, "got %u\n", count);
3999 text_width = 0.0f;
4000 while (count)
4001 text_width += clusters[--count].width;
4003 /* maxwidth is 500, leading alignment */
4004 hr = IDWriteTextLayout_SetTextAlignment(layout, DWRITE_TEXT_ALIGNMENT_LEADING);
4005 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4007 hr = IDWriteTextLayout_GetMetrics(layout, &metrics);
4008 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4010 ok(metrics.left == 0.0f, "got %.2f\n", metrics.left);
4011 ok(metrics.width == text_width, "got %.2f\n", metrics.width);
4012 ok(metrics.layoutWidth == 500.0f, "got %.2f\n", metrics.layoutWidth);
4013 ok(metrics.lineCount == 1, "got %d\n", metrics.lineCount);
4015 /* maxwidth is 500, trailing alignment */
4016 hr = IDWriteTextLayout_SetTextAlignment(layout, DWRITE_TEXT_ALIGNMENT_TRAILING);
4017 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4019 hr = IDWriteTextLayout_GetMetrics(layout, &metrics);
4020 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4022 ok(metrics.left == metrics.layoutWidth - metrics.width, "got %.2f\n", metrics.left);
4023 ok(metrics.width == text_width, "got %.2f\n", metrics.width);
4024 ok(metrics.layoutWidth == 500.0f, "got %.2f\n", metrics.layoutWidth);
4025 ok(metrics.lineCount == 1, "got %d\n", metrics.lineCount);
4026 IDWriteTextLayout_Release(layout);
4028 /* initially created with trailing alignment */
4029 hr = IDWriteTextFormat_SetTextAlignment(format, DWRITE_TEXT_ALIGNMENT_TRAILING);
4030 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4032 hr = IDWriteFactory_CreateTextLayout(factory, stringsW[i], lstrlenW(stringsW[i]), format, 500.0f, 100.0f, &layout);
4033 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4035 hr = IDWriteTextLayout_GetMetrics(layout, &metrics);
4036 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4038 ok(metrics.left == metrics.layoutWidth - metrics.width, "got %.2f\n", metrics.left);
4039 ok(metrics.width == text_width, "got %.2f\n", metrics.width);
4040 ok(metrics.layoutWidth == 500.0f, "got %.2f\n", metrics.layoutWidth);
4041 ok(metrics.lineCount == 1, "got %d\n", metrics.lineCount);
4042 IDWriteTextLayout_Release(layout);
4044 if (stringsW[i][0]) {
4045 /* max width less than total run width, trailing alignment */
4046 hr = IDWriteTextFormat_SetWordWrapping(format, DWRITE_WORD_WRAPPING_NO_WRAP);
4047 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4049 hr = IDWriteFactory_CreateTextLayout(factory, stringsW[i], lstrlenW(stringsW[i]), format, clusters[0].width, 100.0f, &layout);
4050 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4051 hr = IDWriteTextLayout_GetMetrics(layout, &metrics);
4052 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4053 ok(metrics.left == metrics.layoutWidth - metrics.width, "got %.2f\n", metrics.left);
4054 ok(metrics.width == text_width, "got %.2f\n", metrics.width);
4055 ok(metrics.lineCount == 1, "got %d\n", metrics.lineCount);
4056 IDWriteTextLayout_Release(layout);
4059 /* maxwidth is 500, centered */
4060 hr = IDWriteTextFormat_SetTextAlignment(format, DWRITE_TEXT_ALIGNMENT_CENTER);
4061 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4063 hr = IDWriteFactory_CreateTextLayout(factory, stringsW[i], lstrlenW(stringsW[i]), format, 500.0f, 100.0f, &layout);
4064 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4066 hr = IDWriteTextLayout_GetMetrics(layout, &metrics);
4067 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4068 ok(metrics.left == (metrics.layoutWidth - metrics.width) / 2.0f, "got %.2f\n", metrics.left);
4069 ok(metrics.width == text_width, "got %.2f\n", metrics.width);
4070 ok(metrics.lineCount == 1, "got %d\n", metrics.lineCount);
4072 IDWriteTextLayout_Release(layout);
4075 IDWriteTextFormat_Release(format);
4076 IDWriteFactory_Release(factory);
4079 static void test_SetParagraphAlignment(void)
4081 DWRITE_TEXT_METRICS metrics;
4082 IDWriteTextFormat *format;
4083 IDWriteTextLayout *layout;
4084 IDWriteFactory *factory;
4085 DWRITE_PARAGRAPH_ALIGNMENT v;
4086 DWRITE_LINE_METRICS lines[1];
4087 UINT32 count;
4088 HRESULT hr;
4090 factory = create_factory();
4092 hr = IDWriteFactory_CreateTextFormat(factory, L"Tahoma", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
4093 DWRITE_FONT_STRETCH_NORMAL, 12.0f, L"en-us", &format);
4094 ok(hr == S_OK, "Failed to create text format, hr %#lx.\n", hr);
4096 v = IDWriteTextFormat_GetParagraphAlignment(format);
4097 ok(v == DWRITE_PARAGRAPH_ALIGNMENT_NEAR, "got %d\n", v);
4099 hr = IDWriteFactory_CreateTextLayout(factory, L"a", 1, format, 500.0f, 100.0f, &layout);
4100 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
4102 v = IDWriteTextLayout_GetParagraphAlignment(layout);
4103 ok(v == DWRITE_PARAGRAPH_ALIGNMENT_NEAR, "got %d\n", v);
4105 hr = IDWriteTextLayout_SetParagraphAlignment(layout, DWRITE_PARAGRAPH_ALIGNMENT_FAR);
4106 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4108 hr = IDWriteTextLayout_SetParagraphAlignment(layout, DWRITE_PARAGRAPH_ALIGNMENT_FAR);
4109 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4111 v = IDWriteTextFormat_GetParagraphAlignment(format);
4112 ok(v == DWRITE_PARAGRAPH_ALIGNMENT_NEAR, "got %d\n", v);
4114 v = IDWriteTextLayout_GetParagraphAlignment(layout);
4115 ok(v == DWRITE_PARAGRAPH_ALIGNMENT_FAR, "got %d\n", v);
4117 hr = IDWriteTextLayout_SetParagraphAlignment(layout, DWRITE_PARAGRAPH_ALIGNMENT_CENTER);
4118 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4120 v = IDWriteTextLayout_GetParagraphAlignment(layout);
4121 ok(v == DWRITE_PARAGRAPH_ALIGNMENT_CENTER, "got %d\n", v);
4123 count = 0;
4124 hr = IDWriteTextLayout_GetLineMetrics(layout, lines, 1, &count);
4125 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4126 ok(count == 1, "got %u\n", count);
4128 /* maxheight is 100, near alignment */
4129 hr = IDWriteTextLayout_SetParagraphAlignment(layout, DWRITE_PARAGRAPH_ALIGNMENT_NEAR);
4130 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4132 hr = IDWriteTextLayout_GetMetrics(layout, &metrics);
4133 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4135 ok(metrics.top == 0.0, "got %.2f\n", metrics.top);
4136 ok(metrics.height == lines[0].height, "got %.2f\n", metrics.height);
4137 ok(metrics.layoutHeight == 100.0, "got %.2f\n", metrics.layoutHeight);
4138 ok(metrics.lineCount == 1, "got %d\n", metrics.lineCount);
4140 /* maxwidth is 100, far alignment */
4141 hr = IDWriteTextLayout_SetParagraphAlignment(layout, DWRITE_PARAGRAPH_ALIGNMENT_FAR);
4142 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4144 hr = IDWriteTextLayout_GetMetrics(layout, &metrics);
4145 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4147 ok(metrics.top == metrics.layoutHeight - metrics.height, "got %.2f\n", metrics.top);
4148 ok(metrics.height == lines[0].height, "got %.2f\n", metrics.height);
4149 ok(metrics.layoutHeight == 100.0, "got %.2f\n", metrics.layoutHeight);
4150 ok(metrics.lineCount == 1, "got %d\n", metrics.lineCount);
4151 IDWriteTextLayout_Release(layout);
4153 /* initially created with centered alignment */
4154 hr = IDWriteTextFormat_SetParagraphAlignment(format, DWRITE_PARAGRAPH_ALIGNMENT_CENTER);
4155 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4157 hr = IDWriteFactory_CreateTextLayout(factory, L"a", 1, format, 500.0f, 100.0f, &layout);
4158 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
4160 hr = IDWriteTextLayout_GetMetrics(layout, &metrics);
4161 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4163 ok(metrics.top == (metrics.layoutHeight - lines[0].height) / 2, "got %.2f\n", metrics.top);
4164 ok(metrics.height == lines[0].height, "got %.2f\n", metrics.height);
4165 ok(metrics.lineCount == 1, "got %d\n", metrics.lineCount);
4166 IDWriteTextLayout_Release(layout);
4168 IDWriteTextFormat_Release(format);
4169 IDWriteFactory_Release(factory);
4172 static void test_SetReadingDirection(void)
4174 DWRITE_CLUSTER_METRICS clusters[1];
4175 DWRITE_TEXT_METRICS metrics;
4176 IDWriteTextFormat *format;
4177 IDWriteTextLayout *layout;
4178 IDWriteFactory *factory;
4179 DWRITE_READING_DIRECTION v;
4180 DWRITE_LINE_METRICS lines[1];
4181 UINT32 count;
4182 HRESULT hr;
4184 factory = create_factory();
4186 hr = IDWriteFactory_CreateTextFormat(factory, L"Tahoma", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
4187 DWRITE_FONT_STRETCH_NORMAL, 12.0f, L"en-us", &format);
4188 ok(hr == S_OK, "Failed to create text format, hr %#lx.\n", hr);
4190 v = IDWriteTextFormat_GetReadingDirection(format);
4191 ok(v == DWRITE_READING_DIRECTION_LEFT_TO_RIGHT, "got %d\n", v);
4193 hr = IDWriteFactory_CreateTextLayout(factory, L"a", 1, format, 500.0f, 100.0f, &layout);
4194 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
4196 v = IDWriteTextLayout_GetReadingDirection(layout);
4197 ok(v == DWRITE_READING_DIRECTION_LEFT_TO_RIGHT, "got %d\n", v);
4199 v = IDWriteTextFormat_GetReadingDirection(format);
4200 ok(v == DWRITE_READING_DIRECTION_LEFT_TO_RIGHT, "got %d\n", v);
4202 hr = IDWriteTextLayout_SetReadingDirection(layout, DWRITE_READING_DIRECTION_RIGHT_TO_LEFT);
4203 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4205 count = 0;
4206 hr = IDWriteTextLayout_GetLineMetrics(layout, lines, 1, &count);
4207 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4208 ok(count == 1, "got %u\n", count);
4210 count = 0;
4211 hr = IDWriteTextLayout_GetClusterMetrics(layout, clusters, 1, &count);
4212 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4213 ok(count == 1, "got %u\n", count);
4215 /* leading alignment, RTL */
4216 hr = IDWriteTextLayout_GetMetrics(layout, &metrics);
4217 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4219 ok(metrics.left == metrics.layoutWidth - clusters[0].width, "got %.2f\n", metrics.left);
4220 ok(metrics.top == 0.0, "got %.2f\n", metrics.top);
4221 ok(metrics.width == clusters[0].width, "got %.2f\n", metrics.width);
4222 ok(metrics.height == lines[0].height, "got %.2f\n", metrics.height);
4223 ok(metrics.layoutWidth == 500.0, "got %.2f\n", metrics.layoutWidth);
4224 ok(metrics.layoutHeight == 100.0, "got %.2f\n", metrics.layoutHeight);
4225 ok(metrics.lineCount == 1, "got %d\n", metrics.lineCount);
4227 /* trailing alignment, RTL */
4228 hr = IDWriteTextLayout_SetTextAlignment(layout, DWRITE_TEXT_ALIGNMENT_TRAILING);
4229 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4231 hr = IDWriteTextLayout_GetMetrics(layout, &metrics);
4232 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4234 ok(metrics.left == 0.0, "got %.2f\n", metrics.left);
4235 ok(metrics.top == 0.0, "got %.2f\n", metrics.top);
4236 ok(metrics.width == clusters[0].width, "got %.2f\n", metrics.width);
4237 ok(metrics.height == lines[0].height, "got %.2f\n", metrics.height);
4238 ok(metrics.layoutWidth == 500.0, "got %.2f\n", metrics.layoutWidth);
4239 ok(metrics.layoutHeight == 100.0, "got %.2f\n", metrics.layoutHeight);
4240 ok(metrics.lineCount == 1, "got %d\n", metrics.lineCount);
4242 /* centered alignment, RTL */
4243 hr = IDWriteTextLayout_SetTextAlignment(layout, DWRITE_TEXT_ALIGNMENT_CENTER);
4244 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4246 hr = IDWriteTextLayout_GetMetrics(layout, &metrics);
4247 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4249 ok(metrics.left == (metrics.layoutWidth - clusters[0].width) / 2.0, "got %.2f\n", metrics.left);
4250 ok(metrics.top == 0.0, "got %.2f\n", metrics.top);
4251 ok(metrics.width == clusters[0].width, "got %.2f\n", metrics.width);
4252 ok(metrics.height == lines[0].height, "got %.2f\n", metrics.height);
4253 ok(metrics.layoutWidth == 500.0, "got %.2f\n", metrics.layoutWidth);
4254 ok(metrics.layoutHeight == 100.0, "got %.2f\n", metrics.layoutHeight);
4255 ok(metrics.lineCount == 1, "got %d\n", metrics.lineCount);
4257 IDWriteTextLayout_Release(layout);
4259 IDWriteTextFormat_Release(format);
4260 IDWriteFactory_Release(factory);
4263 static inline FLOAT get_scaled_font_metric(UINT32 metric, FLOAT emSize, const DWRITE_FONT_METRICS *metrics)
4265 return (FLOAT)metric * emSize / (FLOAT)metrics->designUnitsPerEm;
4268 static FLOAT snap_coord(const DWRITE_MATRIX *m, FLOAT ppdip, FLOAT coord)
4270 FLOAT vec[2], det, vec2[2];
4271 BOOL transform;
4273 /* has to be a diagonal matrix */
4274 if ((ppdip <= 0.0) ||
4275 (m->m11 * m->m22 != 0.0 && (m->m12 != 0.0 || m->m21 != 0.0)) ||
4276 (m->m12 * m->m21 != 0.0 && (m->m11 != 0.0 || m->m22 != 0.0)))
4277 return coord;
4279 det = m->m11 * m->m22 - m->m12 * m->m21;
4280 transform = fabsf(det) > 1e-10;
4282 if (transform) {
4283 /* apply transform */
4284 vec[0] = 0.0;
4285 vec[1] = coord * ppdip;
4287 vec2[0] = m->m11 * vec[0] + m->m21 * vec[1] + m->dx;
4288 vec2[1] = m->m12 * vec[0] + m->m22 * vec[1] + m->dy;
4290 /* snap */
4291 vec2[0] = floorf(vec2[0] + 0.5f);
4292 vec2[1] = floorf(vec2[1] + 0.5f);
4294 /* apply inverted transform */
4295 vec[1] = (-m->m12 * vec2[0] + m->m11 * vec2[1] - (m->m11 * m->dy - m->m12 * m->dx)) / det;
4296 vec[1] /= ppdip;
4298 else
4299 vec[1] = floorf(coord * ppdip + 0.5f) / ppdip;
4300 return vec[1];
4303 static inline BOOL float_eq(FLOAT left, FLOAT right)
4305 int x = *(int *)&left;
4306 int y = *(int *)&right;
4308 if (x < 0)
4309 x = INT_MIN - x;
4310 if (y < 0)
4311 y = INT_MIN - y;
4313 return abs(x - y) <= 16;
4316 struct snapping_test {
4317 DWRITE_MATRIX m;
4318 FLOAT ppdip;
4321 static struct snapping_test snapping_tests[] = {
4322 { { 0.0, 1.0, 2.0, 0.0, 0.2, 0.3 }, 1.0 },
4323 { { 0.0, 1.0, 2.0, 0.0, 0.0, 0.0 }, 1.0 },
4324 { { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }, 1.0 }, /* identity transform */
4325 { { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }, 0.9 },
4326 { { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }, -1.0 },
4327 { { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }, 0.0 },
4328 { { 1.0, 0.0, 0.0, 1.0, 0.0, 0.3 }, 1.0 }, /* simple Y shift */
4329 { { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }, 10.0 }, /* identity, 10 ppdip */
4330 { { 1.0, 0.0, 0.0, 10.0, 0.0, 0.0 }, 10.0 },
4331 { { 0.0, 1.0, 1.0, 0.0, 0.2, 0.6 }, 1.0 },
4332 { { 0.0, 2.0, 2.0, 0.0, 0.2, 0.6 }, 1.0 },
4333 { { 0.0, 0.5, -0.5, 0.0, 0.2, 0.6 }, 1.0 },
4334 { { 1.0, 2.0, 0.0, 1.0, 0.2, 0.6 }, 1.0 },
4335 { { 1.0, 1.0, 0.0, 1.0, 0.2, 0.6 }, 1.0 },
4336 { { 0.5, 0.5, -0.5, 0.5, 0.2, 0.6 }, 1.0 }, /* 45 degrees rotation */
4337 { { 0.5, 0.5, -0.5, 0.5, 0.0, 0.0 }, 100.0 }, /* 45 degrees rotation */
4338 { { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }, 100.0 },
4339 { { 0.0, 1.0, -1.0, 0.0, 0.2, 0.6 }, 1.0 }, /* 90 degrees rotation */
4340 { { -1.0, 0.0, 0.0, -1.0, 0.2, 0.6 }, 1.0 }, /* 180 degrees rotation */
4341 { { 0.0, -1.0, 1.0, 0.0, 0.2, 0.6 }, 1.0 }, /* 270 degrees rotation */
4342 { { 1.0, 0.0, 0.0, 1.0,-0.1, 0.2 }, 1.0 },
4343 { { 0.0, 1.0, -1.0, 0.0,-0.2,-0.3 }, 1.0 }, /* 90 degrees rotation */
4344 { { -1.0, 0.0, 0.0, -1.0,-0.3,-1.6 }, 1.0 }, /* 180 degrees rotation */
4345 { { 0.0, -1.0, 1.0, 0.0,-0.7, 0.6 }, 10.0 }, /* 270 degrees rotation */
4346 { { 0.0, 2.0, 1.0, 0.0, 0.2, 0.6 }, 1.0 },
4347 { { 0.0, 0.0, 1.0, 0.0, 0.0, 0.0 }, 1.0 },
4348 { { 3.0, 0.0, 0.0, 5.0, 0.2,-0.3 }, 10.0 },
4349 { { 0.0, -3.0, 5.0, 0.0,-0.1, 0.7 }, 10.0 },
4352 static DWRITE_MATRIX compattransforms[] = {
4353 { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 },
4354 { 1.0, 0.0, 0.0, 1.0, 0.2, 0.3 },
4355 { 2.0, 0.0, 0.0, 2.0, 0.2, 0.3 },
4356 { 2.0, 1.0, 2.0, 2.0, 0.2, 0.3 },
4359 static void test_pixelsnapping(void)
4361 IDWriteTextLayout *layout, *layout2;
4362 struct renderer_context ctxt;
4363 DWRITE_FONT_METRICS metrics;
4364 IDWriteTextFormat *format;
4365 IDWriteFontFace *fontface;
4366 IDWriteFactory *factory;
4367 FLOAT baseline, originX;
4368 HRESULT hr;
4369 int i, j;
4371 factory = create_factory();
4373 hr = IDWriteFactory_CreateTextFormat(factory, L"Tahoma", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
4374 DWRITE_FONT_STRETCH_NORMAL, 12.0f, L"en-us", &format);
4375 ok(hr == S_OK, "Failed to create text format, hr %#lx.\n", hr);
4377 fontface = get_fontface_from_format(format);
4378 IDWriteFontFace_GetMetrics(fontface, &metrics);
4380 hr = IDWriteFactory_CreateTextLayout(factory, L"a", 1, format, 500.0f, 100.0f, &layout);
4381 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
4383 /* disabled snapping */
4384 ctxt.snapping_disabled = TRUE;
4385 ctxt.gdicompat = FALSE;
4386 ctxt.use_gdi_natural = FALSE;
4387 ctxt.ppdip = 1.0f;
4388 memset(&ctxt.m, 0, sizeof(ctxt.m));
4389 ctxt.m.m11 = ctxt.m.m22 = 1.0;
4390 originX = 0.1;
4392 hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, originX, 0.0);
4393 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4395 baseline = get_scaled_font_metric(metrics.ascent, 12.0, &metrics);
4396 ok(ctxt.originX == originX, "got %f, originX %f\n", ctxt.originX, originX);
4397 ok(ctxt.originY == baseline, "got %f, baseline %f\n", ctxt.originY, baseline);
4398 ok(floor(baseline) != baseline, "got %f\n", baseline);
4400 ctxt.snapping_disabled = FALSE;
4402 for (i = 0; i < ARRAY_SIZE(snapping_tests); i++) {
4403 struct snapping_test *ptr = &snapping_tests[i];
4404 FLOAT expectedY;
4406 ctxt.m = ptr->m;
4407 ctxt.ppdip = ptr->ppdip;
4408 ctxt.originX = 678.9;
4409 ctxt.originY = 678.9;
4411 expectedY = snap_coord(&ctxt.m, ctxt.ppdip, baseline);
4412 hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, originX, 0.0);
4413 ok(hr == S_OK, "%d: unexpected hr %#lx.\n", i, hr);
4414 ok(ctxt.originX == originX, "%d: got %f, originX %f\n", i, ctxt.originX, originX);
4415 ok(float_eq(ctxt.originY, expectedY), "%d: got %f, expected %f, baseline %f\n",
4416 i, ctxt.originY, expectedY, baseline);
4418 /* gdicompat layout transform doesn't affect snapping */
4419 for (j = 0; j < ARRAY_SIZE(compattransforms); ++j)
4421 hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, L"a", 1, format, 500.0f, 100.0f,
4422 1.0f, &compattransforms[j], FALSE, &layout2);
4423 ok(hr == S_OK, "%d: failed to create text layout, hr %#lx.\n", i, hr);
4425 expectedY = snap_coord(&ctxt.m, ctxt.ppdip, baseline);
4426 hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, originX, 0.0);
4427 ok(hr == S_OK, "%d: unexpected hr %#lx.\n", i, hr);
4428 ok(ctxt.originX == originX, "%d: got %f, originX %f\n", i, ctxt.originX, originX);
4429 ok(float_eq(ctxt.originY, expectedY), "%d: got %f, expected %f, baseline %f\n",
4430 i, ctxt.originY, expectedY, baseline);
4432 IDWriteTextLayout_Release(layout2);
4436 IDWriteTextLayout_Release(layout);
4437 IDWriteTextFormat_Release(format);
4438 IDWriteFontFace_Release(fontface);
4439 IDWriteFactory_Release(factory);
4442 static void test_SetWordWrapping(void)
4444 static const WCHAR strW[] = {'a',' ','s','o','m','e',' ','t','e','x','t',' ','a','n','d',
4445 ' ','a',' ','b','i','t',' ','m','o','r','e','\n','b'};
4446 IDWriteTextFormat *format;
4447 IDWriteTextLayout *layout;
4448 IDWriteFactory *factory;
4449 DWRITE_WORD_WRAPPING v;
4450 UINT32 count;
4451 HRESULT hr;
4453 factory = create_factory();
4455 hr = IDWriteFactory_CreateTextFormat(factory, L"Tahoma", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
4456 DWRITE_FONT_STRETCH_NORMAL, 12.0f, L"en-us", &format);
4457 ok(hr == S_OK, "Failed to create text format, hr %#lx.\n", hr);
4459 v = IDWriteTextFormat_GetWordWrapping(format);
4460 ok(v == DWRITE_WORD_WRAPPING_WRAP, "got %d\n", v);
4462 hr = IDWriteFactory_CreateTextLayout(factory, strW, ARRAY_SIZE(strW), format, 10.0f, 100.0f, &layout);
4463 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4465 v = IDWriteTextLayout_GetWordWrapping(layout);
4466 ok(v == DWRITE_WORD_WRAPPING_WRAP, "got %d\n", v);
4468 hr = IDWriteTextLayout_SetWordWrapping(layout, DWRITE_WORD_WRAPPING_NO_WRAP);
4469 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4471 hr = IDWriteTextLayout_SetWordWrapping(layout, DWRITE_WORD_WRAPPING_NO_WRAP);
4472 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4474 v = IDWriteTextFormat_GetWordWrapping(format);
4475 ok(v == DWRITE_WORD_WRAPPING_WRAP, "got %d\n", v);
4477 /* disable wrapping, text has explicit newline */
4478 hr = IDWriteTextLayout_SetWordWrapping(layout, DWRITE_WORD_WRAPPING_NO_WRAP);
4479 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4481 count = 0;
4482 hr = IDWriteTextLayout_GetLineMetrics(layout, NULL, 0, &count);
4483 ok(hr == E_NOT_SUFFICIENT_BUFFER, "Unexpected hr %#lx.\n", hr);
4484 ok(count == 2, "got %u\n", count);
4486 hr = IDWriteTextLayout_SetWordWrapping(layout, DWRITE_WORD_WRAPPING_WRAP);
4487 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4489 count = 0;
4490 hr = IDWriteTextLayout_GetLineMetrics(layout, NULL, 0, &count);
4491 ok(hr == E_NOT_SUFFICIENT_BUFFER, "Unexpected hr %#lx.\n", hr);
4492 ok(count > 2, "got %u\n", count);
4494 IDWriteTextLayout_Release(layout);
4495 IDWriteTextFormat_Release(format);
4496 IDWriteFactory_Release(factory);
4499 /* Collection dedicated to fallback testing */
4501 static const WCHAR g_blahfontW[] = {'B','l','a','h',0};
4502 static HRESULT WINAPI fontcollection_QI(IDWriteFontCollection *iface, REFIID riid, void **obj)
4504 if (IsEqualIID(riid, &IID_IDWriteFontCollection) || IsEqualIID(riid, &IID_IUnknown)) {
4505 *obj = iface;
4506 IDWriteFontCollection_AddRef(iface);
4507 return S_OK;
4510 *obj = NULL;
4511 return E_NOINTERFACE;
4514 static ULONG WINAPI fontcollection_AddRef(IDWriteFontCollection *iface)
4516 return 2;
4519 static ULONG WINAPI fontcollection_Release(IDWriteFontCollection *iface)
4521 return 1;
4524 static UINT32 WINAPI fontcollection_GetFontFamilyCount(IDWriteFontCollection *iface)
4526 ok(0, "unexpected call\n");
4527 return 0;
4530 static HRESULT WINAPI fontcollection_GetFontFamily(IDWriteFontCollection *iface, UINT32 index, IDWriteFontFamily **family)
4532 if (index == 123456)
4534 IDWriteFactory *factory = create_factory();
4535 IDWriteFontCollection *syscollection;
4536 BOOL exists;
4537 HRESULT hr;
4539 hr = IDWriteFactory_GetSystemFontCollection(factory, &syscollection, FALSE);
4540 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4542 hr = IDWriteFontCollection_FindFamilyName(syscollection, L"Tahoma", &index, &exists);
4543 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4545 hr = IDWriteFontCollection_GetFontFamily(syscollection, index, family);
4546 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4548 IDWriteFontCollection_Release(syscollection);
4549 IDWriteFactory_Release(factory);
4550 return S_OK;
4553 ok(0, "unexpected call\n");
4554 return E_NOTIMPL;
4557 static HRESULT WINAPI fontcollection_FindFamilyName(IDWriteFontCollection *iface, WCHAR const *name, UINT32 *index, BOOL *exists)
4559 if (!lstrcmpW(name, g_blahfontW)) {
4560 *index = 123456;
4561 *exists = TRUE;
4562 return S_OK;
4564 ok(0, "unexpected call, name %s\n", wine_dbgstr_w(name));
4565 return E_NOTIMPL;
4568 static HRESULT WINAPI fontcollection_GetFontFromFontFace(IDWriteFontCollection *iface, IDWriteFontFace *face, IDWriteFont **font)
4570 ok(0, "unexpected call\n");
4571 return E_NOTIMPL;
4574 static const IDWriteFontCollectionVtbl fallbackcollectionvtbl = {
4575 fontcollection_QI,
4576 fontcollection_AddRef,
4577 fontcollection_Release,
4578 fontcollection_GetFontFamilyCount,
4579 fontcollection_GetFontFamily,
4580 fontcollection_FindFamilyName,
4581 fontcollection_GetFontFromFontFace
4584 static IDWriteFontCollection fallbackcollection = { &fallbackcollectionvtbl };
4586 static void test_MapCharacters(void)
4588 static const WCHAR str2W[] = {'a',0x3058,'b',0};
4589 IDWriteLocalizedStrings *strings;
4590 IDWriteFontFallback *fallback;
4591 IDWriteFactory2 *factory2;
4592 IDWriteFactory *factory;
4593 UINT32 mappedlength;
4594 IDWriteFont *font;
4595 WCHAR buffW[50];
4596 BOOL exists;
4597 FLOAT scale;
4598 HRESULT hr;
4600 factory = create_factory();
4602 hr = IDWriteFactory_QueryInterface(factory, &IID_IDWriteFactory2, (void**)&factory2);
4603 IDWriteFactory_Release(factory);
4604 if (hr != S_OK) {
4605 win_skip("MapCharacters() is not supported\n");
4606 return;
4609 fallback = NULL;
4610 hr = IDWriteFactory2_GetSystemFontFallback(factory2, &fallback);
4611 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4612 ok(fallback != NULL, "got %p\n", fallback);
4614 mappedlength = 1;
4615 scale = 0.0f;
4616 font = (void*)0xdeadbeef;
4617 hr = IDWriteFontFallback_MapCharacters(fallback, NULL, 0, 0, NULL, NULL, DWRITE_FONT_WEIGHT_NORMAL,
4618 DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, &mappedlength, &font, &scale);
4619 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
4620 ok(mappedlength == 0, "got %u\n", mappedlength);
4621 ok(scale == 1.0f, "got %f\n", scale);
4622 ok(font == NULL, "got %p\n", font);
4624 /* zero length source */
4625 g_source = L"abc";
4626 mappedlength = 1;
4627 scale = 0.0f;
4628 font = (void*)0xdeadbeef;
4629 hr = IDWriteFontFallback_MapCharacters(fallback, &analysissource, 0, 0, NULL, NULL, DWRITE_FONT_WEIGHT_NORMAL,
4630 DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, &mappedlength, &font, &scale);
4631 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4632 ok(mappedlength == 0, "got %u\n", mappedlength);
4633 ok(scale == 1.0f, "got %f\n", scale);
4634 ok(font == NULL, "got %p\n", font);
4636 g_source = L"abc";
4637 mappedlength = 0;
4638 scale = 0.0f;
4639 font = NULL;
4640 hr = IDWriteFontFallback_MapCharacters(fallback, &analysissource, 0, 1, NULL, NULL, DWRITE_FONT_WEIGHT_NORMAL,
4641 DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, &mappedlength, &font, &scale);
4642 todo_wine {
4643 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4644 ok(mappedlength == 1, "got %u\n", mappedlength);
4646 ok(scale == 1.0f, "got %f\n", scale);
4647 todo_wine
4648 ok(font != NULL, "got %p\n", font);
4649 if (font) {
4650 IDWriteFont_Release(font);
4652 /* same Latin text, full length */
4653 g_source = L"abc";
4654 mappedlength = 0;
4655 scale = 0.0f;
4656 font = NULL;
4657 hr = IDWriteFontFallback_MapCharacters(fallback, &analysissource, 0, 3, NULL, NULL, DWRITE_FONT_WEIGHT_NORMAL,
4658 DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, &mappedlength, &font, &scale);
4659 todo_wine {
4660 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4661 ok(mappedlength == 3, "got %u\n", mappedlength);
4663 ok(scale == 1.0f, "got %f\n", scale);
4664 todo_wine
4665 ok(font != NULL, "got %p\n", font);
4666 if (font) {
4667 IDWriteFont_Release(font);
4669 /* string 'a\x3058b' */
4670 g_source = str2W;
4671 mappedlength = 0;
4672 scale = 0.0f;
4673 font = NULL;
4674 hr = IDWriteFontFallback_MapCharacters(fallback, &analysissource, 0, 3, NULL, NULL, DWRITE_FONT_WEIGHT_NORMAL,
4675 DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, &mappedlength, &font, &scale);
4676 todo_wine {
4677 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4678 ok(mappedlength == 1, "got %u\n", mappedlength);
4680 ok(scale == 1.0f, "got %f\n", scale);
4681 todo_wine
4682 ok(font != NULL, "got %p\n", font);
4683 if (font) {
4684 IDWriteFont_Release(font);
4686 g_source = str2W;
4687 mappedlength = 0;
4688 scale = 0.0f;
4689 font = NULL;
4690 hr = IDWriteFontFallback_MapCharacters(fallback, &analysissource, 1, 2, NULL, NULL, DWRITE_FONT_WEIGHT_NORMAL,
4691 DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, &mappedlength, &font, &scale);
4692 todo_wine {
4693 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4694 ok(mappedlength == 1, "got %u\n", mappedlength);
4696 ok(scale == 1.0f, "got %f\n", scale);
4697 todo_wine
4698 ok(font != NULL, "got %p\n", font);
4699 if (font) {
4700 IDWriteFont_Release(font);
4702 /* Try with explicit collection, Tahoma will be forced. */
4703 /* 1. Latin part */
4704 g_source = str2W;
4705 mappedlength = 0;
4706 scale = 0.0f;
4707 font = NULL;
4708 hr = IDWriteFontFallback_MapCharacters(fallback, &analysissource, 0, 3, &fallbackcollection, g_blahfontW, DWRITE_FONT_WEIGHT_NORMAL,
4709 DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, &mappedlength, &font, &scale);
4710 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4711 ok(mappedlength == 1, "got %u\n", mappedlength);
4712 ok(scale == 1.0f, "got %f\n", scale);
4713 ok(font != NULL, "got %p\n", font);
4715 exists = FALSE;
4716 hr = IDWriteFont_GetInformationalStrings(font, DWRITE_INFORMATIONAL_STRING_WIN32_FAMILY_NAMES, &strings, &exists);
4717 ok(hr == S_OK && exists, "Unexpected hr %#lx, exists %d.\n", hr, exists);
4718 hr = IDWriteLocalizedStrings_GetString(strings, 0, buffW, ARRAY_SIZE(buffW));
4719 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4720 ok(!lstrcmpW(buffW, L"Tahoma"), "Unexpected string %s.\n", wine_dbgstr_w(buffW));
4721 IDWriteLocalizedStrings_Release(strings);
4722 IDWriteFont_Release(font);
4724 /* 2. Hiragana character, force Tahoma font does not support Japanese */
4725 g_source = str2W;
4726 mappedlength = 0;
4727 scale = 0.0f;
4728 font = NULL;
4729 hr = IDWriteFontFallback_MapCharacters(fallback, &analysissource, 1, 1, &fallbackcollection, g_blahfontW, DWRITE_FONT_WEIGHT_NORMAL,
4730 DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, &mappedlength, &font, &scale);
4731 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4732 ok(mappedlength == 1, "got %u\n", mappedlength);
4733 ok(scale == 1.0f, "got %f\n", scale);
4734 ok(font != NULL, "got %p\n", font);
4736 exists = FALSE;
4737 hr = IDWriteFont_GetInformationalStrings(font, DWRITE_INFORMATIONAL_STRING_WIN32_FAMILY_NAMES, &strings, &exists);
4738 ok(hr == S_OK && exists, "Unexpected hr %#lx, exists %d.\n", hr, exists);
4739 hr = IDWriteLocalizedStrings_GetString(strings, 0, buffW, ARRAY_SIZE(buffW));
4740 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4741 todo_wine
4742 ok(lstrcmpW(buffW, L"Tahoma"), "Unexpected string %s.\n", wine_dbgstr_w(buffW));
4743 IDWriteLocalizedStrings_Release(strings);
4744 IDWriteFont_Release(font);
4746 IDWriteFontFallback_Release(fallback);
4747 IDWriteFactory2_Release(factory2);
4750 static void get_font_name(IDWriteFont *font, WCHAR *name, UINT32 size)
4752 IDWriteLocalizedStrings *names;
4753 IDWriteFontFamily *family;
4754 UINT32 index;
4755 BOOL exists;
4756 HRESULT hr;
4758 hr = IDWriteFont_GetFontFamily(font, &family);
4759 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4761 hr = IDWriteFontFamily_GetFamilyNames(family, &names);
4762 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4764 hr = IDWriteLocalizedStrings_FindLocaleName(names, L"en-us", &index, &exists);
4765 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4766 ok(index != UINT_MAX && exists, "Name was not found.\n");
4767 hr = IDWriteLocalizedStrings_GetString(names, index, name, size);
4768 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4770 IDWriteLocalizedStrings_Release(names);
4771 IDWriteFontFamily_Release(family);
4774 static void test_system_fallback(void)
4776 static const struct fallback_test
4778 const WCHAR text[2];
4779 const WCHAR *name;
4781 tests[] =
4783 { { 0x25d4, 0}, L"Segoe UI Symbol" },
4785 IDWriteFontFallback *fallback;
4786 IDWriteFactory2 *factory;
4787 IDWriteFont *font;
4788 UINT32 i, length;
4789 WCHAR name[256];
4790 BOOL exists;
4791 float scale;
4792 HRESULT hr;
4794 if (!(factory = create_factory_iid(&IID_IDWriteFactory2)))
4796 win_skip("System font fallback API is not supported.\n");
4797 return;
4800 hr = IDWriteFactory2_GetSystemFontFallback(factory, &fallback);
4801 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4803 for (i = 0; i < ARRAY_SIZE(tests); ++i)
4805 g_source = tests[i].text;
4806 hr = IDWriteFontFallback_MapCharacters(fallback, &analysissource, 0, 1, NULL, NULL, DWRITE_FONT_WEIGHT_NORMAL,
4807 DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, &length, &font, &scale);
4808 todo_wine
4809 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4810 if (hr != S_OK) continue;
4811 ok(length == 1, "Unexpected length %u\n", length);
4812 ok(scale == 1.0f, "got %f\n", scale);
4814 get_font_name(font, name, ARRAY_SIZE(name));
4815 todo_wine
4816 ok(!wcscmp(name, tests[i].name), "%u: unexpected name %s.\n", i, wine_dbgstr_w(name));
4818 hr = IDWriteFont_HasCharacter(font, g_source[0], &exists);
4819 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4820 todo_wine
4821 ok(exists, "%s missing character %#x\n", wine_dbgstr_w(name), g_source[0]);
4823 IDWriteFont_Release(font);
4826 IDWriteFontFallback_Release(fallback);
4827 IDWriteFactory2_Release(factory);
4830 static void test_FontFallbackBuilder(void)
4832 IDWriteFontFallback *fallback, *fallback2;
4833 IDWriteFontFallbackBuilder *builder;
4834 IDWriteFontFallback1 *fallback1;
4835 DWRITE_UNICODE_RANGE range;
4836 IDWriteFactory2 *factory2;
4837 IDWriteFactory *factory;
4838 const WCHAR *familyW;
4839 UINT32 mappedlength;
4840 IDWriteFont *font;
4841 FLOAT scale;
4842 HRESULT hr;
4843 ULONG ref;
4845 factory = create_factory();
4847 hr = IDWriteFactory_QueryInterface(factory, &IID_IDWriteFactory2, (void**)&factory2);
4848 IDWriteFactory_Release(factory);
4850 if (hr != S_OK) {
4851 win_skip("IDWriteFontFallbackBuilder is not supported\n");
4852 return;
4855 EXPECT_REF(factory2, 1);
4856 hr = IDWriteFactory2_CreateFontFallbackBuilder(factory2, &builder);
4857 EXPECT_REF(factory2, 2);
4859 fallback = NULL;
4860 EXPECT_REF(factory2, 2);
4861 EXPECT_REF(builder, 1);
4862 hr = IDWriteFontFallbackBuilder_CreateFontFallback(builder, &fallback);
4863 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4864 EXPECT_REF(factory2, 3);
4865 EXPECT_REF(fallback, 1);
4866 EXPECT_REF(builder, 1);
4868 IDWriteFontFallback_AddRef(fallback);
4869 EXPECT_REF(builder, 1);
4870 EXPECT_REF(fallback, 2);
4871 EXPECT_REF(factory2, 3);
4872 IDWriteFontFallback_Release(fallback);
4874 /* New instance is created every time, even if mappings have not changed. */
4875 hr = IDWriteFontFallbackBuilder_CreateFontFallback(builder, &fallback2);
4876 ok(hr == S_OK, "Failed to create fallback object, hr %#lx.\n", hr);
4877 ok(fallback != fallback2, "Unexpected fallback instance.\n");
4878 IDWriteFontFallback_Release(fallback2);
4880 hr = IDWriteFontFallbackBuilder_AddMapping(builder, NULL, 0, NULL, 0, NULL, NULL, NULL, 0.0f);
4881 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
4883 range.first = 'A';
4884 range.last = 'B';
4885 hr = IDWriteFontFallbackBuilder_AddMapping(builder, &range, 0, NULL, 0, NULL, NULL, NULL, 0.0f);
4886 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
4888 hr = IDWriteFontFallbackBuilder_AddMapping(builder, &range, 0, NULL, 0, NULL, NULL, NULL, 1.0f);
4889 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
4891 hr = IDWriteFontFallbackBuilder_AddMapping(builder, &range, 0, &familyW, 1, NULL, NULL, NULL, 1.0f);
4892 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
4894 hr = IDWriteFontFallbackBuilder_AddMapping(builder, NULL, 0, &familyW, 1, NULL, NULL, NULL, 1.0f);
4895 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
4897 /* negative scaling factor */
4898 range.first = range.last = 0;
4899 familyW = g_blahfontW;
4900 hr = IDWriteFontFallbackBuilder_AddMapping(builder, &range, 1, &familyW, 1, NULL, NULL, NULL, -1.0f);
4901 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
4903 hr = IDWriteFontFallbackBuilder_AddMapping(builder, &range, 1, &familyW, 1, NULL, NULL, NULL, 0.0f);
4904 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4906 /* empty range */
4907 range.first = range.last = 0;
4908 familyW = g_blahfontW;
4909 hr = IDWriteFontFallbackBuilder_AddMapping(builder, &range, 1, &familyW, 1, NULL, NULL, NULL, 1.0f);
4910 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4912 range.first = range.last = 0;
4913 hr = IDWriteFontFallbackBuilder_AddMapping(builder, &range, 1, &familyW, 1, NULL, NULL, NULL, 2.0f);
4914 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4916 range.first = range.last = 'A';
4917 hr = IDWriteFontFallbackBuilder_AddMapping(builder, &range, 1, &familyW, 1, NULL, NULL, NULL, 3.0f);
4918 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4920 range.first = 'B';
4921 range.last = 'A';
4922 hr = IDWriteFontFallbackBuilder_AddMapping(builder, &range, 1, &familyW, 1, NULL, NULL, NULL, 4.0f);
4923 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4925 IDWriteFontFallback_Release(fallback);
4927 if (0) /* crashes on native */
4928 hr = IDWriteFontFallbackBuilder_CreateFontFallback(builder, NULL);
4930 hr = IDWriteFontFallbackBuilder_CreateFontFallback(builder, &fallback);
4931 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4933 /* fallback font missing from system collection */
4934 g_source = L"A";
4935 mappedlength = 0;
4936 scale = 0.0f;
4937 font = (void*)0xdeadbeef;
4938 hr = IDWriteFontFallback_MapCharacters(fallback, &analysissource, 0, 1, NULL, NULL, DWRITE_FONT_WEIGHT_NORMAL,
4939 DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, &mappedlength, &font, &scale);
4940 todo_wine {
4941 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4942 ok(mappedlength == 1, "got %u\n", mappedlength);
4943 ok(scale == 1.0f, "got %f\n", scale);
4944 ok(font == NULL, "got %p\n", font);
4946 IDWriteFontFallback_Release(fallback);
4948 /* remap with custom collection */
4949 range.first = range.last = 'A';
4950 hr = IDWriteFontFallbackBuilder_AddMapping(builder, &range, 1, &familyW, 1, &fallbackcollection, NULL, NULL, 5.0f);
4951 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4953 hr = IDWriteFontFallbackBuilder_CreateFontFallback(builder, &fallback);
4954 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4956 g_source = L"A";
4957 mappedlength = 0;
4958 scale = 0.0f;
4959 font = NULL;
4960 hr = IDWriteFontFallback_MapCharacters(fallback, &analysissource, 0, 1, NULL, NULL, DWRITE_FONT_WEIGHT_NORMAL,
4961 DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, &mappedlength, &font, &scale);
4962 todo_wine {
4963 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4964 ok(mappedlength == 1, "got %u\n", mappedlength);
4965 ok(scale == 5.0f, "got %f\n", scale);
4966 ok(font != NULL, "got %p\n", font);
4968 if (font)
4969 IDWriteFont_Release(font);
4971 IDWriteFontFallback_Release(fallback);
4973 range.first = 'B';
4974 range.last = 'A';
4975 hr = IDWriteFontFallbackBuilder_AddMapping(builder, &range, 1, &familyW, 1, &fallbackcollection, NULL, NULL, 6.0f);
4976 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4978 hr = IDWriteFontFallbackBuilder_CreateFontFallback(builder, &fallback);
4979 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4981 g_source = L"A";
4982 mappedlength = 0;
4983 scale = 0.0f;
4984 font = NULL;
4985 hr = IDWriteFontFallback_MapCharacters(fallback, &analysissource, 0, 1, NULL, NULL, DWRITE_FONT_WEIGHT_NORMAL,
4986 DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, &mappedlength, &font, &scale);
4987 todo_wine {
4988 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
4989 ok(mappedlength == 1, "got %u\n", mappedlength);
4990 ok(scale == 5.0f, "got %f\n", scale);
4991 ok(font != NULL, "got %p\n", font);
4993 if (font)
4994 IDWriteFont_Release(font);
4996 IDWriteFontFallback_Release(fallback);
4998 /* explicit locale */
4999 range.first = 'A';
5000 range.last = 'B';
5001 hr = IDWriteFontFallbackBuilder_AddMapping(builder, &range, 1, &familyW, 1, &fallbackcollection, L"locale", NULL, 6.0f);
5002 ok(hr == S_OK, "Failed to add mapping, hr %#lx.\n", hr);
5004 hr = IDWriteFontFallbackBuilder_CreateFontFallback(builder, &fallback);
5005 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5007 g_source = L"A";
5008 mappedlength = 0;
5009 scale = 0.0f;
5010 font = NULL;
5011 hr = IDWriteFontFallback_MapCharacters(fallback, &analysissource, 0, 1, NULL, NULL, DWRITE_FONT_WEIGHT_NORMAL,
5012 DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, &mappedlength, &font, &scale);
5013 todo_wine {
5014 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5015 ok(mappedlength == 1, "got %u\n", mappedlength);
5016 ok(scale == 5.0f, "got %f\n", scale);
5017 ok(font != NULL, "got %p\n", font);
5019 if (font)
5020 IDWriteFont_Release(font);
5022 if (SUCCEEDED(IDWriteFontFallback_QueryInterface(fallback, &IID_IDWriteFontFallback1, (void **)&fallback1)))
5024 IDWriteFontFallback1_Release(fallback1);
5026 else
5027 win_skip("IDWriteFontFallback1 is not supported.\n");
5029 IDWriteFontFallback_Release(fallback);
5031 IDWriteFontFallbackBuilder_Release(builder);
5032 ref = IDWriteFactory2_Release(factory2);
5033 ok(ref == 0, "Factory is not released, ref %lu.\n", ref);
5036 static void test_fallback(void)
5038 IDWriteFontFallback *fallback, *fallback2;
5039 IDWriteFontFallback1 *fallback1;
5040 DWRITE_CLUSTER_METRICS clusters[4];
5041 DWRITE_TEXT_METRICS metrics;
5042 IDWriteTextLayout2 *layout2;
5043 IDWriteTextFormat1 *format1;
5044 IDWriteTextFormat *format;
5045 IDWriteTextLayout *layout;
5046 IDWriteFactory2 *factory2;
5047 IDWriteFactory *factory;
5048 UINT32 count, i;
5049 FLOAT width;
5050 HRESULT hr;
5052 factory = create_factory();
5054 /* Font does not exist in system collection. */
5055 hr = IDWriteFactory_CreateTextFormat(factory, g_blahfontW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
5056 DWRITE_FONT_STRETCH_NORMAL, 10.0f, L"en-us", &format);
5057 ok(hr == S_OK, "Failed to create text format, hr %#lx.\n", hr);
5059 hr = IDWriteFactory_CreateTextLayout(factory, L"abcd", 4, format, 1000.0f, 1000.0f, &layout);
5060 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
5062 count = 0;
5063 hr = IDWriteTextLayout_GetClusterMetrics(layout, clusters, 4, &count);
5064 todo_wine {
5065 ok(hr == S_OK, "Failed to get cluster metrics, hr %#lx.\n", hr);
5066 ok(count == 4, "Unexpected count %u.\n", count);
5068 for (i = 0, width = 0.0; i < count; i++)
5069 width += clusters[i].width;
5071 memset(&metrics, 0xcc, sizeof(metrics));
5072 hr = IDWriteTextLayout_GetMetrics(layout, &metrics);
5073 ok(hr == S_OK, "Failed to get layout metrics, hr %#lx.\n", hr);
5074 todo_wine {
5075 ok(metrics.width > 0.0 && metrics.width == width, "Unexpected width %.2f, expected %.2f.\n", metrics.width, width);
5076 ok(metrics.height > 0.0, "Unexpected height %.2f.\n", metrics.height);
5077 ok(metrics.lineCount == 1, "Unexpected line count %u.\n", metrics.lineCount);
5079 IDWriteTextLayout_Release(layout);
5080 IDWriteTextFormat_Release(format);
5082 hr = IDWriteFactory_CreateTextFormat(factory, L"Tahoma", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
5083 DWRITE_FONT_STRETCH_NORMAL, 10.0f, L"en-us", &format);
5084 ok(hr == S_OK, "Failed to create text format, hr %#lx.\n", hr);
5086 /* Existing font. */
5087 hr = IDWriteFactory_CreateTextLayout(factory, L"abcd", 4, format, 1000.0f, 1000.0f, &layout);
5088 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
5089 IDWriteTextFormat_Release(format);
5091 hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextLayout2, (void**)&layout2);
5092 IDWriteTextLayout_Release(layout);
5094 if (hr != S_OK) {
5095 win_skip("GetFontFallback() is not supported.\n");
5096 IDWriteFactory_Release(factory);
5097 return;
5100 if (0) /* crashes on native */
5101 hr = IDWriteTextLayout2_GetFontFallback(layout2, NULL);
5103 fallback = (void*)0xdeadbeef;
5104 hr = IDWriteTextLayout2_GetFontFallback(layout2, &fallback);
5105 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5106 ok(fallback == NULL, "got %p\n", fallback);
5108 hr = IDWriteTextLayout2_QueryInterface(layout2, &IID_IDWriteTextFormat1, (void**)&format1);
5109 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5111 fallback = (void*)0xdeadbeef;
5112 hr = IDWriteTextFormat1_GetFontFallback(format1, &fallback);
5113 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5114 ok(fallback == NULL, "got %p\n", fallback);
5116 hr = IDWriteFactory_QueryInterface(factory, &IID_IDWriteFactory2, (void**)&factory2);
5117 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5119 fallback = NULL;
5120 hr = IDWriteFactory2_GetSystemFontFallback(factory2, &fallback);
5121 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5122 ok(fallback != NULL, "got %p\n", fallback);
5124 hr = IDWriteTextFormat1_SetFontFallback(format1, fallback);
5125 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5127 fallback2 = (void*)0xdeadbeef;
5128 hr = IDWriteTextLayout2_GetFontFallback(layout2, &fallback2);
5129 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5130 ok(fallback2 == fallback, "got %p\n", fallback2);
5132 hr = IDWriteTextLayout2_SetFontFallback(layout2, NULL);
5133 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5135 fallback2 = (void*)0xdeadbeef;
5136 hr = IDWriteTextFormat1_GetFontFallback(format1, &fallback2);
5137 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5138 ok(fallback2 == NULL, "got %p\n", fallback2);
5140 if (SUCCEEDED(IDWriteFontFallback_QueryInterface(fallback, &IID_IDWriteFontFallback1, (void **)&fallback1)))
5142 IDWriteFontFallback1_Release(fallback1);
5144 else
5145 win_skip("IDWriteFontFallback1 is not supported.\n");
5147 IDWriteFontFallback_Release(fallback);
5148 IDWriteTextFormat1_Release(format1);
5149 IDWriteTextLayout2_Release(layout2);
5150 IDWriteFactory_Release(factory);
5153 static void test_SetTypography(void)
5155 IDWriteTypography *typography, *typography2;
5156 IDWriteTextFormat *format;
5157 IDWriteTextLayout *layout;
5158 DWRITE_TEXT_RANGE range;
5159 IDWriteFactory *factory;
5160 HRESULT hr;
5162 factory = create_factory();
5164 hr = IDWriteFactory_CreateTextFormat(factory, L"Tahoma", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
5165 DWRITE_FONT_STRETCH_NORMAL, 10.0f, L"en-us", &format);
5166 ok(hr == S_OK, "Failed to create text format, hr %#lx.\n", hr);
5168 hr = IDWriteFactory_CreateTextLayout(factory, L"afib", 4, format, 1000.0f, 1000.0f, &layout);
5169 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
5170 IDWriteTextFormat_Release(format);
5172 hr = IDWriteFactory_CreateTypography(factory, &typography);
5173 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5175 EXPECT_REF(typography, 1);
5176 range.startPosition = 0;
5177 range.length = 2;
5178 hr = IDWriteTextLayout_SetTypography(layout, typography, range);
5179 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5180 EXPECT_REF(typography, 2);
5182 hr = IDWriteTextLayout_GetTypography(layout, 0, &typography2, NULL);
5183 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5184 ok(typography2 == typography, "got %p, expected %p\n", typography2, typography);
5185 IDWriteTypography_Release(typography2);
5186 IDWriteTypography_Release(typography);
5188 hr = IDWriteFactory_CreateTypography(factory, &typography2);
5189 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5191 range.startPosition = 0;
5192 range.length = 1;
5193 hr = IDWriteTextLayout_SetTypography(layout, typography2, range);
5194 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5195 EXPECT_REF(typography2, 2);
5196 IDWriteTypography_Release(typography2);
5198 hr = IDWriteTextLayout_GetTypography(layout, 0, &typography, &range);
5199 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5200 ok(range.length == 1, "got %u\n", range.length);
5202 IDWriteTypography_Release(typography);
5204 IDWriteTextLayout_Release(layout);
5205 IDWriteFactory_Release(factory);
5208 static void test_SetLastLineWrapping(void)
5210 IDWriteTextLayout2 *layout2;
5211 IDWriteTextFormat1 *format1;
5212 IDWriteTextLayout *layout;
5213 IDWriteTextFormat *format;
5214 IDWriteFactory *factory;
5215 HRESULT hr;
5216 BOOL ret;
5218 factory = create_factory();
5220 hr = IDWriteFactory_CreateTextFormat(factory, L"Tahoma", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
5221 DWRITE_FONT_STRETCH_NORMAL, 10.0f, L"en-us", &format);
5222 ok(hr == S_OK, "Failed to create text format, hr %#lx.\n", hr);
5224 hr = IDWriteTextFormat_QueryInterface(format, &IID_IDWriteTextFormat1, (void**)&format1);
5225 IDWriteTextFormat_Release(format);
5226 if (hr != S_OK) {
5227 win_skip("SetLastLineWrapping() is not supported\n");
5228 IDWriteFactory_Release(factory);
5229 return;
5232 ret = IDWriteTextFormat1_GetLastLineWrapping(format1);
5233 ok(ret, "got %d\n", ret);
5235 hr = IDWriteTextFormat1_SetLastLineWrapping(format1, FALSE);
5236 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5238 hr = IDWriteFactory_CreateTextLayout(factory, L"a", 1, (IDWriteTextFormat *)format1, 1000.0f, 1000.0f, &layout);
5239 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
5241 hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextLayout2, (void**)&layout2);
5242 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5243 IDWriteTextLayout_Release(layout);
5245 ret = IDWriteTextLayout2_GetLastLineWrapping(layout2);
5246 ok(!ret, "got %d\n", ret);
5248 hr = IDWriteTextLayout2_SetLastLineWrapping(layout2, TRUE);
5249 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5251 IDWriteTextLayout2_Release(layout2);
5252 IDWriteTextFormat1_Release(format1);
5253 IDWriteFactory_Release(factory);
5256 static void test_SetOpticalAlignment(void)
5258 DWRITE_OPTICAL_ALIGNMENT alignment;
5259 IDWriteTextLayout2 *layout2;
5260 IDWriteTextFormat1 *format1;
5261 IDWriteTextLayout *layout;
5262 IDWriteTextFormat *format;
5263 IDWriteFactory *factory;
5264 HRESULT hr;
5266 factory = create_factory();
5268 hr = IDWriteFactory_CreateTextFormat(factory, L"Tahoma", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
5269 DWRITE_FONT_STRETCH_NORMAL, 10.0f, L"en-us", &format);
5270 ok(hr == S_OK, "Failed to create text format, hr %#lx.\n", hr);
5272 hr = IDWriteTextFormat_QueryInterface(format, &IID_IDWriteTextFormat1, (void**)&format1);
5273 IDWriteTextFormat_Release(format);
5274 if (hr != S_OK) {
5275 win_skip("SetOpticalAlignment() is not supported\n");
5276 IDWriteFactory_Release(factory);
5277 return;
5280 alignment = IDWriteTextFormat1_GetOpticalAlignment(format1);
5281 ok(alignment == DWRITE_OPTICAL_ALIGNMENT_NONE, "got %d\n", alignment);
5283 hr = IDWriteFactory_CreateTextLayout(factory, L"a", 1, (IDWriteTextFormat *)format1, 1000.0f, 1000.0f, &layout);
5284 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
5286 hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextLayout2, (void**)&layout2);
5287 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5288 IDWriteTextLayout_Release(layout);
5289 IDWriteTextFormat1_Release(format1);
5291 alignment = IDWriteTextLayout2_GetOpticalAlignment(layout2);
5292 ok(alignment == DWRITE_OPTICAL_ALIGNMENT_NONE, "got %d\n", alignment);
5294 hr = IDWriteTextLayout2_QueryInterface(layout2, &IID_IDWriteTextFormat1, (void**)&format1);
5295 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5297 alignment = IDWriteTextFormat1_GetOpticalAlignment(format1);
5298 ok(alignment == DWRITE_OPTICAL_ALIGNMENT_NONE, "got %d\n", alignment);
5300 hr = IDWriteTextLayout2_SetOpticalAlignment(layout2, DWRITE_OPTICAL_ALIGNMENT_NO_SIDE_BEARINGS);
5301 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5303 hr = IDWriteTextLayout2_SetOpticalAlignment(layout2, DWRITE_OPTICAL_ALIGNMENT_NO_SIDE_BEARINGS+1);
5304 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
5306 alignment = IDWriteTextFormat1_GetOpticalAlignment(format1);
5307 ok(alignment == DWRITE_OPTICAL_ALIGNMENT_NO_SIDE_BEARINGS, "got %d\n", alignment);
5309 hr = IDWriteTextFormat1_SetOpticalAlignment(format1, DWRITE_OPTICAL_ALIGNMENT_NONE);
5310 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5312 hr = IDWriteTextFormat1_SetOpticalAlignment(format1, DWRITE_OPTICAL_ALIGNMENT_NO_SIDE_BEARINGS+1);
5313 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
5315 alignment = IDWriteTextLayout2_GetOpticalAlignment(layout2);
5316 ok(alignment == DWRITE_OPTICAL_ALIGNMENT_NONE, "got %d\n", alignment);
5318 IDWriteTextLayout2_Release(layout2);
5319 IDWriteTextFormat1_Release(format1);
5320 IDWriteFactory_Release(factory);
5323 static const struct drawcall_entry drawunderline_seq[] = {
5324 { DRAW_GLYPHRUN, {'a','e',0x0300,0}, {'e','n','-','u','s',0}, 2 }, /* reported runs can't mix different underline values */
5325 { DRAW_GLYPHRUN, {'d',0}, {'e','n','-','u','s',0}, 1 },
5326 { DRAW_UNDERLINE, {0}, {'e','n','-','u','s',0} },
5327 { DRAW_LAST_KIND }
5330 static const struct drawcall_entry drawunderline2_seq[] = {
5331 { DRAW_GLYPHRUN, {'a',0}, {'e','n','-','u','s',0}, 1 },
5332 { DRAW_GLYPHRUN, {'e',0}, {'e','n','-','u','s',0}, 1 },
5333 { DRAW_UNDERLINE, {0}, {'e','n','-','u','s',0} },
5334 { DRAW_LAST_KIND }
5337 static const struct drawcall_entry drawunderline3_seq[] = {
5338 { DRAW_GLYPHRUN, {'a',0}, {'e','n','-','c','a',0}, 1 },
5339 { DRAW_GLYPHRUN, {'e',0}, {'e','n','-','u','s',0}, 1 },
5340 { DRAW_UNDERLINE, {0}, {'e','n','-','c','a',0} },
5341 { DRAW_UNDERLINE, {0}, {'e','n','-','u','s',0} },
5342 { DRAW_LAST_KIND }
5345 static const struct drawcall_entry drawunderline4_seq[] = {
5346 { DRAW_GLYPHRUN, {'a',0}, {'e','n','-','u','s',0}, 1 },
5347 { DRAW_GLYPHRUN, {'e',0}, {'e','n','-','u','s',0}, 1 },
5348 { DRAW_UNDERLINE, {0}, {'e','n','-','u','s',0} },
5349 { DRAW_STRIKETHROUGH },
5350 { DRAW_LAST_KIND }
5353 static void test_SetUnderline(void)
5355 static const WCHAR strW[] = {'a','e',0x0300,'d',0}; /* accent grave */
5356 IDWriteFontCollection *syscollection;
5357 DWRITE_CLUSTER_METRICS clusters[4];
5358 IDWriteTextFormat *format;
5359 IDWriteTextLayout *layout;
5360 DWRITE_TEXT_RANGE range;
5361 IDWriteFactory *factory;
5362 UINT32 count, i;
5363 HRESULT hr;
5365 factory = create_factory();
5367 hr = IDWriteFactory_CreateTextFormat(factory, L"Tahoma", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
5368 DWRITE_FONT_STRETCH_NORMAL, 10.0f, L"en-us", &format);
5369 ok(hr == S_OK, "Failed to create text format, hr %#lx.\n", hr);
5371 hr = IDWriteFactory_CreateTextLayout(factory, strW, 4, format, 1000.0, 1000.0, &layout);
5372 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5374 count = 0;
5375 hr = IDWriteTextLayout_GetClusterMetrics(layout, clusters, ARRAY_SIZE(clusters), &count);
5376 ok(hr == S_OK, "Failed to get cluster metrics, hr %#lx.\n", hr);
5377 ok(count == 3, "Unexpected cluster count %u.\n", count);
5379 range.startPosition = 0;
5380 range.length = 2;
5381 hr = IDWriteTextLayout_SetUnderline(layout, TRUE, range);
5382 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5384 count = 0;
5385 hr = IDWriteTextLayout_GetClusterMetrics(layout, clusters, ARRAY_SIZE(clusters), &count);
5386 ok(hr == S_OK, "Failed to get cluster metrics, hr %#lx.\n", hr);
5387 ok(count == 3, "Unexpected cluster count %u.\n", count);
5389 flush_sequence(sequences, RENDERER_ID);
5390 hr = IDWriteTextLayout_Draw(layout, NULL, &testrenderer, 0.0, 0.0);
5391 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5392 ok_sequence(sequences, RENDERER_ID, drawunderline_seq, "draw underline test", TRUE);
5394 IDWriteTextLayout_Release(layout);
5396 /* 2 characters, same font, significantly different font size. Set underline for both, see how many
5397 underline drawing calls is there. */
5398 hr = IDWriteFactory_CreateTextLayout(factory, strW, 2, format, 1000.0f, 1000.0f, &layout);
5399 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5401 range.startPosition = 0;
5402 range.length = 2;
5403 hr = IDWriteTextLayout_SetUnderline(layout, TRUE, range);
5404 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5406 range.startPosition = 0;
5407 range.length = 1;
5408 hr = IDWriteTextLayout_SetFontSize(layout, 100.0f, range);
5409 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5411 flush_sequence(sequences, RENDERER_ID);
5412 hr = IDWriteTextLayout_Draw(layout, NULL, &testrenderer, 0.0f, 0.0f);
5413 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5414 ok_sequence(sequences, RENDERER_ID, drawunderline2_seq, "draw underline test 2", FALSE);
5416 /* now set different locale for second char, draw again */
5417 range.startPosition = 0;
5418 range.length = 1;
5419 hr = IDWriteTextLayout_SetLocaleName(layout, L"en-CA", range);
5420 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5422 flush_sequence(sequences, RENDERER_ID);
5423 hr = IDWriteTextLayout_Draw(layout, NULL, &testrenderer, 0.0f, 0.0f);
5424 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5425 ok_sequence(sequences, RENDERER_ID, drawunderline3_seq, "draw underline test 2", FALSE);
5427 IDWriteTextLayout_Release(layout);
5429 /* 2 characters, same font properties, first with strikethrough, both underlined */
5430 hr = IDWriteFactory_CreateTextLayout(factory, strW, 2, format, 1000.0f, 1000.0f, &layout);
5431 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5433 range.startPosition = 0;
5434 range.length = 1;
5435 hr = IDWriteTextLayout_SetStrikethrough(layout, TRUE, range);
5436 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5438 range.startPosition = 0;
5439 range.length = 2;
5440 hr = IDWriteTextLayout_SetUnderline(layout, TRUE, range);
5441 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5443 flush_sequence(sequences, RENDERER_ID);
5444 hr = IDWriteTextLayout_Draw(layout, NULL, &testrenderer, 0.0f, 0.0f);
5445 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5446 ok_sequence(sequences, RENDERER_ID, drawunderline4_seq, "draw underline test 4", FALSE);
5448 IDWriteTextLayout_Release(layout);
5450 IDWriteTextFormat_Release(format);
5452 /* Test runHeight value with all available fonts */
5453 hr = IDWriteFactory_GetSystemFontCollection(factory, &syscollection, FALSE);
5454 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5455 count = IDWriteFontCollection_GetFontFamilyCount(syscollection);
5457 for (i = 0; i < count; ++i)
5459 DWRITE_FONT_METRICS fontmetrics;
5460 IDWriteLocalizedStrings *names;
5461 struct renderer_context ctxt;
5462 IDWriteFontFamily *family;
5463 IDWriteFontFace *fontface;
5464 WCHAR nameW[256], str[1];
5465 IDWriteFont *font;
5466 UINT32 codepoint;
5467 UINT16 glyph;
5468 BOOL exists;
5470 format = NULL;
5471 layout = NULL;
5473 hr = IDWriteFontCollection_GetFontFamily(syscollection, i, &family);
5474 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5476 hr = IDWriteFontFamily_GetFirstMatchingFont(family, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STRETCH_NORMAL,
5477 DWRITE_FONT_STYLE_NORMAL, &font);
5478 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5480 hr = IDWriteFont_CreateFontFace(font, &fontface);
5481 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5483 hr = IDWriteFontFamily_GetFamilyNames(family, &names);
5484 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5486 if (!(exists = get_enus_string(names, nameW, ARRAY_SIZE(nameW)))) {
5487 IDWriteLocalFontFileLoader *localloader;
5488 IDWriteFontFileLoader *loader;
5489 IDWriteFontFile *file;
5490 const void *key;
5491 UINT32 keysize;
5492 UINT32 count;
5494 count = 1;
5495 hr = IDWriteFontFace_GetFiles(fontface, &count, &file);
5496 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5498 hr = IDWriteFontFile_GetLoader(file, &loader);
5499 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5501 hr = IDWriteFontFileLoader_QueryInterface(loader, &IID_IDWriteLocalFontFileLoader, (void**)&localloader);
5502 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5503 IDWriteFontFileLoader_Release(loader);
5505 hr = IDWriteFontFile_GetReferenceKey(file, &key, &keysize);
5506 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5508 hr = IDWriteLocalFontFileLoader_GetFilePathFromKey(localloader, key, keysize, nameW, ARRAY_SIZE(nameW));
5509 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5511 skip("Failed to get English family name, font file %s\n", wine_dbgstr_w(nameW));
5513 IDWriteLocalFontFileLoader_Release(localloader);
5514 IDWriteFontFile_Release(file);
5517 IDWriteLocalizedStrings_Release(names);
5518 IDWriteFont_Release(font);
5520 if (!exists)
5521 goto cleanup;
5523 IDWriteFontFace_GetMetrics(fontface, &fontmetrics);
5524 hr = IDWriteFactory_CreateTextFormat(factory, nameW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
5525 DWRITE_FONT_STRETCH_NORMAL, fontmetrics.designUnitsPerEm, L"en-us", &format);
5526 ok(hr == S_OK, "Failed to create text format, hr %#lx.\n", hr);
5528 /* Look for first supported character to avoid triggering fallback path. With fallback it's harder to test
5529 DrawUnderline() metrics, because actual resolved fontface is not passed to it. Grabbing fontface instance
5530 from corresponding DrawGlyphRun() call is not straightforward. */
5531 for (codepoint = ' '; codepoint < 0xffff; ++codepoint)
5533 glyph = 0;
5534 hr = IDWriteFontFace_GetGlyphIndices(fontface, &codepoint, 1, &glyph);
5535 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5536 if (glyph)
5537 break;
5540 if (!glyph)
5542 skip("Couldn't find reasonable test string.\n");
5543 goto cleanup;
5546 str[0] = codepoint;
5547 hr = IDWriteFactory_CreateTextLayout(factory, str, 1, format, 30000.0f, 100.0f, &layout);
5548 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5550 range.startPosition = 0;
5551 range.length = 2;
5552 hr = IDWriteTextLayout_SetUnderline(layout, TRUE, range);
5553 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5555 memset(&ctxt, 0, sizeof(ctxt));
5556 ctxt.format = format;
5557 ctxt.familyW = nameW;
5558 hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0f, 0.0f);
5559 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5561 cleanup:
5562 if (layout)
5563 IDWriteTextLayout_Release(layout);
5564 if (format)
5565 IDWriteTextFormat_Release(format);
5566 IDWriteFontFace_Release(fontface);
5567 IDWriteFontFamily_Release(family);
5569 IDWriteFontCollection_Release(syscollection);
5571 IDWriteFactory_Release(factory);
5574 static void test_InvalidateLayout(void)
5576 IDWriteTextLayout3 *layout3;
5577 IDWriteTextLayout *layout;
5578 IDWriteTextFormat *format;
5579 IDWriteFactory *factory;
5580 HRESULT hr;
5582 factory = create_factory();
5584 hr = IDWriteFactory_CreateTextFormat(factory, L"Tahoma", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
5585 DWRITE_FONT_STRETCH_NORMAL, 10.0f, L"en-us", &format);
5586 ok(hr == S_OK, "Failed to create text format, hr %#lx.\n", hr);
5588 hr = IDWriteFactory_CreateTextLayout(factory, L"a", 1, format, 1000.0f, 1000.0f, &layout);
5589 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
5591 hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextLayout3, (void**)&layout3);
5592 if (hr == S_OK) {
5593 IDWriteTextFormat1 *format1;
5594 IDWriteTextFormat2 *format2;
5596 hr = IDWriteTextFormat_QueryInterface(format, &IID_IDWriteTextFormat2, (void**)&format2);
5597 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5598 IDWriteTextFormat2_Release(format2);
5600 hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextFormat2, (void**)&format2);
5601 ok(hr == S_OK || broken(hr == E_NOINTERFACE), "Unexpected hr %#lx.\n", hr);
5602 if (hr == S_OK) {
5603 ok(format != (IDWriteTextFormat *)format2, "Unexpected interface pointer.\n");
5604 IDWriteTextFormat2_Release(format2);
5607 hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextFormat1, (void**)&format1);
5608 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5610 hr = IDWriteTextFormat1_QueryInterface(format1, &IID_IDWriteTextFormat2, (void**)&format2);
5611 ok(hr == S_OK || broken(hr == E_NOINTERFACE), "Unexpected hr %#lx.\n", hr);
5612 if (hr == S_OK)
5613 IDWriteTextFormat2_Release(format2);
5614 IDWriteTextFormat1_Release(format1);
5616 hr = IDWriteTextLayout3_QueryInterface(layout3, &IID_IDWriteTextFormat2, (void**)&format2);
5617 ok(hr == S_OK || broken(hr == E_NOINTERFACE), "Unexpected hr %#lx.\n", hr);
5618 if (hr == S_OK)
5619 IDWriteTextFormat2_Release(format2);
5621 hr = IDWriteTextLayout3_InvalidateLayout(layout3);
5622 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5623 IDWriteTextLayout3_Release(layout3);
5625 else
5626 win_skip("IDWriteTextLayout3::InvalidateLayout() is not supported.\n");
5628 IDWriteTextLayout_Release(layout);
5629 IDWriteTextFormat_Release(format);
5630 IDWriteFactory_Release(factory);
5633 static void test_line_spacing(void)
5635 IDWriteTextFormat2 *format2;
5636 IDWriteTextLayout *layout;
5637 IDWriteTextFormat *format;
5638 IDWriteFactory *factory;
5639 HRESULT hr;
5641 factory = create_factory();
5643 hr = IDWriteFactory_CreateTextFormat(factory, L"Tahoma", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
5644 DWRITE_FONT_STRETCH_NORMAL, 10.0f, L"en-us", &format);
5645 ok(hr == S_OK, "Failed to create text format, hr %#lx.\n", hr);
5647 hr = IDWriteTextFormat_SetLineSpacing(format, DWRITE_LINE_SPACING_METHOD_DEFAULT, 0.0f, 0.0f);
5648 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5650 hr = IDWriteTextFormat_SetLineSpacing(format, DWRITE_LINE_SPACING_METHOD_DEFAULT, 0.0f, -10.0f);
5651 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5653 hr = IDWriteTextFormat_SetLineSpacing(format, DWRITE_LINE_SPACING_METHOD_DEFAULT, -10.0f, 0.0f);
5654 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
5656 hr = IDWriteTextFormat_SetLineSpacing(format, DWRITE_LINE_SPACING_METHOD_PROPORTIONAL+1, 0.0f, 0.0f);
5657 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
5659 hr = IDWriteFactory_CreateTextLayout(factory, L"a", 1, format, 1000.0f, 1000.0f, &layout);
5660 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
5662 hr = IDWriteTextLayout_SetLineSpacing(layout, DWRITE_LINE_SPACING_METHOD_DEFAULT, 0.0f, 0.0f);
5663 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5665 hr = IDWriteTextLayout_SetLineSpacing(layout, DWRITE_LINE_SPACING_METHOD_DEFAULT, 0.0f, -10.0f);
5666 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5668 hr = IDWriteTextLayout_SetLineSpacing(layout, DWRITE_LINE_SPACING_METHOD_DEFAULT, -10.0f, 0.0f);
5669 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
5671 hr = IDWriteTextLayout_SetLineSpacing(layout, DWRITE_LINE_SPACING_METHOD_PROPORTIONAL+1, 0.0f, 0.0f);
5672 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
5674 if (IDWriteTextFormat_QueryInterface(format, &IID_IDWriteTextFormat2, (void**)&format2) == S_OK) {
5675 DWRITE_LINE_SPACING spacing;
5677 hr = IDWriteTextFormat2_GetLineSpacing(format2, &spacing);
5678 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5680 ok(spacing.method == DWRITE_LINE_SPACING_METHOD_DEFAULT, "got method %d\n", spacing.method);
5681 ok(spacing.height == 0.0f, "got %f\n", spacing.height);
5682 ok(spacing.baseline == -10.0f, "got %f\n", spacing.baseline);
5683 ok(spacing.leadingBefore == 0.0f, "got %f\n", spacing.leadingBefore);
5684 ok(spacing.fontLineGapUsage == DWRITE_FONT_LINE_GAP_USAGE_DEFAULT, "got %f\n", spacing.leadingBefore);
5686 spacing.leadingBefore = -1.0f;
5688 hr = IDWriteTextFormat2_SetLineSpacing(format2, &spacing);
5689 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
5691 spacing.leadingBefore = 1.1f;
5693 hr = IDWriteTextFormat2_SetLineSpacing(format2, &spacing);
5694 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
5696 spacing.leadingBefore = 1.0f;
5698 hr = IDWriteTextFormat2_SetLineSpacing(format2, &spacing);
5699 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5701 spacing.method = DWRITE_LINE_SPACING_METHOD_PROPORTIONAL + 1;
5702 hr = IDWriteTextFormat2_SetLineSpacing(format2, &spacing);
5703 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
5705 spacing.method = DWRITE_LINE_SPACING_METHOD_PROPORTIONAL;
5706 spacing.fontLineGapUsage = DWRITE_FONT_LINE_GAP_USAGE_ENABLED + 1;
5707 hr = IDWriteTextFormat2_SetLineSpacing(format2, &spacing);
5708 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5710 hr = IDWriteTextFormat2_GetLineSpacing(format2, &spacing);
5711 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5712 ok(spacing.fontLineGapUsage == DWRITE_FONT_LINE_GAP_USAGE_ENABLED + 1, "got %d\n", spacing.fontLineGapUsage);
5714 IDWriteTextFormat2_Release(format2);
5717 IDWriteTextLayout_Release(layout);
5718 IDWriteTextFormat_Release(format);
5719 IDWriteFactory_Release(factory);
5722 static void test_GetOverhangMetrics(void)
5724 static const struct overhangs_test
5726 FLOAT uniform_baseline;
5727 DWRITE_INLINE_OBJECT_METRICS metrics;
5728 DWRITE_OVERHANG_METRICS overhang_metrics;
5729 DWRITE_OVERHANG_METRICS expected;
5730 } overhangs_tests[] = {
5731 { 16.0f, { 10.0f, 50.0f, 20.0f }, { 1.0f, 2.0f, 3.0f, 4.0f }, { 1.0f, 6.0f, 3.0f, 0.0f } },
5732 { 15.0f, { 10.0f, 50.0f, 20.0f }, { 1.0f, 2.0f, 3.0f, 4.0f }, { 1.0f, 7.0f, 3.0f, -1.0f } },
5733 { 16.0f, { 10.0f, 50.0f, 20.0f }, { -1.0f, 0.0f, -3.0f, 4.0f }, { -1.0f, 4.0f, -3.0f, 0.0f } },
5734 { 15.0f, { 10.0f, 50.0f, 20.0f }, { -1.0f, 10.0f, 3.0f, -4.0f }, { -1.0f, 15.0f, 3.0f, -9.0f } },
5736 IDWriteFactory *factory;
5737 IDWriteTextFormat *format;
5738 IDWriteTextLayout *layout;
5739 HRESULT hr;
5740 UINT32 i;
5742 factory = create_factory();
5744 hr = IDWriteFactory_CreateTextFormat(factory, L"Tahoma", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
5745 DWRITE_FONT_STRETCH_NORMAL, 100.0f, L"en-us", &format);
5746 ok(hr == S_OK, "Failed to create text format, hr %#lx.\n", hr);
5748 hr = IDWriteFactory_CreateTextLayout(factory, L"A", 1, format, 1000.0f, 1000.0f, &layout);
5749 ok(hr == S_OK, "Failed to create text layout, hr %lx.\n", hr);
5751 for (i = 0; i < ARRAY_SIZE(overhangs_tests); i++) {
5752 const struct overhangs_test *test = &overhangs_tests[i];
5753 DWRITE_OVERHANG_METRICS overhang_metrics;
5754 DWRITE_TEXT_RANGE range = { 0, 1 };
5755 DWRITE_TEXT_METRICS metrics;
5756 struct test_inline_obj obj;
5758 test_inline_obj_init(&obj, &test->metrics, &test->overhang_metrics);
5760 hr = IDWriteTextLayout_SetLineSpacing(layout, DWRITE_LINE_SPACING_METHOD_UNIFORM, test->metrics.height * 2.0f,
5761 test->uniform_baseline);
5762 ok(hr == S_OK, "Failed to set line spacing, hr %#lx.\n", hr);
5764 hr = IDWriteTextLayout_SetInlineObject(layout, NULL, range);
5765 ok(hr == S_OK, "Failed to reset inline object, hr %#lx.\n", hr);
5767 hr = IDWriteTextLayout_SetInlineObject(layout, &obj.IDWriteInlineObject_iface, range);
5768 ok(hr == S_OK, "Failed to set inline object, hr %#lx.\n", hr);
5770 hr = IDWriteTextLayout_GetMetrics(layout, &metrics);
5771 ok(hr == S_OK, "Failed to get layout metrics, hr %#lx.\n", hr);
5773 ok(metrics.width == test->metrics.width, "%u: unexpected formatted width.\n", i);
5774 ok(metrics.height == test->metrics.height * 2.0f, "%u: unexpected formatted height.\n", i);
5776 hr = IDWriteTextLayout_SetMaxWidth(layout, metrics.width);
5777 hr = IDWriteTextLayout_SetMaxHeight(layout, test->metrics.height);
5779 hr = IDWriteTextLayout_GetOverhangMetrics(layout, &overhang_metrics);
5780 ok(hr == S_OK, "Failed to get overhang metrics, hr %#lx.\n", hr);
5782 ok(!memcmp(&overhang_metrics, &test->expected, sizeof(overhang_metrics)),
5783 "%u: unexpected overhang metrics (%f, %f, %f, %f).\n", i, overhang_metrics.left, overhang_metrics.top,
5784 overhang_metrics.right, overhang_metrics.bottom);
5787 IDWriteTextLayout_Release(layout);
5788 IDWriteTextFormat_Release(format);
5789 IDWriteFactory_Release(factory);
5792 static void test_tab_stops(void)
5794 DWRITE_CLUSTER_METRICS clusters[4];
5795 IDWriteTextLayout *layout;
5796 IDWriteTextFormat *format;
5797 IDWriteFactory *factory;
5798 DWRITE_TEXT_RANGE range;
5799 FLOAT tabstop, size;
5800 UINT count;
5801 HRESULT hr;
5803 factory = create_factory();
5805 /* Default tab stop value. */
5806 for (size = 1.0f; size < 25.0f; size += 5.0f)
5808 hr = IDWriteFactory_CreateTextFormat(factory, L"Tahoma", NULL, DWRITE_FONT_WEIGHT_NORMAL,
5809 DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, size, L"en-us", &format);
5810 ok(hr == S_OK, "Failed to create text format, hr %#lx.\n", hr);
5812 tabstop = IDWriteTextFormat_GetIncrementalTabStop(format);
5813 ok(tabstop == 4.0f * size, "Unexpected tab stop %f.\n", tabstop);
5815 IDWriteTextFormat_Release(format);
5818 hr = IDWriteFactory_CreateTextFormat(factory, L"Tahoma", NULL, DWRITE_FONT_WEIGHT_NORMAL,
5819 DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, 10.0f, L"en-us", &format);
5820 ok(hr == S_OK, "Failed to create text format, hr %#lx.\n", hr);
5822 hr = IDWriteTextFormat_SetIncrementalTabStop(format, 0.0f);
5823 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
5825 hr = IDWriteTextFormat_SetIncrementalTabStop(format, -10.0f);
5826 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
5828 tabstop = IDWriteTextFormat_GetIncrementalTabStop(format);
5829 ok(tabstop == 40.0f, "Unexpected tab stop %f.\n", tabstop);
5831 hr = IDWriteTextFormat_SetIncrementalTabStop(format, 100.0f);
5832 ok(hr == S_OK, "Failed to set tab stop value, hr %#lx.\n", hr);
5834 tabstop = IDWriteTextFormat_GetIncrementalTabStop(format);
5835 ok(tabstop == 100.0f, "Unexpected tab stop %f.\n", tabstop);
5837 hr = IDWriteFactory_CreateTextLayout(factory, L"\ta\tb", 4, format, 1000.0f, 1000.0f, &layout);
5838 ok(hr == S_OK, "Failed to create text layout, hr %lx.\n", hr);
5840 hr = IDWriteTextLayout_GetClusterMetrics(layout, clusters, 4, &count);
5841 ok(hr == S_OK, "Failed to get cluster metrics, hr %#lx.\n", hr);
5842 ok(clusters[0].isWhitespace, "Unexpected isWhitespace.\n");
5843 ok(!clusters[1].isWhitespace, "Unexpected isWhitespace.\n");
5844 ok(clusters[2].isWhitespace, "Unexpected isWhitespace.\n");
5845 ok(!clusters[3].isWhitespace, "Unexpected isWhitespace.\n");
5846 todo_wine {
5847 ok(clusters[0].width == tabstop, "Unexpected tab width.\n");
5848 ok(clusters[1].width + clusters[2].width == tabstop, "Unexpected tab width.\n");
5850 range.startPosition = 0;
5851 range.length = ~0u;
5852 hr = IDWriteTextLayout_SetFontSize(layout, 20.0f, range);
5853 ok(hr == S_OK, "Failed to set font size, hr %#lx.\n", hr);
5855 tabstop = IDWriteTextLayout_GetIncrementalTabStop(layout);
5856 ok(tabstop == 100.0f, "Unexpected tab stop %f.\n", tabstop);
5858 hr = IDWriteTextLayout_GetClusterMetrics(layout, clusters, 4, &count);
5859 ok(hr == S_OK, "Failed to get cluster metrics, hr %#lx.\n", hr);
5860 ok(clusters[0].isWhitespace, "Unexpected isWhitespace.\n");
5861 ok(!clusters[1].isWhitespace, "Unexpected isWhitespace.\n");
5862 ok(clusters[2].isWhitespace, "Unexpected isWhitespace.\n");
5863 ok(!clusters[3].isWhitespace, "Unexpected isWhitespace.\n");
5864 todo_wine {
5865 ok(clusters[0].width == tabstop, "Unexpected tab width.\n");
5866 ok(clusters[1].width + clusters[2].width == tabstop, "Unexpected tab width.\n");
5868 IDWriteTextLayout_Release(layout);
5870 IDWriteTextFormat_Release(format);
5872 IDWriteFactory_Release(factory);
5875 static void test_automatic_font_axes(void)
5877 DWRITE_AUTOMATIC_FONT_AXES axes;
5878 IDWriteTextLayout4 *layout4 = NULL;
5879 IDWriteTextFormat3 *format3;
5880 IDWriteTextLayout *layout;
5881 IDWriteTextFormat *format;
5882 IDWriteFactory *factory;
5883 HRESULT hr;
5885 factory = create_factory();
5887 hr = IDWriteFactory_CreateTextFormat(factory, L"Tahoma", NULL, DWRITE_FONT_WEIGHT_NORMAL,
5888 DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, 16.0f, L"en-us", &format);
5889 ok(hr == S_OK, "Failed to create text format, hr %#lx.\n", hr);
5891 hr = IDWriteFactory_CreateTextLayout(factory, L"a", 1, format, 1000.0f, 1000.0f, &layout);
5892 ok(hr == S_OK, "Failed to create text layout, hr %lx.\n", hr);
5894 IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextLayout4, (void **)&layout4);
5896 IDWriteTextLayout_Release(layout);
5898 if (!layout4)
5900 win_skip("Text layout does not support variable fonts.\n");
5901 IDWriteFactory_Release(factory);
5902 IDWriteTextFormat_Release(format);
5903 return;
5906 hr = IDWriteTextFormat_QueryInterface(format, &IID_IDWriteTextFormat3, (void **)&format3);
5907 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5909 axes = IDWriteTextFormat3_GetAutomaticFontAxes(format3);
5910 ok(axes == DWRITE_AUTOMATIC_FONT_AXES_NONE, "Unexpected automatic axes %u.\n", axes);
5912 hr = IDWriteTextFormat3_SetAutomaticFontAxes(format3, DWRITE_AUTOMATIC_FONT_AXES_OPTICAL_SIZE);
5913 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5915 hr = IDWriteTextFormat3_SetAutomaticFontAxes(format3, DWRITE_AUTOMATIC_FONT_AXES_OPTICAL_SIZE + 1);
5916 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5918 IDWriteTextFormat3_Release(format3);
5920 axes = IDWriteTextLayout4_GetAutomaticFontAxes(layout4);
5921 ok(axes == DWRITE_AUTOMATIC_FONT_AXES_NONE, "Unexpected automatic axes %u.\n", axes);
5923 hr = IDWriteTextLayout4_SetAutomaticFontAxes(layout4, DWRITE_AUTOMATIC_FONT_AXES_OPTICAL_SIZE + 1);
5924 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
5926 hr = IDWriteTextLayout4_SetAutomaticFontAxes(layout4, DWRITE_AUTOMATIC_FONT_AXES_OPTICAL_SIZE);
5927 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5929 IDWriteTextLayout4_Release(layout4);
5931 /* Out of range values allow for formats, but not for layouts. */
5932 hr = IDWriteFactory_CreateTextLayout(factory, L"a", 1, format, 1000.0f, 1000.0f, &layout);
5933 ok(hr == S_OK, "Failed to create text layout, hr %lx.\n", hr);
5935 hr = IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextLayout4, (void **)&layout4);
5936 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5938 axes = IDWriteTextLayout4_GetAutomaticFontAxes(layout4);
5939 ok(axes == DWRITE_AUTOMATIC_FONT_AXES_OPTICAL_SIZE + 1, "Unexpected automatic axes %u.\n", axes);
5941 hr = IDWriteTextLayout4_QueryInterface(layout4, &IID_IDWriteTextFormat3, (void **)&format3);
5942 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5944 axes = IDWriteTextFormat3_GetAutomaticFontAxes(format3);
5945 ok(axes == DWRITE_AUTOMATIC_FONT_AXES_OPTICAL_SIZE + 1, "Unexpected automatic axes %u.\n", axes);
5947 hr = IDWriteTextLayout4_SetAutomaticFontAxes(layout4, DWRITE_AUTOMATIC_FONT_AXES_OPTICAL_SIZE);
5948 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5950 axes = IDWriteTextFormat3_GetAutomaticFontAxes(format3);
5951 ok(axes == DWRITE_AUTOMATIC_FONT_AXES_OPTICAL_SIZE, "Unexpected automatic axes %u.\n", axes);
5953 hr = IDWriteTextFormat3_SetAutomaticFontAxes(format3, DWRITE_AUTOMATIC_FONT_AXES_OPTICAL_SIZE + 1);
5954 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
5956 IDWriteTextFormat3_Release(format3);
5958 IDWriteTextLayout_Release(layout);
5960 IDWriteTextLayout4_Release(layout4);
5961 IDWriteTextFormat_Release(format);
5962 IDWriteFactory_Release(factory);
5965 static void test_text_format_axes(void)
5967 IDWriteFontCollection *collection;
5968 IDWriteFontCollection2 *collection2;
5969 DWRITE_FONT_AXIS_VALUE axes[2];
5970 IDWriteTextFormat3 *format3;
5971 DWRITE_FONT_STRETCH stretch;
5972 DWRITE_FONT_WEIGHT weight;
5973 IDWriteTextFormat *format;
5974 IDWriteFactory6 *factory;
5975 DWRITE_FONT_STYLE style;
5976 DWRITE_FONT_FAMILY_MODEL model;
5977 unsigned int count;
5978 HRESULT hr;
5980 factory = create_factory_iid(&IID_IDWriteFactory6);
5982 if (!factory)
5984 win_skip("Text format does not support variations.\n");
5985 return;
5988 hr = IDWriteFactory6_CreateTextFormat(factory, L"test_family", NULL, NULL, 0, 10.0f, L"en-us", &format3);
5989 todo_wine
5990 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5992 if (SUCCEEDED(hr))
5994 hr = IDWriteTextFormat3_GetFontCollection(format3, &collection);
5995 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
5997 hr = IDWriteFontCollection_QueryInterface(collection, &IID_IDWriteFontCollection2, (void **)&collection2);
5998 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6000 model = IDWriteFontCollection2_GetFontFamilyModel(collection2);
6001 ok(model == DWRITE_FONT_FAMILY_MODEL_TYPOGRAPHIC, "Unexpected model %d.\n", model);
6003 IDWriteFontCollection_Release(collection);
6004 IDWriteFontCollection2_Release(collection2);
6006 count = IDWriteTextFormat3_GetFontAxisValueCount(format3);
6007 ok(!count, "Unexpected axis count %u.\n", count);
6009 stretch = IDWriteTextFormat3_GetFontStretch(format3);
6010 ok(stretch == DWRITE_FONT_STRETCH_NORMAL, "Unexpected font stretch %d.\n", stretch);
6012 style = IDWriteTextFormat3_GetFontStyle(format3);
6013 ok(style == DWRITE_FONT_STYLE_NORMAL, "Unexpected font style %d.\n", style);
6015 weight = IDWriteTextFormat3_GetFontWeight(format3);
6016 ok(weight == DWRITE_FONT_WEIGHT_NORMAL, "Unexpected font weight %d.\n", weight);
6018 /* Regular properties are not set from axis values. */
6019 axes[0].axisTag = DWRITE_FONT_AXIS_TAG_WEIGHT;
6020 axes[0].value = 200.0f;
6021 hr = IDWriteTextFormat3_SetFontAxisValues(format3, axes, 1);
6022 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6024 weight = IDWriteTextFormat3_GetFontWeight(format3);
6025 ok(weight == DWRITE_FONT_WEIGHT_NORMAL, "Unexpected font weight %d.\n", weight);
6027 IDWriteTextFormat3_Release(format3);
6029 hr = IDWriteFactory_CreateTextFormat((IDWriteFactory *)factory, L"test_family", NULL,
6030 DWRITE_FONT_WEIGHT_BOLD, DWRITE_FONT_STYLE_ITALIC, DWRITE_FONT_STRETCH_EXPANDED,
6031 10.0f, L"en-us", &format);
6032 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6034 hr = IDWriteTextFormat_QueryInterface(format, &IID_IDWriteTextFormat3, (void **)&format3);
6035 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6037 count = IDWriteTextFormat3_GetFontAxisValueCount(format3);
6038 ok(!count, "Unexpected axis count %u.\n", count);
6040 axes[0].axisTag = DWRITE_FONT_AXIS_TAG_WEIGHT;
6041 hr = IDWriteTextFormat3_GetFontAxisValues(format3, axes, 1);
6042 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6043 ok(axes[0].axisTag == 0 && axes[0].value == 0.0f, "Unexpected value.\n");
6045 axes[0].axisTag = DWRITE_FONT_AXIS_TAG_WEIGHT;
6046 axes[0].value = 200.0f;
6047 axes[1].axisTag = DWRITE_FONT_AXIS_TAG_WIDTH;
6048 axes[1].value = 2.0f;
6049 hr = IDWriteTextFormat3_SetFontAxisValues(format3, axes, 2);
6050 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6052 count = IDWriteTextFormat3_GetFontAxisValueCount(format3);
6053 ok(count == 2, "Unexpected axis count %u.\n", count);
6055 hr = IDWriteTextFormat3_GetFontAxisValues(format3, axes, 1);
6056 ok(hr == E_NOT_SUFFICIENT_BUFFER, "Unexpected hr %#lx.\n", hr);
6058 hr = IDWriteTextFormat3_GetFontAxisValues(format3, axes, 0);
6059 ok(hr == E_NOT_SUFFICIENT_BUFFER, "Unexpected hr %#lx.\n", hr);
6061 hr = IDWriteTextFormat3_GetFontAxisValues(format3, axes, 2);
6062 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6064 hr = IDWriteTextFormat3_SetFontAxisValues(format3, axes, 0);
6065 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6067 count = IDWriteTextFormat3_GetFontAxisValueCount(format3);
6068 ok(!count, "Unexpected axis count %u.\n", count);
6070 IDWriteTextFormat3_Release(format3);
6071 IDWriteTextFormat_Release(format);
6073 IDWriteFactory6_Release(factory);
6076 static void test_layout_range_length(void)
6078 IDWriteFontCollection *collection, *collection2;
6079 IDWriteInlineObject *sign, *object;
6080 IDWriteTypography *typography;
6081 DWRITE_FONT_STRETCH stretch;
6082 IDWriteTextLayout1 *layout1;
6083 IDWriteTextFormat *format;
6084 IDWriteTextLayout *layout;
6085 DWRITE_FONT_WEIGHT weight;
6086 DWRITE_FONT_STYLE style;
6087 DWRITE_TEXT_RANGE range;
6088 IDWriteFactory *factory;
6089 HRESULT hr;
6090 BOOL value;
6092 factory = create_factory();
6094 hr = IDWriteFactory_CreateTextFormat(factory, L"Tahoma", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
6095 DWRITE_FONT_STRETCH_NORMAL, 10.0f, L"ru", &format);
6096 ok(hr == S_OK, "Failed to create text format, hr %#lx.\n", hr);
6098 /* Range length is validated when setting properties. */
6100 hr = IDWriteFactory_CreateTextLayout(factory, L"string", 6, format, 100.0f, 100.0f, &layout);
6101 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
6103 /* Weight */
6104 range.startPosition = 10;
6105 range.length = ~0u;
6106 hr = IDWriteTextLayout_SetFontWeight(layout, DWRITE_FONT_WEIGHT_NORMAL, range);
6107 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
6109 range.startPosition = 10;
6110 range.length = ~0u - 9;
6111 hr = IDWriteTextLayout_SetFontWeight(layout, DWRITE_FONT_WEIGHT_NORMAL, range);
6112 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
6114 range.startPosition = 10;
6115 range.length = ~0u - 10;
6116 hr = IDWriteTextLayout_SetFontWeight(layout, DWRITE_FONT_WEIGHT_HEAVY, range);
6117 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6119 range.startPosition = range.length = 0;
6120 hr = IDWriteTextLayout_GetFontWeight(layout, 0, &weight, &range);
6121 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6122 ok(range.startPosition == 0 && range.length == 10, "Unexpected range (%u, %u).\n", range.startPosition, range.length);
6124 range.startPosition = range.length = 0;
6125 hr = IDWriteTextLayout_GetFontWeight(layout, 10, &weight, &range);
6126 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6127 ok(range.startPosition == 10 && range.length == ~0u - 10, "Unexpected range (%u, %u).\n",
6128 range.startPosition, range.length);
6130 range.startPosition = 0;
6131 range.length = ~0u;
6132 hr = IDWriteTextLayout_SetFontWeight(layout, DWRITE_FONT_WEIGHT_NORMAL, range);
6133 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6135 /* Family name */
6136 range.startPosition = 10;
6137 range.length = ~0u;
6138 hr = IDWriteTextLayout_SetFontFamilyName(layout, L"family", range);
6139 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
6141 range.startPosition = 10;
6142 range.length = ~0u - 9;
6143 hr = IDWriteTextLayout_SetFontFamilyName(layout, L"family", range);
6144 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
6146 range.startPosition = 10;
6147 range.length = ~0u - 10;
6148 hr = IDWriteTextLayout_SetFontFamilyName(layout, L"family", range);
6149 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6151 range.startPosition = 0;
6152 range.length = ~0u;
6153 hr = IDWriteTextLayout_SetFontFamilyName(layout, L"Tahoma", range);
6154 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6156 /* Style */
6157 range.startPosition = 10;
6158 range.length = ~0u;
6159 hr = IDWriteTextLayout_SetFontStyle(layout, DWRITE_FONT_STYLE_ITALIC, range);
6160 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
6162 range.startPosition = 10;
6163 range.length = ~0u - 9;
6164 hr = IDWriteTextLayout_SetFontStyle(layout, DWRITE_FONT_STYLE_ITALIC, range);
6165 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
6167 range.startPosition = 10;
6168 range.length = ~0u - 10;
6169 hr = IDWriteTextLayout_SetFontStyle(layout, DWRITE_FONT_STYLE_ITALIC, range);
6170 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6172 range.startPosition = range.length = 0;
6173 hr = IDWriteTextLayout_GetFontStyle(layout, 0, &style, &range);
6174 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6175 ok(range.startPosition == 0 && range.length == 10, "Unexpected range (%u, %u).\n", range.startPosition, range.length);
6177 range.startPosition = range.length = 0;
6178 hr = IDWriteTextLayout_GetFontStyle(layout, 10, &style, &range);
6179 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6180 ok(range.startPosition == 10 && range.length == ~0u - 10, "Unexpected range (%u, %u).\n",
6181 range.startPosition, range.length);
6183 range.startPosition = 0;
6184 range.length = ~0u;
6185 hr = IDWriteTextLayout_SetFontStyle(layout, DWRITE_FONT_STYLE_NORMAL, range);
6186 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6188 /* Stretch */
6189 range.startPosition = 10;
6190 range.length = ~0u;
6191 hr = IDWriteTextLayout_SetFontStretch(layout, DWRITE_FONT_STRETCH_CONDENSED, range);
6192 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
6194 range.startPosition = 10;
6195 range.length = ~0u - 9;
6196 hr = IDWriteTextLayout_SetFontStretch(layout, DWRITE_FONT_STRETCH_CONDENSED, range);
6197 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
6199 range.startPosition = 10;
6200 range.length = ~0u - 10;
6201 hr = IDWriteTextLayout_SetFontStretch(layout, DWRITE_FONT_STRETCH_CONDENSED, range);
6202 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6204 range.startPosition = range.length = 0;
6205 hr = IDWriteTextLayout_GetFontStretch(layout, 0, &stretch, &range);
6206 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6207 ok(range.startPosition == 0 && range.length == 10, "Unexpected range (%u, %u).\n", range.startPosition, range.length);
6209 range.startPosition = range.length = 0;
6210 hr = IDWriteTextLayout_GetFontStretch(layout, 10, &stretch, &range);
6211 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6212 ok(range.startPosition == 10 && range.length == ~0u - 10, "Unexpected range (%u, %u).\n",
6213 range.startPosition, range.length);
6215 range.startPosition = 0;
6216 range.length = ~0u;
6217 hr = IDWriteTextLayout_SetFontStretch(layout, DWRITE_FONT_STRETCH_NORMAL, range);
6218 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6220 /* Underline */
6221 range.startPosition = 10;
6222 range.length = ~0u;
6223 hr = IDWriteTextLayout_SetUnderline(layout, TRUE, range);
6224 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
6226 range.startPosition = 10;
6227 range.length = ~0u - 9;
6228 hr = IDWriteTextLayout_SetUnderline(layout, TRUE, range);
6229 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
6231 range.startPosition = 10;
6232 range.length = ~0u - 10;
6233 hr = IDWriteTextLayout_SetUnderline(layout, TRUE, range);
6234 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6236 range.startPosition = range.length = 0;
6237 hr = IDWriteTextLayout_GetUnderline(layout, 0, &value, &range);
6238 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6239 ok(range.startPosition == 0 && range.length == 10, "Unexpected range (%u, %u).\n", range.startPosition, range.length);
6241 range.startPosition = range.length = 0;
6242 hr = IDWriteTextLayout_GetUnderline(layout, 10, &value, &range);
6243 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6244 ok(range.startPosition == 10 && range.length == ~0u - 10, "Unexpected range (%u, %u).\n",
6245 range.startPosition, range.length);
6247 range.startPosition = 0;
6248 range.length = ~0u;
6249 hr = IDWriteTextLayout_SetUnderline(layout, FALSE, range);
6250 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6252 /* Strikethrough */
6253 range.startPosition = 10;
6254 range.length = ~0u;
6255 hr = IDWriteTextLayout_SetStrikethrough(layout, TRUE, range);
6256 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
6258 range.startPosition = 10;
6259 range.length = ~0u - 9;
6260 hr = IDWriteTextLayout_SetStrikethrough(layout, TRUE, range);
6261 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
6263 range.startPosition = 10;
6264 range.length = ~0u - 10;
6265 hr = IDWriteTextLayout_SetStrikethrough(layout, TRUE, range);
6266 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6268 range.startPosition = range.length = 0;
6269 hr = IDWriteTextLayout_GetStrikethrough(layout, 0, &value, &range);
6270 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6271 ok(range.startPosition == 0 && range.length == 10, "Unexpected range (%u, %u).\n", range.startPosition, range.length);
6273 range.startPosition = range.length = 0;
6274 hr = IDWriteTextLayout_GetStrikethrough(layout, 10, &value, &range);
6275 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6276 ok(range.startPosition == 10 && range.length == ~0u - 10, "Unexpected range (%u, %u).\n",
6277 range.startPosition, range.length);
6279 range.startPosition = 0;
6280 range.length = ~0u;
6281 hr = IDWriteTextLayout_SetStrikethrough(layout, FALSE, range);
6282 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6284 /* Locale name */
6285 range.startPosition = 10;
6286 range.length = ~0u;
6287 hr = IDWriteTextLayout_SetLocaleName(layout, L"locale", range);
6288 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
6290 range.startPosition = 10;
6291 range.length = ~0u - 9;
6292 hr = IDWriteTextLayout_SetLocaleName(layout, L"locale", range);
6293 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
6295 range.startPosition = 10;
6296 range.length = ~0u - 10;
6297 hr = IDWriteTextLayout_SetLocaleName(layout, L"locale", range);
6298 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6300 range.startPosition = 0;
6301 range.length = ~0u;
6302 hr = IDWriteTextLayout_SetLocaleName(layout, L"ru", range);
6303 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6305 /* Inline object */
6306 hr = IDWriteFactory_CreateEllipsisTrimmingSign(factory, format, &sign);
6307 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6309 range.startPosition = 10;
6310 range.length = ~0u;
6311 hr = IDWriteTextLayout_SetInlineObject(layout, sign, range);
6312 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
6314 range.startPosition = 10;
6315 range.length = ~0u - 9;
6316 hr = IDWriteTextLayout_SetInlineObject(layout, sign, range);
6317 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
6319 range.startPosition = 10;
6320 range.length = ~0u - 10;
6321 hr = IDWriteTextLayout_SetInlineObject(layout, sign, range);
6322 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6324 range.startPosition = range.length = 0;
6325 object = NULL;
6326 hr = IDWriteTextLayout_GetInlineObject(layout, 10, &object, &range);
6327 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6328 ok(range.startPosition == 10 && range.length == ~0u - 10, "Unexpected range (%u, %u).\n",
6329 range.startPosition, range.length);
6330 IDWriteInlineObject_Release(object);
6332 range.startPosition = 0;
6333 range.length = ~0u;
6334 hr = IDWriteTextLayout_SetInlineObject(layout, NULL, range);
6335 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6337 /* Drawing effect */
6338 range.startPosition = 10;
6339 range.length = ~0u;
6340 hr = IDWriteTextLayout_SetDrawingEffect(layout, (IUnknown *)sign, range);
6341 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
6343 range.startPosition = 10;
6344 range.length = ~0u - 9;
6345 hr = IDWriteTextLayout_SetDrawingEffect(layout, (IUnknown *)sign, range);
6346 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
6348 range.startPosition = 10;
6349 range.length = ~0u - 10;
6350 hr = IDWriteTextLayout_SetDrawingEffect(layout, (IUnknown *)sign, range);
6351 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6353 range.startPosition = 0;
6354 range.length = ~0u;
6355 hr = IDWriteTextLayout_SetDrawingEffect(layout, NULL, range);
6356 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6358 IDWriteInlineObject_Release(sign);
6360 /* Typography */
6361 hr = IDWriteFactory_CreateTypography(factory, &typography);
6362 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6364 range.startPosition = 10;
6365 range.length = ~0u;
6366 hr = IDWriteTextLayout_SetTypography(layout, typography, range);
6367 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
6369 range.startPosition = 10;
6370 range.length = ~0u - 9;
6371 hr = IDWriteTextLayout_SetTypography(layout, typography, range);
6372 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
6374 range.startPosition = 10;
6375 range.length = ~0u - 10;
6376 hr = IDWriteTextLayout_SetTypography(layout, typography, range);
6377 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6379 range.startPosition = 0;
6380 range.length = ~0u;
6381 hr = IDWriteTextLayout_SetTypography(layout, NULL, range);
6382 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6384 IDWriteTypography_Release(typography);
6386 /* Font collection */
6387 hr = IDWriteFactory_GetSystemFontCollection(factory, &collection, FALSE);
6388 ok(hr == S_OK, "Failed to get system collection, hr %#lx.\n", hr);
6390 range.startPosition = 10;
6391 range.length = ~0u;
6392 hr = IDWriteTextLayout_SetFontCollection(layout, collection, range);
6393 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
6395 range.startPosition = 10;
6396 range.length = ~0u - 9;
6397 hr = IDWriteTextLayout_SetFontCollection(layout, collection, range);
6398 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
6400 range.startPosition = 10;
6401 range.length = ~0u - 10;
6402 hr = IDWriteTextLayout_SetFontCollection(layout, collection, range);
6403 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6405 range.startPosition = 0;
6406 range.length = ~0u;
6407 hr = IDWriteTextLayout_SetFontCollection(layout, NULL, range);
6408 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6410 range.startPosition = range.length = 0;
6411 collection2 = NULL;
6412 hr = IDWriteTextLayout_GetFontCollection(layout, 10, &collection2, &range);
6413 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6414 ok(range.length == ~0u, "Unexpected range length %u.\n", range.length);
6415 if (collection2)
6416 IDWriteFontCollection_Release(collection2);
6418 IDWriteFontCollection_Release(collection);
6420 if (SUCCEEDED(IDWriteTextLayout_QueryInterface(layout, &IID_IDWriteTextLayout1, (void **)&layout1)))
6422 /* Pair kerning */
6423 range.startPosition = 10;
6424 range.length = ~0u;
6425 hr = IDWriteTextLayout1_SetPairKerning(layout1, TRUE, range);
6426 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
6428 range.startPosition = 10;
6429 range.length = ~0u - 9;
6430 hr = IDWriteTextLayout1_SetPairKerning(layout1, TRUE, range);
6431 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
6433 range.startPosition = 10;
6434 range.length = ~0u - 10;
6435 hr = IDWriteTextLayout1_SetPairKerning(layout1, TRUE, range);
6436 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6438 range.startPosition = range.length = 0;
6439 hr = IDWriteTextLayout1_GetPairKerning(layout1, 0, &value, &range);
6440 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6441 ok(range.startPosition == 0 && range.length == 10, "Unexpected range (%u, %u).\n", range.startPosition, range.length);
6443 range.startPosition = range.length = 0;
6444 value = FALSE;
6445 hr = IDWriteTextLayout1_GetPairKerning(layout1, 10, &value, &range);
6446 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6447 ok(range.startPosition == 10 && range.length == ~0u - 10, "Unexpected range (%u, %u).\n",
6448 range.startPosition, range.length);
6449 ok(!!value, "Unexpected value %d.\n", value);
6451 range.startPosition = 0;
6452 range.length = ~0u;
6453 hr = IDWriteTextLayout1_SetPairKerning(layout1, FALSE, range);
6454 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6456 IDWriteTextLayout1_Release(layout1);
6459 IDWriteTextLayout_Release(layout);
6461 IDWriteTextFormat_Release(format);
6462 IDWriteFactory_Release(factory);
6465 static void test_HitTestTextRange(void)
6467 DWRITE_HIT_TEST_METRICS metrics[10];
6468 IDWriteInlineObject *inlineobj;
6469 DWRITE_LINE_METRICS line;
6470 IDWriteTextFormat *format;
6471 IDWriteTextLayout *layout;
6472 DWRITE_TEXT_RANGE range;
6473 IDWriteFactory *factory;
6474 unsigned int count;
6475 HRESULT hr;
6477 factory = create_factory();
6479 hr = IDWriteFactory_CreateTextFormat(factory, L"Tahoma", NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
6480 DWRITE_FONT_STRETCH_NORMAL, 10.0f, L"ru", &format);
6481 ok(hr == S_OK, "Failed to create text format, hr %#lx.\n", hr);
6483 hr = IDWriteFactory_CreateTextLayout(factory, L"string", 6, format, 100.0f, 100.0f, &layout);
6484 ok(hr == S_OK, "Failed to create text layout, hr %#lx.\n", hr);
6486 /* Start index exceeding layout text length, dummy range returned. */
6487 count = 0;
6488 hr = IDWriteTextLayout_HitTestTextRange(layout, 7, 10, 0.0f, 0.0f, metrics, ARRAY_SIZE(metrics), &count);
6489 todo_wine
6490 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6491 if (SUCCEEDED(hr))
6493 ok(count == 1, "Unexpected metrics count %u.\n", count);
6494 ok(metrics[0].textPosition == 6 && metrics[0].length == 0, "Unexpected metrics range %u, %u.\n",
6495 metrics[0].textPosition, metrics[0].length);
6496 ok(!!metrics[0].isText, "Expected text range.\n");
6498 /* Length exceeding layout text length, trimmed. */
6499 count = 0;
6500 hr = IDWriteTextLayout_HitTestTextRange(layout, 0, 10, 0.0f, 0.0f, metrics, ARRAY_SIZE(metrics), &count);
6501 todo_wine
6502 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6503 if (SUCCEEDED(hr))
6505 ok(count == 1, "Unexpected metrics count %u.\n", count);
6506 ok(metrics[0].textPosition == 0 && metrics[0].length == 6, "Unexpected metrics range %u, %u.\n",
6507 metrics[0].textPosition, metrics[0].length);
6508 ok(!!metrics[0].isText, "Expected text range.\n");
6510 /* Change font size for second half. */
6511 range.startPosition = 3;
6512 range.length = 3;
6513 hr = IDWriteTextLayout_SetFontSize(layout, 20.0f, range);
6514 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6516 count = 0;
6517 hr = IDWriteTextLayout_HitTestTextRange(layout, 0, 6, 0.0f, 0.0f, metrics, ARRAY_SIZE(metrics), &count);
6518 todo_wine
6519 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6520 if (SUCCEEDED(hr))
6522 ok(count == 1, "Unexpected metrics count %u.\n", count);
6523 ok(metrics[0].textPosition == 0 && metrics[0].length == 6, "Unexpected metrics range %u, %u.\n",
6524 metrics[0].textPosition, metrics[0].length);
6525 ok(!!metrics[0].isText, "Expected text range.\n");
6527 hr = IDWriteTextLayout_GetLineMetrics(layout, &line, 1, &count);
6528 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6529 ok(line.height == metrics[0].height, "Unexpected range height.\n");
6531 /* With inline object. */
6532 hr = IDWriteFactory_CreateEllipsisTrimmingSign(factory, format, &inlineobj);
6533 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6535 hr = IDWriteTextLayout_SetInlineObject(layout, inlineobj, range);
6536 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6538 count = 0;
6539 hr = IDWriteTextLayout_HitTestTextRange(layout, 0, 6, 0.0f, 0.0f, metrics, ARRAY_SIZE(metrics), &count);
6540 todo_wine
6541 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6542 if (SUCCEEDED(hr))
6544 ok(count == 2, "Unexpected metrics count %u.\n", count);
6545 ok(metrics[0].textPosition == 0 && metrics[0].length == 3, "Unexpected metrics range %u, %u.\n",
6546 metrics[0].textPosition, metrics[0].length);
6547 ok(!!metrics[0].isText, "Expected text range.\n");
6548 ok(metrics[1].textPosition == 3 && metrics[1].length == 3, "Unexpected metrics range %u, %u.\n",
6549 metrics[1].textPosition, metrics[1].length);
6550 ok(!metrics[1].isText, "Unexpected text range.\n");
6552 count = 0;
6553 hr = IDWriteTextLayout_HitTestTextRange(layout, 7, 10, 0.0f, 0.0f, metrics, ARRAY_SIZE(metrics), &count);
6554 todo_wine
6555 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
6556 if (SUCCEEDED(hr))
6558 ok(count == 1, "Unexpected metrics count %u.\n", count);
6559 ok(metrics[0].textPosition == 6 && metrics[0].length == 0, "Unexpected metrics range %u, %u.\n",
6560 metrics[0].textPosition, metrics[0].length);
6561 ok(!metrics[0].isText, "Unexpected text range.\n");
6563 IDWriteInlineObject_Release(inlineobj);
6564 IDWriteTextLayout_Release(layout);
6565 IDWriteTextFormat_Release(format);
6567 IDWriteFactory_Release(factory);
6570 START_TEST(layout)
6572 IDWriteFactory *factory;
6574 if (!(factory = create_factory())) {
6575 win_skip("failed to create factory\n");
6576 return;
6579 init_call_sequences(sequences, NUM_CALL_SEQUENCES);
6580 init_call_sequences(expected_seq, 1);
6582 test_CreateTextLayout();
6583 test_CreateGdiCompatibleTextLayout();
6584 test_CreateTextFormat();
6585 test_GetLocaleName();
6586 test_CreateEllipsisTrimmingSign();
6587 test_fontweight();
6588 test_SetInlineObject();
6589 test_Draw();
6590 test_typography();
6591 test_GetClusterMetrics();
6592 test_SetLocaleName();
6593 test_SetPairKerning();
6594 test_SetVerticalGlyphOrientation();
6595 test_fallback();
6596 test_DetermineMinWidth();
6597 test_SetFontSize();
6598 test_SetFontFamilyName();
6599 test_SetFontStyle();
6600 test_SetFontStretch();
6601 test_SetStrikethrough();
6602 test_GetMetrics();
6603 test_SetFlowDirection();
6604 test_SetDrawingEffect();
6605 test_GetLineMetrics();
6606 test_SetTextAlignment();
6607 test_SetParagraphAlignment();
6608 test_SetReadingDirection();
6609 test_pixelsnapping();
6610 test_SetWordWrapping();
6611 test_MapCharacters();
6612 test_system_fallback();
6613 test_FontFallbackBuilder();
6614 test_SetTypography();
6615 test_SetLastLineWrapping();
6616 test_SetOpticalAlignment();
6617 test_SetUnderline();
6618 test_InvalidateLayout();
6619 test_line_spacing();
6620 test_GetOverhangMetrics();
6621 test_tab_stops();
6622 test_automatic_font_axes();
6623 test_text_format_axes();
6624 test_layout_range_length();
6625 test_HitTestTextRange();
6627 IDWriteFactory_Release(factory);