wined3d: Pass a wined3d_device_context to wined3d_cs_emit_blt_sub_resource().
[wine/zf.git] / dlls / dwrite / tests / analyzer.c
blob0294bbfb7197ac68193e3b676eee5bc26db275f3
1 /*
2 * Text analyzing tests
4 * Copyright 2012-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 <stdio.h>
25 #include <limits.h>
26 #include <math.h>
28 #include "initguid.h"
29 #include "windows.h"
30 #include "winternl.h"
31 #include "dwrite_3.h"
33 #include "wine/heap.h"
34 #include "wine/test.h"
36 static IDWriteFactory *factory;
38 #define LRE 0x202a
39 #define RLE 0x202b
40 #define PDF 0x202c
41 #define LRO 0x202d
42 #define RLO 0x202e
43 #define LRI 0x2066
44 #define RLI 0x2067
45 #define FSI 0x2068
46 #define PDI 0x2069
48 #define MS_GDEF_TAG DWRITE_MAKE_OPENTYPE_TAG('G','D','E','F')
50 #ifdef WORDS_BIGENDIAN
51 #define GET_BE_WORD(x) (x)
52 #define GET_BE_DWORD(x) (x)
53 #define GET_LE_WORD(x) RtlUshortByteSwap(x)
54 #define GET_LE_DWORD(x) RtlUlongByteSwap(x)
55 #else
56 #define GET_BE_WORD(x) RtlUshortByteSwap(x)
57 #define GET_BE_DWORD(x) RtlUlongByteSwap(x)
58 #define GET_LE_WORD(x) (x)
59 #define GET_LE_DWORD(x) (x)
60 #endif
62 struct ot_gdef_classdef_format1
64 WORD format;
65 WORD start_glyph;
66 WORD glyph_count;
67 WORD classes[1];
70 struct ot_gdef_class_range
72 WORD start_glyph;
73 WORD end_glyph;
74 WORD glyph_class;
77 struct ot_gdef_classdef_format2
79 WORD format;
80 WORD range_count;
81 struct ot_gdef_class_range ranges[1];
84 enum analysis_kind {
85 ScriptAnalysis,
86 LastKind
89 static const char *get_analysis_kind_name(enum analysis_kind kind)
91 switch (kind)
93 case ScriptAnalysis:
94 return "ScriptAnalysis";
95 default:
96 return "unknown";
100 struct script_analysis {
101 UINT32 pos;
102 UINT32 len;
103 DWRITE_SCRIPT_SHAPES shapes;
106 struct call_entry {
107 enum analysis_kind kind;
108 struct script_analysis sa;
111 struct testcontext {
112 enum analysis_kind kind;
113 BOOL todo;
114 int *failcount;
115 const char *file;
116 int line;
119 struct call_sequence
121 int count;
122 int size;
123 struct call_entry *sequence;
126 #define NUM_CALL_SEQUENCES 1
127 #define ANALYZER_ID 0
128 static struct call_sequence *sequences[NUM_CALL_SEQUENCES];
129 static struct call_sequence *expected_seq[1];
131 static void add_call(struct call_sequence **seq, int sequence_index, const struct call_entry *call)
133 struct call_sequence *call_seq = seq[sequence_index];
135 if (!call_seq->sequence)
137 call_seq->size = 10;
138 call_seq->sequence = HeapAlloc(GetProcessHeap(), 0,
139 call_seq->size * sizeof (struct call_entry));
142 if (call_seq->count == call_seq->size)
144 call_seq->size *= 2;
145 call_seq->sequence = HeapReAlloc(GetProcessHeap(), 0,
146 call_seq->sequence,
147 call_seq->size * sizeof (struct call_entry));
150 assert(call_seq->sequence);
152 call_seq->sequence[call_seq->count++] = *call;
155 static inline void flush_sequence(struct call_sequence **seg, int sequence_index)
157 struct call_sequence *call_seq = seg[sequence_index];
159 HeapFree(GetProcessHeap(), 0, call_seq->sequence);
160 call_seq->sequence = NULL;
161 call_seq->count = call_seq->size = 0;
164 static void init_call_sequences(struct call_sequence **seq, int n)
166 int i;
168 for (i = 0; i < n; i++)
169 seq[i] = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct call_sequence));
172 static void test_uint(UINT32 actual, UINT32 expected, const char *name, const struct testcontext *ctxt)
174 if (expected != actual && ctxt->todo)
176 (*ctxt->failcount)++;
177 ok_(ctxt->file, ctxt->line) (0, "%s: \"%s\" expecting %u, got %u\n", get_analysis_kind_name(ctxt->kind), name, expected, actual);
179 else
180 ok_(ctxt->file, ctxt->line) (expected == actual, "%s: \"%s\" expecting %u, got %u\n", get_analysis_kind_name(ctxt->kind), name,
181 expected, actual);
184 static void ok_sequence_(struct call_sequence **seq, int sequence_index,
185 const struct call_entry *expected, const char *context, BOOL todo,
186 const char *file, int line)
188 struct call_sequence *call_seq = seq[sequence_index];
189 static const struct call_entry end_of_sequence = { LastKind };
190 const struct call_entry *actual, *sequence;
191 int failcount = 0;
192 struct testcontext ctxt;
194 add_call(seq, sequence_index, &end_of_sequence);
196 sequence = call_seq->sequence;
197 actual = sequence;
199 ctxt.failcount = &failcount;
200 ctxt.todo = todo;
201 ctxt.file = file;
202 ctxt.line = line;
204 while (expected->kind != LastKind && actual->kind != LastKind)
206 if (expected->kind == actual->kind)
208 ctxt.kind = expected->kind;
210 switch (actual->kind)
212 case ScriptAnalysis:
213 test_uint(actual->sa.pos, expected->sa.pos, "position", &ctxt);
214 test_uint(actual->sa.len, expected->sa.len, "length", &ctxt);
215 test_uint(actual->sa.shapes, expected->sa.shapes, "shapes", &ctxt);
216 break;
217 default:
218 ok(0, "%s: callback not handled, %s\n", context, get_analysis_kind_name(actual->kind));
220 expected++;
221 actual++;
223 else if (todo)
225 failcount++;
226 todo_wine
228 ok_(file, line) (0, "%s: call %s was expected, but got call %s instead\n",
229 context, get_analysis_kind_name(expected->kind), get_analysis_kind_name(actual->kind));
232 flush_sequence(seq, sequence_index);
233 return;
235 else
237 ok_(file, line) (0, "%s: call %s was expected, but got call %s instead\n",
238 context, get_analysis_kind_name(expected->kind), get_analysis_kind_name(actual->kind));
239 expected++;
240 actual++;
244 if (todo)
246 todo_wine
248 if (expected->kind != LastKind || actual->kind != LastKind)
250 failcount++;
251 ok_(file, line) (0, "%s: the call sequence is not complete: expected %s - actual %s\n",
252 context, get_analysis_kind_name(expected->kind), get_analysis_kind_name(actual->kind));
256 else if (expected->kind != LastKind || actual->kind != LastKind)
258 ok_(file, line) (0, "%s: the call sequence is not complete: expected %s - actual %s\n",
259 context, get_analysis_kind_name(expected->kind), get_analysis_kind_name(actual->kind));
262 if (todo && !failcount) /* succeeded yet marked todo */
264 todo_wine
266 ok_(file, line)(1, "%s: marked \"todo_wine\" but succeeds\n", context);
270 flush_sequence(seq, sequence_index);
273 #define ok_sequence(seq, index, exp, contx, todo) \
274 ok_sequence_(seq, index, (exp), (contx), (todo), __FILE__, __LINE__)
276 static HRESULT WINAPI analysissink_QueryInterface(IDWriteTextAnalysisSink *iface, REFIID riid, void **obj)
278 if (IsEqualIID(riid, &IID_IDWriteTextAnalysisSink) || IsEqualIID(riid, &IID_IUnknown))
280 *obj = iface;
281 return S_OK;
284 *obj = NULL;
285 return E_NOINTERFACE;
288 static ULONG WINAPI analysissink_AddRef(IDWriteTextAnalysisSink *iface)
290 return 2;
293 static ULONG WINAPI analysissink_Release(IDWriteTextAnalysisSink *iface)
295 return 1;
298 static HRESULT WINAPI analysissink_SetScriptAnalysis(IDWriteTextAnalysisSink *iface,
299 UINT32 position, UINT32 length, DWRITE_SCRIPT_ANALYSIS const* sa)
301 struct call_entry entry;
302 entry.kind = ScriptAnalysis;
303 entry.sa.pos = position;
304 entry.sa.len = length;
305 entry.sa.shapes = sa->shapes;
306 add_call(sequences, ANALYZER_ID, &entry);
307 return S_OK;
310 static DWRITE_SCRIPT_ANALYSIS g_sa;
311 static HRESULT WINAPI analysissink_SetScriptAnalysis2(IDWriteTextAnalysisSink *iface,
312 UINT32 position, UINT32 length, DWRITE_SCRIPT_ANALYSIS const* sa)
314 g_sa = *sa;
315 return S_OK;
318 #define BREAKPOINT_COUNT 20
319 static DWRITE_LINE_BREAKPOINT g_actual_bp[BREAKPOINT_COUNT];
321 static HRESULT WINAPI analysissink_SetLineBreakpoints(IDWriteTextAnalysisSink *iface,
322 UINT32 position,
323 UINT32 length,
324 DWRITE_LINE_BREAKPOINT const* breakpoints)
326 if (position + length > BREAKPOINT_COUNT) {
327 ok(0, "SetLineBreakpoints: reported pos=%u, len=%u overflows expected length %d\n", position, length, BREAKPOINT_COUNT);
328 return E_FAIL;
330 memcpy(&g_actual_bp[position], breakpoints, length*sizeof(DWRITE_LINE_BREAKPOINT));
331 return S_OK;
334 #define BIDI_LEVELS_COUNT 10
335 static UINT8 g_explicit_levels[BIDI_LEVELS_COUNT];
336 static UINT8 g_resolved_levels[BIDI_LEVELS_COUNT];
337 static HRESULT WINAPI analysissink_SetBidiLevel(IDWriteTextAnalysisSink *iface,
338 UINT32 position,
339 UINT32 length,
340 UINT8 explicitLevel,
341 UINT8 resolvedLevel)
343 if (position + length > BIDI_LEVELS_COUNT) {
344 ok(0, "SetBidiLevel: reported pos=%u, len=%u overflows expected length %d\n", position, length, BIDI_LEVELS_COUNT);
345 return E_FAIL;
347 memset(g_explicit_levels + position, explicitLevel, length);
348 memset(g_resolved_levels + position, resolvedLevel, length);
349 return S_OK;
352 static HRESULT WINAPI analysissink_SetNumberSubstitution(IDWriteTextAnalysisSink *iface,
353 UINT32 position,
354 UINT32 length,
355 IDWriteNumberSubstitution* substitution)
357 ok(0, "unexpected\n");
358 return E_NOTIMPL;
361 static IDWriteTextAnalysisSinkVtbl analysissinkvtbl = {
362 analysissink_QueryInterface,
363 analysissink_AddRef,
364 analysissink_Release,
365 analysissink_SetScriptAnalysis,
366 analysissink_SetLineBreakpoints,
367 analysissink_SetBidiLevel,
368 analysissink_SetNumberSubstitution
371 static IDWriteTextAnalysisSinkVtbl analysissinkvtbl2 = {
372 analysissink_QueryInterface,
373 analysissink_AddRef,
374 analysissink_Release,
375 analysissink_SetScriptAnalysis2,
376 analysissink_SetLineBreakpoints,
377 analysissink_SetBidiLevel,
378 analysissink_SetNumberSubstitution
381 static IDWriteTextAnalysisSink analysissink = { &analysissinkvtbl };
382 static IDWriteTextAnalysisSink analysissink2 = { &analysissinkvtbl2 };
384 static HRESULT WINAPI analysissource_QueryInterface(IDWriteTextAnalysisSource *iface,
385 REFIID riid, void **obj)
387 ok(0, "QueryInterface not expected\n");
388 return E_NOTIMPL;
391 static ULONG WINAPI analysissource_AddRef(IDWriteTextAnalysisSource *iface)
393 ok(0, "AddRef not expected\n");
394 return 2;
397 static ULONG WINAPI analysissource_Release(IDWriteTextAnalysisSource *iface)
399 ok(0, "Release not expected\n");
400 return 1;
403 struct testanalysissource
405 IDWriteTextAnalysisSource IDWriteTextAnalysisSource_iface;
406 const WCHAR *text;
407 UINT32 text_length;
408 DWRITE_READING_DIRECTION direction;
411 static void init_textsource(struct testanalysissource *source, const WCHAR *text,
412 DWRITE_READING_DIRECTION direction)
414 source->text = text;
415 source->text_length = lstrlenW(text);
416 source->direction = direction;
419 static inline struct testanalysissource *impl_from_IDWriteTextAnalysisSource(IDWriteTextAnalysisSource *iface)
421 return CONTAINING_RECORD(iface, struct testanalysissource, IDWriteTextAnalysisSource_iface);
424 static HRESULT WINAPI analysissource_GetTextAtPosition(IDWriteTextAnalysisSource *iface,
425 UINT32 position, WCHAR const** text, UINT32* text_len)
427 struct testanalysissource *source = impl_from_IDWriteTextAnalysisSource(iface);
429 if (position >= source->text_length)
431 *text = NULL;
432 *text_len = 0;
434 else
436 *text = source->text + position;
437 *text_len = source->text_length - position;
440 return S_OK;
443 static HRESULT WINAPI analysissource_GetTextBeforePosition(IDWriteTextAnalysisSource *iface,
444 UINT32 position, WCHAR const** text, UINT32* text_len)
446 ok(0, "unexpected\n");
447 return E_NOTIMPL;
450 static DWRITE_READING_DIRECTION WINAPI analysissource_GetParagraphReadingDirection(
451 IDWriteTextAnalysisSource *iface)
453 struct testanalysissource *source = impl_from_IDWriteTextAnalysisSource(iface);
454 return source->direction;
457 static HRESULT WINAPI analysissource_GetLocaleName(IDWriteTextAnalysisSource *iface,
458 UINT32 position, UINT32* text_len, WCHAR const** locale)
460 *locale = NULL;
461 *text_len = 0;
462 return S_OK;
465 static HRESULT WINAPI analysissource_GetNumberSubstitution(IDWriteTextAnalysisSource *iface,
466 UINT32 position, UINT32* text_len, IDWriteNumberSubstitution **substitution)
468 ok(0, "unexpected\n");
469 return E_NOTIMPL;
472 static IDWriteTextAnalysisSourceVtbl analysissourcevtbl = {
473 analysissource_QueryInterface,
474 analysissource_AddRef,
475 analysissource_Release,
476 analysissource_GetTextAtPosition,
477 analysissource_GetTextBeforePosition,
478 analysissource_GetParagraphReadingDirection,
479 analysissource_GetLocaleName,
480 analysissource_GetNumberSubstitution
483 static struct testanalysissource analysissource = { { &analysissourcevtbl } };
485 static IDWriteFontFace *create_fontface(void)
487 IDWriteGdiInterop *interop;
488 IDWriteFontFace *fontface;
489 IDWriteFont *font;
490 LOGFONTW logfont;
491 HRESULT hr;
493 hr = IDWriteFactory_GetGdiInterop(factory, &interop);
494 ok(hr == S_OK, "got 0x%08x\n", hr);
496 memset(&logfont, 0, sizeof(logfont));
497 logfont.lfHeight = 12;
498 logfont.lfWidth = 12;
499 logfont.lfWeight = FW_NORMAL;
500 logfont.lfItalic = 1;
501 lstrcpyW(logfont.lfFaceName, L"Tahoma");
503 hr = IDWriteGdiInterop_CreateFontFromLOGFONT(interop, &logfont, &font);
504 ok(hr == S_OK, "got 0x%08x\n", hr);
506 hr = IDWriteFont_CreateFontFace(font, &fontface);
507 ok(hr == S_OK, "got 0x%08x\n", hr);
509 IDWriteFont_Release(font);
510 IDWriteGdiInterop_Release(interop);
512 return fontface;
515 static WCHAR *create_testfontfile(const WCHAR *filename)
517 static WCHAR pathW[MAX_PATH];
518 DWORD written;
519 HANDLE file;
520 HRSRC res;
521 void *ptr;
523 GetTempPathW(ARRAY_SIZE(pathW), pathW);
524 lstrcatW(pathW, filename);
526 file = CreateFileW(pathW, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0);
527 ok(file != INVALID_HANDLE_VALUE, "file creation failed, at %s, error %d\n", wine_dbgstr_w(pathW),
528 GetLastError());
530 res = FindResourceA(GetModuleHandleA(NULL), (LPCSTR)MAKEINTRESOURCE(1), (LPCSTR)RT_RCDATA);
531 ok(res != 0, "couldn't find resource\n");
532 ptr = LockResource(LoadResource(GetModuleHandleA(NULL), res));
533 WriteFile(file, ptr, SizeofResource(GetModuleHandleA(NULL), res), &written, NULL);
534 ok(written == SizeofResource(GetModuleHandleA(NULL), res), "couldn't write resource\n");
535 CloseHandle(file);
537 return pathW;
540 #define DELETE_FONTFILE(filename) _delete_testfontfile(filename, __LINE__)
541 static void _delete_testfontfile(const WCHAR *filename, int line)
543 BOOL ret = DeleteFileW(filename);
544 ok_(__FILE__,line)(ret, "failed to delete file %s, error %d\n", wine_dbgstr_w(filename), GetLastError());
547 static IDWriteFontFace *create_testfontface(const WCHAR *filename)
549 IDWriteFontFace *face;
550 IDWriteFontFile *file;
551 HRESULT hr;
553 hr = IDWriteFactory_CreateFontFileReference(factory, filename, NULL, &file);
554 ok(hr == S_OK, "got 0x%08x\n",hr);
556 hr = IDWriteFactory_CreateFontFace(factory, DWRITE_FONT_FACE_TYPE_TRUETYPE, 1, &file, 0,
557 DWRITE_FONT_SIMULATIONS_NONE, &face);
558 ok(hr == S_OK, "got 0x%08x\n", hr);
559 IDWriteFontFile_Release(file);
561 return face;
564 struct sa_test {
565 const WCHAR string[50];
566 int item_count;
567 struct script_analysis sa[10];
570 static struct sa_test sa_tests[] = {
572 /* just 1 char string */
573 {'t',0}, 1,
574 { { 0, 1, DWRITE_SCRIPT_SHAPES_DEFAULT }}
577 {'t','e','s','t',0}, 1,
578 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT }}
581 {' ',' ',' ',' ','!','$','[','^','{','~',0}, 1,
582 { { 0, 10, DWRITE_SCRIPT_SHAPES_DEFAULT }}
585 {' ',' ',' ','1','2',' ',0}, 1,
586 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
589 /* digits only */
590 {'1','2',0}, 1,
591 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }}
594 /* Arabic */
595 {0x064a,0x064f,0x0633,0x0627,0x0648,0x0650,0x064a,0x0661,0}, 1,
596 { { 0, 8, DWRITE_SCRIPT_SHAPES_DEFAULT }}
599 /* Arabic */
600 {0x0627,0x0644,0x0635,0x0651,0x0650,0x062d,0x0629,0x064f,' ',0x062a,0x064e,
601 0x0627,0x062c,0x064c,' ',0x0639,0x064e,0x0644,0x0649,' ',
602 0x0631,0x064f,0x0624,0x0648,0x0633,0x0650,' ',0x0627,0x0644,
603 0x0623,0x0635,0x0650,0x062d,0x0651,0x064e,0x0627,0x0621,0x0650,0x06f0,0x06f5,0}, 1,
604 { { 0, 40, DWRITE_SCRIPT_SHAPES_DEFAULT }}
607 /* Arabic, Latin */
608 {'1','2','3','-','5','2',0x064a,0x064f,0x0633,0x0627,0x0648,0x0650,0x064a,'7','1','.',0}, 1,
609 { { 0, 16, DWRITE_SCRIPT_SHAPES_DEFAULT }}
612 /* Arabic, English */
613 {'A','B','C','-','D','E','F',' ',0x0621,0x0623,0x0624,0}, 2,
614 { { 0, 8, DWRITE_SCRIPT_SHAPES_DEFAULT },
615 { 8, 3, DWRITE_SCRIPT_SHAPES_DEFAULT },
619 /* leading space, Arabic, English */
620 {' ',0x0621,0x0623,0x0624,'A','B','C','-','D','E','F',0}, 2,
621 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT },
622 { 4, 7, DWRITE_SCRIPT_SHAPES_DEFAULT },
626 /* English, Arabic, trailing space */
627 {'A','B','C','-','D','E','F',0x0621,0x0623,0x0624,' ',0}, 2,
628 { { 0, 7, DWRITE_SCRIPT_SHAPES_DEFAULT },
629 { 7, 4, DWRITE_SCRIPT_SHAPES_DEFAULT },
633 /* C1 Controls, Latin-1 Supplement */
634 {0x80,0x90,0x9f,0xa0,0xc0,0xb8,0xbf,0xc0,0xff,0}, 2,
635 { { 0, 3, DWRITE_SCRIPT_SHAPES_NO_VISUAL },
636 { 3, 6, DWRITE_SCRIPT_SHAPES_DEFAULT },
640 /* Latin Extended-A */
641 {0x100,0x120,0x130,0x140,0x150,0x160,0x170,0x17f,0}, 1,
642 { { 0, 8, DWRITE_SCRIPT_SHAPES_DEFAULT }}
645 /* Latin Extended-B */
646 {0x180,0x190,0x1bf,0x1c0,0x1c3,0x1c4,0x1cc,0x1dc,0x1ff,0x217,0x21b,0x24f,0}, 1,
647 { { 0, 12, DWRITE_SCRIPT_SHAPES_DEFAULT }}
650 /* IPA Extensions */
651 {0x250,0x260,0x270,0x290,0x2af,0}, 1,
652 { { 0, 5, DWRITE_SCRIPT_SHAPES_DEFAULT }}
655 /* Spacing Modifier Letters */
656 {0x2b0,0x2ba,0x2d7,0x2dd,0x2ef,0x2ff,0}, 1,
657 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
660 /* Combining Diacritical Marks */
661 {0x300,0x320,0x340,0x345,0x350,0x36f,0}, 1,
662 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
665 /* Greek and Coptic */
666 {0x370,0x388,0x3d8,0x3e1,0x3e2,0x3fa,0x3ff,0}, 3,
667 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT },
668 { 4, 1, DWRITE_SCRIPT_SHAPES_DEFAULT },
669 { 5, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }
673 /* Cyrillic and Cyrillic Supplement */
674 {0x400,0x40f,0x410,0x44f,0x450,0x45f,0x460,0x481,0x48a,0x4f0,0x4fa,0x4ff,0x500,0x510,0x520,0}, 1,
675 { { 0, 15, DWRITE_SCRIPT_SHAPES_DEFAULT }}
678 /* Armenian */
679 {0x531,0x540,0x559,0x55f,0x570,0x589,0x58a,0}, 1,
680 { { 0, 7, DWRITE_SCRIPT_SHAPES_DEFAULT }}
683 /* Hebrew */
684 {0x5e9,0x5dc,0x5d5,0x5dd,0}, 1,
685 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT }}
688 /* Latin, Hebrew, Latin */
689 {'p','a','r','t',' ','o','n','e',' ',0x5d7,0x5dc,0x5e7,' ',0x5e9,0x5ea,0x5d9,0x5d9,0x5dd,' ','p','a','r','t',' ','t','h','r','e','e',0}, 3,
690 { { 0, 9, DWRITE_SCRIPT_SHAPES_DEFAULT },
691 { 9, 10, DWRITE_SCRIPT_SHAPES_DEFAULT },
692 { 19, 10, DWRITE_SCRIPT_SHAPES_DEFAULT }}
695 /* Syriac */
696 {0x710,0x712,0x712,0x714,'.',0}, 1,
697 { { 0, 5, DWRITE_SCRIPT_SHAPES_DEFAULT }}
700 /* Arabic Supplement */
701 {0x750,0x760,0x76d,'.',0}, 1,
702 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT }}
705 /* Thaana */
706 {0x780,0x78e,0x798,0x7a6,0x7b0,'.',0}, 1,
707 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
710 /* N'Ko */
711 {0x7c0,0x7ca,0x7e8,0x7eb,0x7f6,'.',0}, 1,
712 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
715 /* Thaana */
716 {0x780,0x798,0x7a5,0x7a6,0x7b0,'.',0}, 1,
717 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
720 /* Devanagari */
721 {0x926,0x947,0x935,0x928,0x93e,0x917,0x930,0x940,'.',0}, 1,
722 { { 0, 9, DWRITE_SCRIPT_SHAPES_DEFAULT }}
725 /* Bengali */
726 {0x9ac,0x9be,0x982,0x9b2,0x9be,'.',0}, 1,
727 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
730 /* Gurmukhi */
731 {0xa17,0xa41,0xa30,0xa2e,0xa41,0xa16,0xa40,'.',0}, 1,
732 { { 0, 8, DWRITE_SCRIPT_SHAPES_DEFAULT }}
735 /* Gujarati */
736 {0xa97,0xac1,0xa9c,0xab0,0xabe,0xaa4,0xac0,'.',0}, 1,
737 { { 0, 8, DWRITE_SCRIPT_SHAPES_DEFAULT }}
740 /* Oriya */
741 {0xb13,0xb21,0xb3c,0xb3f,0xb06,'.',0}, 1,
742 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
745 /* Tamil */
746 {0xba4,0xbae,0xbbf,0xbb4,0xbcd,'.',0}, 1,
747 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
750 /* Telugu */
751 {0xc24,0xc46,0xc32,0xc41,0xc17,0xc41,'.',0}, 1,
752 { { 0, 7, DWRITE_SCRIPT_SHAPES_DEFAULT }}
755 /* Kannada */
756 {0xc95,0xca8,0xccd,0xca8,0xca1,'.',0}, 1,
757 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
760 /* Malayalam */
761 {0xd2e,0xd32,0xd2f,0xd3e,0xd33,0xd02,'.',0}, 1,
762 { { 0, 7, DWRITE_SCRIPT_SHAPES_DEFAULT }}
765 /* Sinhala */
766 {0xd82,0xd85,0xd9a,0xdcf,'.',0}, 1,
767 { { 0, 5, DWRITE_SCRIPT_SHAPES_DEFAULT }}
770 /* Thai */
771 {0x0e04,0x0e27,0x0e32,0x0e21,0x0e1e,0x0e22,0x0e32,0x0e22,0x0e32,0x0e21,
772 0x0e2d,0x0e22,0x0e39,0x0e48,0x0e17,0x0e35,0x0e48,0x0e44,0x0e2b,0x0e19,
773 0x0e04,0x0e27,0x0e32,0x0e21,0x0e2a, 0x0e33,0x0e40,0x0e23,0x0e47,0x0e08,
774 0x0e2d,0x0e22,0x0e39,0x0e48,0x0e17,0x0e35,0x0e48,0x0e19,0x0e31,0x0e48,0x0e19,'.',0}, 1,
775 { { 0, 42, DWRITE_SCRIPT_SHAPES_DEFAULT }}
778 /* Lao */
779 {0xead,0xeb1,0xe81,0xeaa,0xead,0xe99,0xea5,0xeb2,0xea7,'.',0}, 1,
780 { { 0, 10, DWRITE_SCRIPT_SHAPES_DEFAULT }}
783 /* Tibetan */
784 {0xf04,0xf05,0xf0e,0x020,0xf51,0xf7c,0xf53,0xf0b,0xf5a,0xf53,0xf0b,
785 0xf51,0xf44,0xf0b,0xf54,0xf7c,0xf0d,'.',0}, 1,
786 { { 0, 18, DWRITE_SCRIPT_SHAPES_DEFAULT }}
789 /* Myanmar */
790 {0x1019,0x103c,0x1014,0x103a,0x1019,0x102c,0x1021,0x1000,0x1039,0x1001,0x101b,0x102c,'.',0}, 1,
791 { { 0, 13, DWRITE_SCRIPT_SHAPES_DEFAULT }}
794 /* Georgian */
795 {0x10a0,0x10d0,0x10da,0x10f1,0x10fb,0x2d00,'.',0}, 1,
796 { { 0, 7, DWRITE_SCRIPT_SHAPES_DEFAULT }}
799 /* Hangul */
800 {0x1100,0x1110,0x1160,0x1170,0x11a8,'.',0}, 1,
801 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
804 /* Ethiopic */
805 {0x130d,0x12d5,0x12dd,0}, 1,
806 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
809 /* Cherokee */
810 {0x13e3,0x13b3,0x13a9,0x0020,0x13a6,0x13ec,0x13c2,0x13af,0x13cd,0x13d7,0}, 1,
811 { { 0, 10, DWRITE_SCRIPT_SHAPES_DEFAULT }}
814 /* Canadian */
815 {0x1403,0x14c4,0x1483,0x144e,0x1450,0x1466,0}, 1,
816 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
819 /* Ogham */
820 {0x169b,0x1691,0x168c,0x1690,0x168b,0x169c,0}, 1,
821 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
824 /* Runic */
825 {0x16a0,0x16a1,0x16a2,0x16a3,0x16a4,0x16a5,0}, 1,
826 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
829 /* Khmer */
830 {0x1781,0x17c1,0x1798,0x179a,0x1797,0x17b6,0x179f,0x17b6,0x19e0,0}, 1,
831 { { 0, 9, DWRITE_SCRIPT_SHAPES_DEFAULT }}
834 /* Mongolian */
835 {0x182e,0x1823,0x1829,0x182d,0x1823,0x182f,0x0020,0x182a,0x1822,0x1834,0x1822,0x182d,0x180c,0}, 1,
836 { { 0, 13, DWRITE_SCRIPT_SHAPES_DEFAULT }}
839 /* Limbu */
840 {0x1900,0x1910,0x1920,0x1930,0}, 1,
841 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT }}
844 /* Tai Le */
845 {0x1956,0x196d,0x1970,0x1956,0x196c,0x1973,0x1951,0x1968,0x1952,0x1970,0}, 1,
846 { { 0, 10, DWRITE_SCRIPT_SHAPES_DEFAULT }}
849 /* New Tai Lue */
850 {0x1992,0x19c4,0}, 1,
851 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }}
854 /* Buginese */
855 {0x1a00,0x1a10,0}, 1,
856 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }}
859 /* Tai Tham */
860 {0x1a20,0x1a40,0x1a50,0}, 1,
861 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
864 /* Balinese */
865 {0x1b00,0x1b05,0x1b20,0}, 1,
866 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
869 /* Sundanese */
870 {0x1b80,0x1b85,0x1ba0,0}, 1,
871 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
874 /* Batak */
875 {0x1bc0,0x1be5,0x1bfc,0}, 1,
876 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
879 /* Lepcha */
880 {0x1c00,0x1c20,0x1c40,0}, 1,
881 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
884 /* Ol Chiki */
885 {0x1c50,0x1c5a,0x1c77,0}, 1,
886 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
889 /* Sundanese Supplement */
890 {0x1cc0,0x1cc5,0x1cc7,0}, 1,
891 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
894 /* Phonetic Extensions */
895 {0x1d00,0x1d40,0x1d70,0}, 1,
896 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
899 /* Combining diacritical marks */
900 {0x1dc0,0x300,0x1ddf,0}, 1,
901 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
904 /* Latin Extended Additional, Extended-C */
905 {0x1e00,0x1d00,0x2c60,0}, 1,
906 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
909 /* Greek Extended */
910 {0x3f0,0x1f00,0}, 1,
911 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }}
914 /* General Punctuation */
915 {0x1dc0,0x2000,0}, 1,
916 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }}
919 /* Superscripts and Subscripts */
920 {0x2070,0x2086,0x2000,0}, 1,
921 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
924 /* Currency, Combining Diacritical Marks for Symbols. Letterlike Symbols.. */
925 {0x20a0,0x20b8,0x2000,0x20d0,0x2100,0x2150,0x2190,0x2200,0x2300,0x2400,0x2440,0x2460,0x2500,0x2580,0x25a0,0x2600,
926 0x2700,0x27c0,0x27f0,0x2900,0x2980,0x2a00,0x2b00,0}, 1,
927 { { 0, 23, DWRITE_SCRIPT_SHAPES_DEFAULT }}
930 /* Braille */
931 {0x2800,0x2070,0x2000,0}, 1,
932 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
935 /* Glagolitic */
936 {0x2c00,0x2c12,0}, 1,
937 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }}
940 /* Coptic */
941 {0x2c80,0x3e2,0x1f00,0}, 2,
942 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT },
943 { 2, 1, DWRITE_SCRIPT_SHAPES_DEFAULT } }
946 /* Tifinagh */
947 {0x2d30,0x2d4a,0}, 1,
948 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }}
951 /* LRE/PDF */
952 {LRE,PDF,'a','b','c','\r',0}, 3,
953 { { 0, 2, DWRITE_SCRIPT_SHAPES_NO_VISUAL },
954 { 2, 3, DWRITE_SCRIPT_SHAPES_DEFAULT },
955 { 5, 1, DWRITE_SCRIPT_SHAPES_NO_VISUAL } }
958 /* LRE/PDF and other visual and non-visual codes from Common script range */
959 {LRE,PDF,'r','!',0x200b,'\r',0}, 3,
960 { { 0, 2, DWRITE_SCRIPT_SHAPES_NO_VISUAL },
961 { 2, 2, DWRITE_SCRIPT_SHAPES_DEFAULT },
962 { 4, 2, DWRITE_SCRIPT_SHAPES_NO_VISUAL } }
965 /* Inherited on its own */
966 {0x300,0x300,0}, 1,
967 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT } }
970 /* Inherited followed by Latin */
971 {0x300,0x300,'a',0}, 1,
972 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT } }
975 /* Inherited mixed with Arabic and Latin */
976 {0x300,'+',0x627,0x300,'a',0}, 2,
977 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT },
978 { 4, 1, DWRITE_SCRIPT_SHAPES_DEFAULT } }
981 {'a',0x300,'+',0x627,0x300,')','a',0}, 3,
982 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT },
983 { 3, 3, DWRITE_SCRIPT_SHAPES_DEFAULT },
984 { 6, 1, DWRITE_SCRIPT_SHAPES_DEFAULT } }
986 /* Paired punctuation */
988 {0x627,'(','a',')','a',0}, 2,
989 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT },
990 { 2, 3, DWRITE_SCRIPT_SHAPES_DEFAULT } }
993 {0x627,'[','a',']',0x627,0}, 3,
994 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT },
995 { 2, 2, DWRITE_SCRIPT_SHAPES_DEFAULT },
996 { 4, 1, DWRITE_SCRIPT_SHAPES_DEFAULT } }
998 /* Combining marks */
1000 /* dotted circle - Common, followed by accent - Inherited */
1001 {0x25cc,0x300,0}, 1,
1002 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT } }
1005 /* combining mark with explicit script value */
1006 {0x25cc,0x300,0x5c4,0}, 1,
1007 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT } }
1010 /* inherited merges with following explicit script */
1011 {0x25cc,0x300,'a',0}, 1,
1012 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT } }
1014 /* keep this as end test data marker */
1015 { {0} }
1018 static void init_expected_sa(struct call_sequence **seq, const struct sa_test *test)
1020 static const struct call_entry end_of_sequence = { LastKind };
1021 int i;
1023 flush_sequence(seq, ANALYZER_ID);
1025 /* add expected calls */
1026 for (i = 0; i < test->item_count; i++)
1028 struct call_entry call;
1030 call.kind = ScriptAnalysis;
1031 call.sa.pos = test->sa[i].pos;
1032 call.sa.len = test->sa[i].len;
1033 call.sa.shapes = test->sa[i].shapes;
1034 add_call(seq, 0, &call);
1037 /* and stop marker */
1038 add_call(seq, 0, &end_of_sequence);
1041 static void get_script_analysis(const WCHAR *str, DWRITE_SCRIPT_ANALYSIS *sa)
1043 IDWriteTextAnalyzer *analyzer;
1044 HRESULT hr;
1046 init_textsource(&analysissource, str, DWRITE_READING_DIRECTION_LEFT_TO_RIGHT);
1047 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
1048 ok(hr == S_OK, "got 0x%08x\n", hr);
1050 hr = IDWriteTextAnalyzer_AnalyzeScript(analyzer, &analysissource.IDWriteTextAnalysisSource_iface, 0,
1051 lstrlenW(analysissource.text), &analysissink2);
1052 ok(hr == S_OK, "got 0x%08x\n", hr);
1054 *sa = g_sa;
1057 static void test_AnalyzeScript(void)
1059 const struct sa_test *ptr = sa_tests;
1060 IDWriteTextAnalyzer *analyzer;
1061 HRESULT hr;
1063 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
1064 ok(hr == S_OK, "got 0x%08x\n", hr);
1066 while (*ptr->string)
1068 init_textsource(&analysissource, ptr->string, DWRITE_READING_DIRECTION_LEFT_TO_RIGHT);
1070 init_expected_sa(expected_seq, ptr);
1071 hr = IDWriteTextAnalyzer_AnalyzeScript(analyzer, &analysissource.IDWriteTextAnalysisSource_iface, 0,
1072 lstrlenW(ptr->string), &analysissink);
1073 ok(hr == S_OK, "got 0x%08x\n", hr);
1074 ok_sequence(sequences, ANALYZER_ID, expected_seq[0]->sequence, wine_dbgstr_w(ptr->string), FALSE);
1075 ptr++;
1078 IDWriteTextAnalyzer_Release(analyzer);
1081 struct linebreaks_test {
1082 const WCHAR text[BREAKPOINT_COUNT+1];
1083 DWRITE_LINE_BREAKPOINT bp[BREAKPOINT_COUNT];
1086 static struct linebreaks_test linebreaks_tests[] = {
1087 { {'A','-','B',' ','C',0x58a,'D',0x2010,'E',0x2012,'F',0x2013,'\t',0xc,0xb,0x2028,0x2029,0x200b,0},
1089 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1090 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 0, 0 },
1091 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1092 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 1, 0 },
1093 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1094 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 0, 0 },
1095 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1096 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 0, 0 },
1097 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1098 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 0, 0 },
1099 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1100 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1101 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 1, 0 },
1102 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_MUST_BREAK, 1, 0 },
1103 { DWRITE_BREAK_CONDITION_MUST_BREAK, DWRITE_BREAK_CONDITION_MUST_BREAK, 1, 0 },
1104 { DWRITE_BREAK_CONDITION_MUST_BREAK, DWRITE_BREAK_CONDITION_MUST_BREAK, 1, 0 },
1105 { DWRITE_BREAK_CONDITION_MUST_BREAK, DWRITE_BREAK_CONDITION_MUST_BREAK, 1, 0 },
1106 { DWRITE_BREAK_CONDITION_MUST_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 0, 0 },
1109 /* Soft hyphen, visible word dividers */
1110 { {'A',0xad,'B',0x5be,'C',0xf0b,'D',0x1361,'E',0x17d8,'F',0x17da,'G',0},
1112 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1113 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 0, 1 },
1114 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1115 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 0, 0 },
1116 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1117 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 0, 0 },
1118 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1119 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 0, 0 },
1120 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1121 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 0, 0 },
1122 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1123 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 0, 0 },
1124 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 0, 0 },
1127 { { 0 } }
1130 static void compare_breakpoints(const struct linebreaks_test *test, DWRITE_LINE_BREAKPOINT *actual)
1132 static const char *conditions[] = {"N","CB","NB","B"};
1133 const WCHAR *text = test->text;
1134 int cmp = memcmp(test->bp, actual, sizeof(*actual)*BREAKPOINT_COUNT);
1135 ok(!cmp, "%s: got wrong breakpoint data\n", wine_dbgstr_w(test->text));
1136 if (cmp) {
1137 int i = 0;
1138 while (*text) {
1139 ok(!memcmp(&test->bp[i], &actual[i], sizeof(*actual)),
1140 "%s: got [%s, %s] (%s, %s), expected [%s, %s] (%s, %s)\n",
1141 wine_dbgstr_wn(&test->text[i], 1),
1142 conditions[g_actual_bp[i].breakConditionBefore],
1143 conditions[g_actual_bp[i].breakConditionAfter],
1144 g_actual_bp[i].isWhitespace ? "WS" : "0",
1145 g_actual_bp[i].isSoftHyphen ? "SHY" : "0",
1146 conditions[test->bp[i].breakConditionBefore],
1147 conditions[test->bp[i].breakConditionAfter],
1148 test->bp[i].isWhitespace ? "WS" : "0",
1149 test->bp[i].isSoftHyphen ? "SHY" : "0");
1150 if (g_actual_bp[i].isSoftHyphen)
1151 ok(!g_actual_bp[i].isWhitespace, "%s: soft hyphen marked as a whitespace\n",
1152 wine_dbgstr_wn(&test->text[i], 1));
1153 text++;
1154 i++;
1159 static void test_AnalyzeLineBreakpoints(void)
1161 const struct linebreaks_test *ptr = linebreaks_tests;
1162 IDWriteTextAnalyzer *analyzer;
1163 UINT32 i = 0;
1164 HRESULT hr;
1166 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
1167 ok(hr == S_OK, "got 0x%08x\n", hr);
1169 init_textsource(&analysissource, L"", DWRITE_READING_DIRECTION_LEFT_TO_RIGHT);
1170 hr = IDWriteTextAnalyzer_AnalyzeLineBreakpoints(analyzer, &analysissource.IDWriteTextAnalysisSource_iface, 0, 0,
1171 &analysissink);
1172 ok(hr == S_OK, "got 0x%08x\n", hr);
1174 while (*ptr->text)
1176 UINT32 len;
1178 init_textsource(&analysissource, ptr->text, DWRITE_READING_DIRECTION_LEFT_TO_RIGHT);
1180 len = lstrlenW(ptr->text);
1181 if (len > BREAKPOINT_COUNT) {
1182 ok(0, "test %u: increase BREAKPOINT_COUNT to at least %u\n", i, len);
1183 i++;
1184 ptr++;
1185 continue;
1188 memset(g_actual_bp, 0, sizeof(g_actual_bp));
1189 hr = IDWriteTextAnalyzer_AnalyzeLineBreakpoints(analyzer, &analysissource.IDWriteTextAnalysisSource_iface,
1190 0, len, &analysissink);
1191 ok(hr == S_OK, "got 0x%08x\n", hr);
1192 compare_breakpoints(ptr, g_actual_bp);
1194 i++;
1195 ptr++;
1198 IDWriteTextAnalyzer_Release(analyzer);
1201 static void test_GetScriptProperties(void)
1203 IDWriteTextAnalyzer1 *analyzer1;
1204 IDWriteTextAnalyzer *analyzer;
1205 DWRITE_SCRIPT_ANALYSIS sa;
1206 DWRITE_SCRIPT_PROPERTIES props;
1207 HRESULT hr;
1209 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
1210 ok(hr == S_OK, "got 0x%08x\n", hr);
1212 hr = IDWriteTextAnalyzer_QueryInterface(analyzer, &IID_IDWriteTextAnalyzer1, (void**)&analyzer1);
1213 IDWriteTextAnalyzer_Release(analyzer);
1214 if (hr != S_OK) {
1215 win_skip("GetScriptProperties() is not supported.\n");
1216 return;
1219 sa.script = 1000;
1220 hr = IDWriteTextAnalyzer1_GetScriptProperties(analyzer1, sa, &props);
1221 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1223 if (0) /* crashes on native */
1224 hr = IDWriteTextAnalyzer1_GetScriptProperties(analyzer1, sa, NULL);
1226 sa.script = 0;
1227 hr = IDWriteTextAnalyzer1_GetScriptProperties(analyzer1, sa, &props);
1228 ok(hr == S_OK, "got 0x%08x\n", hr);
1230 IDWriteTextAnalyzer1_Release(analyzer1);
1233 struct textcomplexity_test {
1234 const WCHAR text[5];
1235 UINT32 length;
1236 BOOL simple;
1237 UINT32 len_read;
1240 static const struct textcomplexity_test textcomplexity_tests[] = {
1241 { {0}, 1, FALSE, 1 },
1242 { {0}, 0, TRUE, 0 },
1243 { {0x610,0}, 0, TRUE, 0 },
1244 { {'A','B','C','D',0}, 3, TRUE, 3 },
1245 { {'A','B','C','D',0}, 5, TRUE, 4 },
1246 { {'A','B','C','D',0}, 10, TRUE, 4 },
1247 { {'A',0x610,'C','D',0}, 1, TRUE, 1 },
1248 { {'A',0x610,'C','D',0}, 2, FALSE, 2 },
1249 { {0x610,'A','C','D',0}, 1, FALSE, 1 },
1250 { {0x610,'A','C','D',0}, 2, FALSE, 1 },
1251 { {0x610,0x610,'C','D',0}, 2, FALSE, 2 },
1252 { {0xd800,'A','B',0}, 1, FALSE, 1 },
1253 { {0xd800,'A','B',0}, 2, FALSE, 1 },
1254 { {0xdc00,'A','B',0}, 2, FALSE, 1 },
1255 { {0x202a,'A',0x202c,0}, 3, FALSE, 1 },
1256 { {0x200e,'A',0}, 2, FALSE, 1 },
1257 { {0x200f,'A',0}, 2, FALSE, 1 },
1258 { {0x202d,'A',0}, 2, FALSE, 1 },
1259 { {0x202e,'A',0}, 2, FALSE, 1 },
1263 static void test_GetTextComplexity(void)
1265 IDWriteTextAnalyzer1 *analyzer1;
1266 IDWriteTextAnalyzer *analyzer;
1267 IDWriteFontFace *fontface;
1268 UINT16 indices[10];
1269 BOOL simple;
1270 HRESULT hr;
1271 UINT32 len;
1272 int i;
1274 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
1275 ok(hr == S_OK, "got 0x%08x\n", hr);
1277 hr = IDWriteTextAnalyzer_QueryInterface(analyzer, &IID_IDWriteTextAnalyzer1, (void**)&analyzer1);
1278 IDWriteTextAnalyzer_Release(analyzer);
1279 if (hr != S_OK) {
1280 win_skip("GetTextComplexity() is not supported.\n");
1281 return;
1284 if (0) { /* crashes on native */
1285 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer1, NULL, 0, NULL, NULL, NULL, NULL);
1286 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer1, NULL, 0, NULL, NULL, &len, NULL);
1287 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer1, L"ABC", 3, NULL, NULL, NULL, NULL);
1288 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer1, L"ABC", 3, NULL, NULL, &len, NULL);
1289 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer1, L"ABC", 3, NULL, &simple, NULL, NULL);
1292 len = 1;
1293 simple = TRUE;
1294 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer1, NULL, 0, NULL, &simple, &len, NULL);
1295 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1296 ok(len == 0, "got %d\n", len);
1297 ok(simple == FALSE, "got %d\n", simple);
1299 len = 1;
1300 simple = TRUE;
1301 indices[0] = 1;
1302 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer1, L"ABC", 3, NULL, &simple, &len, NULL);
1303 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1304 ok(len == 0, "got %d\n", len);
1305 ok(simple == FALSE, "got %d\n", simple);
1306 ok(indices[0] == 1, "got %d\n", indices[0]);
1308 fontface = create_fontface();
1310 for (i = 0; i < ARRAY_SIZE(textcomplexity_tests); i++) {
1311 const struct textcomplexity_test *ptr = &textcomplexity_tests[i];
1312 len = 1;
1313 simple = !ptr->simple;
1314 indices[0] = 0;
1315 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer1, ptr->text, ptr->length, fontface, &simple, &len, indices);
1316 ok(hr == S_OK, "%d: got 0x%08x\n", i, hr);
1317 ok(len == ptr->len_read, "%d: read length: got %d, expected %d\n", i, len, ptr->len_read);
1318 ok(simple == ptr->simple, "%d: simple: got %d, expected %d\n", i, simple, ptr->simple);
1319 if (simple && ptr->length)
1320 ok(indices[0] > 0, "%d: got %d\n", i, indices[0]);
1321 else
1322 ok(indices[0] == 0, "%d: got %d\n", i, indices[0]);
1325 IDWriteFontFace_Release(fontface);
1326 IDWriteTextAnalyzer1_Release(analyzer1);
1329 static void test_numbersubstitution(void)
1331 IDWriteNumberSubstitution *substitution;
1332 HRESULT hr;
1334 /* locale is not specified, method does not require it */
1335 hr = IDWriteFactory_CreateNumberSubstitution(factory, DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE, NULL, FALSE, &substitution);
1336 ok(hr == S_OK, "got 0x%08x\n", hr);
1337 IDWriteNumberSubstitution_Release(substitution);
1339 /* invalid locale name, method does not require it */
1340 hr = IDWriteFactory_CreateNumberSubstitution(factory, DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE, L"dummy",
1341 FALSE, &substitution);
1342 ok(hr == S_OK, "Failed to create number substitution, hr %#x.\n", hr);
1343 IDWriteNumberSubstitution_Release(substitution);
1345 /* invalid method */
1346 hr = IDWriteFactory_CreateNumberSubstitution(factory, DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL+1, NULL, FALSE, &substitution);
1347 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1349 /* invalid method */
1350 hr = IDWriteFactory_CreateNumberSubstitution(factory, -1, NULL, FALSE, &substitution);
1351 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1353 /* invalid locale */
1354 hr = IDWriteFactory_CreateNumberSubstitution(factory, DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL, NULL, FALSE, &substitution);
1355 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1357 hr = IDWriteFactory_CreateNumberSubstitution(factory, DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL, L"dummy",
1358 FALSE, &substitution);
1359 ok(hr == E_INVALIDARG, "Unexpected hr %#x.\n", hr);
1361 hr = IDWriteFactory_CreateNumberSubstitution(factory, DWRITE_NUMBER_SUBSTITUTION_METHOD_CONTEXTUAL, L"dummy",
1362 FALSE, &substitution);
1363 ok(hr == E_INVALIDARG, "Unexpected hr %#x.\n", hr);
1365 hr = IDWriteFactory_CreateNumberSubstitution(factory, DWRITE_NUMBER_SUBSTITUTION_METHOD_NATIONAL, L"dummy",
1366 FALSE, &substitution);
1367 ok(hr == E_INVALIDARG, "Unexpected hr %#x.\n", hr);
1369 /* invalid locale, but it's not needed for this method */
1370 hr = IDWriteFactory_CreateNumberSubstitution(factory, DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE, L"dummy", FALSE,
1371 &substitution);
1372 ok(hr == S_OK, "Failed to create number substitution, hr %#x.\n", hr);
1373 IDWriteNumberSubstitution_Release(substitution);
1376 static void get_fontface_glyphs(IDWriteFontFace *fontface, const WCHAR *str, UINT16 *glyphs)
1378 while (*str) {
1379 UINT32 codepoint = *str;
1380 HRESULT hr;
1382 hr = IDWriteFontFace_GetGlyphIndices(fontface, &codepoint, 1, glyphs++);
1383 ok(hr == S_OK, "got 0x%08x\n", hr);
1384 str++;
1388 static void get_fontface_advances(IDWriteFontFace *fontface, FLOAT emsize, const UINT16 *glyphs, FLOAT *advances, UINT32 count)
1390 DWRITE_FONT_METRICS fontmetrics;
1391 UINT32 i;
1393 IDWriteFontFace_GetMetrics(fontface, &fontmetrics);
1394 for (i = 0; i < count; i++) {
1395 DWRITE_GLYPH_METRICS metrics;
1396 HRESULT hr;
1398 hr = IDWriteFontFace_GetDesignGlyphMetrics(fontface, glyphs + i, 1, &metrics, FALSE);
1399 ok(hr == S_OK, "got 0x%08x\n", hr);
1401 advances[i] = (FLOAT)metrics.advanceWidth * emsize / (FLOAT)fontmetrics.designUnitsPerEm;
1405 enum ot_gdef_class
1407 GDEF_CLASS_UNCLASSIFIED = 0,
1408 GDEF_CLASS_BASE = 1,
1409 GDEF_CLASS_LIGATURE = 2,
1410 GDEF_CLASS_MARK = 3,
1411 GDEF_CLASS_COMPONENT = 4,
1412 GDEF_CLASS_MAX = GDEF_CLASS_COMPONENT,
1415 struct dwrite_fonttable
1417 BYTE *data;
1418 void *context;
1419 unsigned int size;
1422 static const void *table_read_ensure(const struct dwrite_fonttable *table, unsigned int offset, unsigned int size)
1424 if (size > table->size || offset > table->size - size)
1425 return NULL;
1427 return table->data + offset;
1430 static WORD table_read_be_word(const struct dwrite_fonttable *table, unsigned int offset)
1432 const WORD *ptr = table_read_ensure(table, offset, sizeof(*ptr));
1433 return ptr ? GET_BE_WORD(*ptr) : 0;
1436 static int gdef_class_compare_format2(const void *g, const void *r)
1438 const struct ot_gdef_class_range *range = r;
1439 UINT16 glyph = *(UINT16 *)g;
1441 if (glyph < GET_BE_WORD(range->start_glyph))
1442 return -1;
1443 else if (glyph > GET_BE_WORD(range->end_glyph))
1444 return 1;
1445 else
1446 return 0;
1449 static unsigned int get_glyph_class(const struct dwrite_fonttable *table, UINT16 glyph)
1451 unsigned int glyph_class = GDEF_CLASS_UNCLASSIFIED, offset;
1452 WORD format, count;
1454 offset = table_read_be_word(table, 4);
1456 format = table_read_be_word(table, offset);
1458 if (format == 1)
1460 const struct ot_gdef_classdef_format1 *format1;
1462 count = table_read_be_word(table, offset + FIELD_OFFSET(struct ot_gdef_classdef_format1, glyph_count));
1463 format1 = table_read_ensure(table, offset, FIELD_OFFSET(struct ot_gdef_classdef_format1, classes[count]));
1464 if (format1)
1466 WORD start_glyph = GET_BE_WORD(format1->start_glyph);
1467 if (glyph >= start_glyph && (glyph - start_glyph) < count)
1469 glyph_class = GET_BE_WORD(format1->classes[glyph - start_glyph]);
1470 if (glyph_class > GDEF_CLASS_MAX)
1471 glyph_class = GDEF_CLASS_UNCLASSIFIED;
1475 else if (format == 2)
1477 const struct ot_gdef_classdef_format2 *format2;
1479 count = table_read_be_word(table, offset + FIELD_OFFSET(struct ot_gdef_classdef_format2, range_count));
1480 format2 = table_read_ensure(table, offset, FIELD_OFFSET(struct ot_gdef_classdef_format2, ranges[count]));
1481 if (format2)
1483 const struct ot_gdef_class_range *range = bsearch(&glyph, format2->ranges, count,
1484 sizeof(struct ot_gdef_class_range), gdef_class_compare_format2);
1485 glyph_class = range && glyph <= GET_BE_WORD(range->end_glyph) ?
1486 GET_BE_WORD(range->glyph_class) : GDEF_CLASS_UNCLASSIFIED;
1487 if (glyph_class > GDEF_CLASS_MAX)
1488 glyph_class = GDEF_CLASS_UNCLASSIFIED;
1492 return glyph_class;
1495 static void get_enus_string(IDWriteLocalizedStrings *strings, WCHAR *buff, unsigned int size)
1497 BOOL exists = FALSE;
1498 unsigned int index;
1499 HRESULT hr;
1501 hr = IDWriteLocalizedStrings_FindLocaleName(strings, L"en-us", &index, &exists);
1502 ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
1504 /* Not all fonts have an en-us name! */
1505 if (!exists)
1506 index = 0;
1508 hr = IDWriteLocalizedStrings_GetString(strings, index, buff, size);
1509 ok(hr == S_OK, "Failed to get name string, hr %#x.\n", hr);
1512 static void test_glyph_props(IDWriteTextAnalyzer *analyzer, const WCHAR *family, const WCHAR *face,
1513 IDWriteFontFace *fontface)
1515 unsigned int i, ch, count, offset;
1516 struct dwrite_fonttable gdef;
1517 DWRITE_UNICODE_RANGE *ranges;
1518 IDWriteFontFace1 *fontface1;
1519 BOOL exists = FALSE;
1520 HRESULT hr;
1522 hr = IDWriteFontFace_TryGetFontTable(fontface, MS_GDEF_TAG, (const void **)&gdef.data, &gdef.size,
1523 &gdef.context, &exists);
1524 ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
1526 if (!exists)
1527 return;
1529 offset = table_read_be_word(&gdef, 4);
1530 if (!offset)
1532 IDWriteFontFace_ReleaseFontTable(fontface, gdef.context);
1533 return;
1536 if (FAILED(IDWriteFontFace_QueryInterface(fontface, &IID_IDWriteFontFace1, (void **)&fontface1)))
1538 IDWriteFontFace_ReleaseFontTable(fontface, gdef.context);
1539 return;
1542 hr = IDWriteFontFace1_GetUnicodeRanges(fontface1, 0, NULL, &count);
1543 ok(hr == E_NOT_SUFFICIENT_BUFFER, "Unexpected hr %#x.\n", hr);
1545 ranges = heap_alloc(count * sizeof(*ranges));
1547 hr = IDWriteFontFace1_GetUnicodeRanges(fontface1, count, ranges, &count);
1548 ok(hr == S_OK, "Failed to get ranges, hr %#x.\n", hr);
1550 for (i = 0; i < count; ++i)
1552 if (ranges[i].first > 0xffff)
1553 break;
1555 for (ch = ranges[i].first; ch <= ranges[i].last; ch++)
1557 DWRITE_SHAPING_TEXT_PROPERTIES text_props[10];
1558 DWRITE_SHAPING_GLYPH_PROPERTIES glyph_props[10];
1559 UINT16 glyphs[10], clustermap[10], glyph;
1560 unsigned int actual_count, glyph_class;
1561 DWRITE_SCRIPT_ANALYSIS sa;
1562 WCHAR text[1];
1564 hr = IDWriteFontFace1_GetGlyphIndices(fontface1, &ch, 1, &glyph);
1565 ok(hr == S_OK, "Failed to get glyph index, hr %#x.\n", hr);
1567 if (!glyph)
1568 continue;
1570 sa.script = 999;
1571 sa.shapes = DWRITE_SCRIPT_SHAPES_DEFAULT;
1572 text[0] = (WCHAR)ch;
1573 memset(glyph_props, 0, sizeof(glyph_props));
1574 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, text, 1, fontface, FALSE, FALSE, &sa, NULL,
1575 NULL, NULL, NULL, 0, ARRAY_SIZE(glyphs), clustermap, text_props, glyphs, glyph_props, &actual_count);
1576 ok(hr == S_OK, "Failed to shape, hr %#x.\n", hr);
1577 if (actual_count > 1)
1578 continue;
1580 glyph_class = get_glyph_class(&gdef, glyphs[0]);
1582 switch (glyph_class)
1584 case GDEF_CLASS_MARK:
1585 ok(glyph_props[0].isDiacritic && glyph_props[0].isZeroWidthSpace,
1586 "%#x -> %u: unexpected glyph properties %u/%u. Class %u. Font %s - %s.\n",
1587 text[0], glyphs[0], glyph_props[0].isDiacritic, glyph_props[0].isZeroWidthSpace,
1588 glyph_class, wine_dbgstr_w(family), wine_dbgstr_w(face));
1589 break;
1590 default:
1591 break;
1594 if (glyph_props[0].isDiacritic)
1595 ok(glyph_props[0].isZeroWidthSpace,
1596 "%#x -> %u: unexpected glyph properties %u/%u. Class %u. Font %s - %s.\n", text[0], glyphs[0],
1597 glyph_props[0].isDiacritic, glyph_props[0].isZeroWidthSpace, glyph_class,
1598 wine_dbgstr_w(family), wine_dbgstr_w(face));
1602 heap_free(ranges);
1604 IDWriteFontFace_ReleaseFontTable(fontface, gdef.context);
1605 IDWriteFontFace1_Release(fontface1);
1608 static void test_GetGlyphs(void)
1610 static const WCHAR test1W[] = {'<','B',' ','C',0};
1611 static const WCHAR test2W[] = {'<','B','\t','C',0};
1612 static const WCHAR test3W[] = {0x202a,0x202c,0};
1613 DWRITE_SHAPING_GLYPH_PROPERTIES shapingprops[20];
1614 DWRITE_SHAPING_TEXT_PROPERTIES props[20];
1615 UINT32 maxglyphcount, actual_count;
1616 FLOAT advances[10], advances2[10];
1617 IDWriteFontCollection *syscoll;
1618 IDWriteTextAnalyzer *analyzer;
1619 IDWriteFontFace *fontface;
1620 DWRITE_SCRIPT_ANALYSIS sa;
1621 DWRITE_GLYPH_OFFSET offsets[10];
1622 UINT16 clustermap[10];
1623 UINT16 glyphs1[10];
1624 UINT16 glyphs2[10];
1625 unsigned int i, j;
1626 HRESULT hr;
1628 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
1629 ok(hr == S_OK, "got 0x%08x\n", hr);
1631 fontface = create_fontface();
1633 maxglyphcount = 1;
1634 sa.script = 0;
1635 sa.shapes = DWRITE_SCRIPT_SHAPES_DEFAULT;
1636 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test1W, lstrlenW(test1W), fontface, FALSE, FALSE, &sa, NULL,
1637 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs1, shapingprops, &actual_count);
1638 ok(hr == E_NOT_SUFFICIENT_BUFFER, "got 0x%08x\n", hr);
1640 if (0) {
1641 /* NULL fontface - crashes on Windows */
1642 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test1W, lstrlenW(test1W), NULL, FALSE, FALSE, &sa, NULL,
1643 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs1, shapingprops, &actual_count);
1646 /* invalid script id */
1647 maxglyphcount = 10;
1648 actual_count = 0;
1649 sa.script = 999;
1650 sa.shapes = DWRITE_SCRIPT_SHAPES_DEFAULT;
1651 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test1W, lstrlenW(test1W), fontface, FALSE, FALSE, &sa, NULL,
1652 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs1, shapingprops, &actual_count);
1653 ok(hr == S_OK, "got 0x%08x\n", hr);
1654 ok(actual_count == 4, "got %d\n", actual_count);
1655 ok(sa.script == 999, "got %u\n", sa.script);
1657 /* no '\t' -> ' ' replacement */
1658 maxglyphcount = 10;
1659 actual_count = 0;
1660 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test1W, lstrlenW(test1W), fontface, FALSE, FALSE, &sa, NULL,
1661 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs1, shapingprops, &actual_count);
1662 ok(hr == S_OK, "got 0x%08x\n", hr);
1663 ok(actual_count == 4, "got %d\n", actual_count);
1665 actual_count = 0;
1666 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test2W, lstrlenW(test2W), fontface, FALSE, FALSE, &sa, NULL,
1667 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs2, shapingprops, &actual_count);
1668 ok(hr == S_OK, "got 0x%08x\n", hr);
1669 ok(actual_count == 4, "got %d\n", actual_count);
1670 ok(glyphs1[2] != glyphs2[2], "got %d\n", glyphs1[2]);
1672 /* check that mirroring works */
1673 maxglyphcount = 10;
1674 actual_count = 0;
1675 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test1W, lstrlenW(test1W), fontface, FALSE, FALSE, &sa, NULL,
1676 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs1, shapingprops, &actual_count);
1677 ok(hr == S_OK, "got 0x%08x\n", hr);
1678 ok(actual_count == 4, "got %d\n", actual_count);
1680 actual_count = 0;
1681 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test1W, lstrlenW(test1W), fontface, FALSE, TRUE, &sa, NULL,
1682 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs2, shapingprops, &actual_count);
1683 ok(hr == S_OK, "got 0x%08x\n", hr);
1684 ok(actual_count == 4, "got %d\n", actual_count);
1685 ok(glyphs1[0] != glyphs2[0], "got %d\n", glyphs1[0]);
1687 /* embedded control codes, with unknown script id 0 */
1688 get_fontface_glyphs(fontface, test3W, glyphs2);
1689 get_fontface_advances(fontface, 10.0, glyphs2, advances2, 2);
1691 actual_count = 0;
1692 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test3W, lstrlenW(test3W), fontface, FALSE, TRUE, &sa, NULL,
1693 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs1, shapingprops, &actual_count);
1694 ok(hr == S_OK, "got 0x%08x\n", hr);
1695 ok(actual_count == 2, "got %d\n", actual_count);
1696 ok(glyphs1[0] == glyphs2[0], "got %u, expected %u\n", glyphs1[0], glyphs2[0]);
1697 ok(glyphs1[1] == glyphs2[1], "got %u, expected %u\n", glyphs1[1], glyphs2[1]);
1698 ok(shapingprops[0].isClusterStart == 1, "got %d\n", shapingprops[0].isClusterStart);
1699 ok(shapingprops[0].isZeroWidthSpace == 0, "got %d\n", shapingprops[0].isZeroWidthSpace);
1700 ok(shapingprops[1].isClusterStart == 1, "got %d\n", shapingprops[1].isClusterStart);
1701 ok(shapingprops[1].isZeroWidthSpace == 0, "got %d\n", shapingprops[1].isZeroWidthSpace);
1702 ok(clustermap[0] == 0, "got %d\n", clustermap[0]);
1703 ok(clustermap[1] == 1, "got %d\n", clustermap[1]);
1705 memset(advances, 0, sizeof(advances));
1706 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, test3W, clustermap, props, lstrlenW(test3W),
1707 glyphs1, shapingprops, actual_count, fontface, 10.0, FALSE, FALSE, &sa, NULL, NULL,
1708 NULL, 0, advances, offsets);
1709 ok(hr == S_OK, "got 0x%08x\n", hr);
1710 ok(advances[0] == advances2[0], "got %.2f, expected %.2f\n", advances[0], advances2[0]);
1711 ok(advances[1] == advances2[1], "got %.2f, expected %.2f\n", advances[1], advances2[1]);
1713 /* embedded control codes with proper script */
1714 sa.script = 0;
1715 get_script_analysis(test3W, &sa);
1716 ok(sa.script != 0, "got %d\n", sa.script);
1717 actual_count = 0;
1718 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test3W, lstrlenW(test3W), fontface, FALSE, FALSE, &sa, NULL,
1719 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs1, shapingprops, &actual_count);
1720 ok(hr == S_OK, "got 0x%08x\n", hr);
1721 ok(actual_count == 2, "got %d\n", actual_count);
1722 ok(glyphs1[0] == glyphs2[0], "got %u, expected %u\n", glyphs1[0], glyphs2[0]);
1723 ok(glyphs1[1] == glyphs2[1], "got %u, expected %u\n", glyphs1[1], glyphs2[1]);
1724 ok(shapingprops[0].isClusterStart == 1, "got %d\n", shapingprops[0].isClusterStart);
1725 ok(shapingprops[0].isZeroWidthSpace == 0, "got %d\n", shapingprops[0].isZeroWidthSpace);
1726 ok(shapingprops[1].isClusterStart == 1, "got %d\n", shapingprops[1].isClusterStart);
1727 ok(shapingprops[1].isZeroWidthSpace == 0, "got %d\n", shapingprops[1].isZeroWidthSpace);
1728 ok(clustermap[0] == 0, "got %d\n", clustermap[0]);
1729 ok(clustermap[1] == 1, "got %d\n", clustermap[1]);
1731 memset(advances, 0, sizeof(advances));
1732 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, test3W, clustermap, props, lstrlenW(test3W),
1733 glyphs1, shapingprops, actual_count, fontface, 10.0, FALSE, FALSE, &sa, NULL, NULL,
1734 NULL, 0, advances, offsets);
1735 ok(hr == S_OK, "got 0x%08x\n", hr);
1736 ok(advances[0] == advances2[0], "got %.2f, expected %.2f\n", advances[0], advances2[0]);
1737 ok(advances[1] == advances2[1], "got %.2f, expected %.2f\n", advances[1], advances2[1]);
1739 /* DWRITE_SCRIPT_SHAPES_NO_VISUAL run */
1740 maxglyphcount = 10;
1741 actual_count = 0;
1742 sa.script = 0;
1743 sa.shapes = DWRITE_SCRIPT_SHAPES_NO_VISUAL;
1744 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test1W, lstrlenW(test1W), fontface, FALSE, FALSE, &sa, NULL,
1745 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs1, shapingprops, &actual_count);
1746 ok(hr == S_OK, "got 0x%08x\n", hr);
1747 ok(actual_count == 4, "got %d\n", actual_count);
1748 ok(sa.script == 0, "got %u\n", sa.script);
1749 ok(!shapingprops[0].isZeroWidthSpace, "got %d\n", shapingprops[0].isZeroWidthSpace);
1751 IDWriteFontFace_Release(fontface);
1753 /* Test setting glyph properties from GDEF. */
1754 hr = IDWriteFactory_GetSystemFontCollection(factory, &syscoll, FALSE);
1755 ok(hr == S_OK, "Failed to get system collection, hr %#x.\n", hr);
1757 for (i = 0; i < IDWriteFontCollection_GetFontFamilyCount(syscoll); ++i)
1759 IDWriteLocalizedStrings *names;
1760 IDWriteFontFamily *family;
1761 WCHAR familyW[256];
1763 hr = IDWriteFontCollection_GetFontFamily(syscoll, i, &family);
1764 ok(hr == S_OK, "Failed to get font family, hr %#x.\n", hr);
1766 hr = IDWriteFontFamily_GetFamilyNames(family, &names);
1767 ok(hr == S_OK, "Failed to get family names, hr %#x.\n", hr);
1768 get_enus_string(names, familyW, ARRAY_SIZE(familyW));
1769 IDWriteLocalizedStrings_Release(names);
1771 for (j = 0; j < IDWriteFontFamily_GetFontCount(family); ++j)
1773 IDWriteFont *font;
1774 WCHAR faceW[256];
1776 hr = IDWriteFontFamily_GetFont(family, j, &font);
1777 ok(hr == S_OK, "Failed to get font instance, hr %#x.\n", hr);
1779 hr = IDWriteFont_CreateFontFace(font, &fontface);
1780 ok(hr == S_OK, "Failed to create fontface, hr %#x.\n", hr);
1782 hr = IDWriteFont_GetFaceNames(font, &names);
1783 ok(hr == S_OK, "Failed to get face names, hr %#x.\n", hr);
1784 get_enus_string(names, faceW, ARRAY_SIZE(faceW));
1785 IDWriteLocalizedStrings_Release(names);
1787 test_glyph_props(analyzer, familyW, faceW, fontface);
1789 IDWriteFontFace_Release(fontface);
1790 IDWriteFont_Release(font);
1793 IDWriteFontFamily_Release(family);
1796 IDWriteFontCollection_Release(syscoll);
1798 IDWriteTextAnalyzer_Release(analyzer);
1801 static void test_GetTypographicFeatures(void)
1803 static const WCHAR arabicW[] = {0x064a,0x064f,0x0633,0};
1804 DWRITE_FONT_FEATURE_TAG tags[20];
1805 IDWriteTextAnalyzer2 *analyzer2;
1806 IDWriteTextAnalyzer *analyzer;
1807 IDWriteFontFace *fontface;
1808 DWRITE_SCRIPT_ANALYSIS sa;
1809 UINT32 count;
1810 HRESULT hr;
1812 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
1813 ok(hr == S_OK, "got 0x%08x\n", hr);
1815 hr = IDWriteTextAnalyzer_QueryInterface(analyzer, &IID_IDWriteTextAnalyzer2, (void**)&analyzer2);
1816 IDWriteTextAnalyzer_Release(analyzer);
1817 if (hr != S_OK) {
1818 win_skip("GetTypographicFeatures() is not supported.\n");
1819 return;
1822 fontface = create_fontface();
1824 get_script_analysis(L"abc", &sa);
1825 count = 0;
1826 hr = IDWriteTextAnalyzer2_GetTypographicFeatures(analyzer2, fontface, sa, NULL, 0, &count, NULL);
1827 ok(hr == E_NOT_SUFFICIENT_BUFFER, "Unexpected hr %#x.\n", hr);
1828 ok(!!count, "Unexpected count %u.\n", count);
1830 /* invalid locale name is ignored */
1831 get_script_analysis(L"abc", &sa);
1832 count = 0;
1833 hr = IDWriteTextAnalyzer2_GetTypographicFeatures(analyzer2, fontface, sa, L"cadabra", 0, &count, NULL);
1834 ok(hr == E_NOT_SUFFICIENT_BUFFER, "Unexpected hr %#x.\n", hr);
1835 ok(!!count, "Unexpected count %u.\n", count);
1837 /* Make some calls for different scripts. */
1839 get_script_analysis(arabicW, &sa);
1840 memset(tags, 0, sizeof(tags));
1841 count = 0;
1842 hr = IDWriteTextAnalyzer2_GetTypographicFeatures(analyzer2, fontface, sa, NULL, ARRAY_SIZE(tags), &count, tags);
1843 ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
1844 ok(!!count, "Unexpected count %u.\n", count);
1846 get_script_analysis(L"abc", &sa);
1847 memset(tags, 0, sizeof(tags));
1848 count = 0;
1849 hr = IDWriteTextAnalyzer2_GetTypographicFeatures(analyzer2, fontface, sa, NULL, ARRAY_SIZE(tags), &count, tags);
1850 ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
1851 ok(!!count, "Unexpected count %u.\n", count);
1853 IDWriteFontFace_Release(fontface);
1854 IDWriteTextAnalyzer2_Release(analyzer2);
1857 static void test_GetGlyphPlacements(void)
1859 DWRITE_SHAPING_GLYPH_PROPERTIES glyphprops[2];
1860 DWRITE_SHAPING_TEXT_PROPERTIES textprops[2];
1861 static const WCHAR aW[] = {'A','D',0};
1862 UINT16 clustermap[2], glyphs[2];
1863 DWRITE_GLYPH_OFFSET offsets[2];
1864 IDWriteTextAnalyzer *analyzer;
1865 IDWriteFontFace *fontface;
1866 DWRITE_SCRIPT_ANALYSIS sa;
1867 FLOAT advances[2];
1868 UINT32 count, len;
1869 WCHAR *path;
1870 HRESULT hr;
1872 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
1873 ok(hr == S_OK, "got 0x%08x\n", hr);
1875 path = create_testfontfile(L"wine_test_font.ttf");
1876 fontface = create_testfontface(path);
1878 get_script_analysis(aW, &sa);
1879 count = 0;
1880 len = lstrlenW(aW);
1881 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, aW, len, fontface, FALSE, FALSE, &sa, NULL,
1882 NULL, NULL, NULL, 0, len, clustermap, textprops, glyphs, glyphprops, &count);
1883 ok(hr == S_OK, "got 0x%08x\n", hr);
1884 ok(count == 2, "got %u\n", count);
1886 /* just return on zero glyphs */
1887 advances[0] = advances[1] = 1.0;
1888 offsets[0].advanceOffset = offsets[0].ascenderOffset = 2.0;
1889 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, clustermap, textprops,
1890 len, glyphs, glyphprops, 0, fontface, 0.0, FALSE, FALSE, &sa, NULL, NULL,
1891 NULL, 0, advances, offsets);
1892 ok(hr == S_OK, "got 0x%08x\n", hr);
1893 ok(advances[0] == 1.0, "got %.2f\n", advances[0]);
1894 ok(offsets[0].advanceOffset == 2.0 && offsets[0].ascenderOffset == 2.0, "got %.2f,%.2f\n",
1895 offsets[0].advanceOffset, offsets[0].ascenderOffset);
1897 /* advances/offsets are scaled with provided font emSize and designed eM box size */
1898 advances[0] = advances[1] = 1.0;
1899 memset(offsets, 0xcc, sizeof(offsets));
1900 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, clustermap, textprops,
1901 len, glyphs, glyphprops, len, fontface, 0.0, FALSE, FALSE, &sa, NULL, NULL,
1902 NULL, 0, advances, offsets);
1903 ok(hr == S_OK, "got 0x%08x\n", hr);
1904 ok(advances[0] == 0.0, "got %.2f\n", advances[0]);
1905 ok(offsets[0].advanceOffset == 0.0 && offsets[0].ascenderOffset == 0.0, "got %.2f,%.2f\n",
1906 offsets[0].advanceOffset, offsets[0].ascenderOffset);
1908 advances[0] = advances[1] = 1.0;
1909 memset(offsets, 0xcc, sizeof(offsets));
1910 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, clustermap, textprops,
1911 len, glyphs, glyphprops, len, fontface, 2048.0, FALSE, FALSE, &sa, NULL, NULL,
1912 NULL, 0, advances, offsets);
1913 ok(hr == S_OK, "got 0x%08x\n", hr);
1914 ok(advances[0] == 1000.0, "got %.2f\n", advances[0]);
1915 ok(offsets[0].advanceOffset == 0.0 && offsets[0].ascenderOffset == 0.0, "got %.2f,%.2f\n",
1916 offsets[0].advanceOffset, offsets[0].ascenderOffset);
1918 advances[0] = advances[1] = 1.0;
1919 memset(offsets, 0xcc, sizeof(offsets));
1920 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, clustermap, textprops,
1921 len, glyphs, glyphprops, len, fontface, 1024.0, FALSE, FALSE, &sa, NULL, NULL,
1922 NULL, 0, advances, offsets);
1923 ok(hr == S_OK, "got 0x%08x\n", hr);
1924 ok(advances[0] == 500.0, "got %.2f\n", advances[0]);
1925 ok(advances[1] == 500.0, "got %.2f\n", advances[1]);
1926 ok(offsets[0].advanceOffset == 0.0 && offsets[0].ascenderOffset == 0.0, "got %.2f,%.2f\n",
1927 offsets[0].advanceOffset, offsets[0].ascenderOffset);
1929 advances[0] = advances[1] = 1.0;
1930 memset(offsets, 0xcc, sizeof(offsets));
1931 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, clustermap, textprops,
1932 len, glyphs, glyphprops, len, fontface, 20.48, FALSE, FALSE, &sa, NULL, NULL,
1933 NULL, 0, advances, offsets);
1934 ok(hr == S_OK, "got 0x%08x\n", hr);
1935 ok(advances[0] == 10.0, "got %.2f\n", advances[0]);
1936 ok(advances[1] == 10.0, "got %.2f\n", advances[1]);
1937 ok(offsets[0].advanceOffset == 0.0 && offsets[0].ascenderOffset == 0.0, "got %.2f,%.2f\n",
1938 offsets[0].advanceOffset, offsets[0].ascenderOffset);
1940 /* without clustermap */
1941 advances[0] = advances[1] = 1.0;
1942 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, NULL, textprops,
1943 len, glyphs, glyphprops, len, fontface, 1024.0, FALSE, FALSE, &sa, NULL, NULL,
1944 NULL, 0, advances, offsets);
1945 ok(hr == S_OK, "got 0x%08x\n", hr);
1946 ok(advances[0] == 500.0, "got %.2f\n", advances[0]);
1947 ok(advances[1] == 500.0, "got %.2f\n", advances[1]);
1949 /* it's happy to use negative size too */
1950 advances[0] = advances[1] = 1.0;
1951 memset(offsets, 0xcc, sizeof(offsets));
1952 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, clustermap, textprops,
1953 len, glyphs, glyphprops, len, fontface, -10.24, FALSE, FALSE, &sa, NULL, NULL,
1954 NULL, 0, advances, offsets);
1955 ok(hr == S_OK, "got 0x%08x\n", hr);
1956 ok(advances[0] == -5.0, "got %.2f\n", advances[0]);
1957 ok(offsets[0].advanceOffset == 0.0 && offsets[0].ascenderOffset == 0.0, "got %.2f,%.2f\n",
1958 offsets[0].advanceOffset, offsets[0].ascenderOffset);
1960 /* DWRITE_SCRIPT_SHAPES_NO_VISUAL has no effect on placement */
1961 sa.shapes = DWRITE_SCRIPT_SHAPES_NO_VISUAL;
1962 advances[0] = advances[1] = 1.0f;
1963 memset(offsets, 0xcc, sizeof(offsets));
1964 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, clustermap, textprops,
1965 len, glyphs, glyphprops, len, fontface, 2048.0f, FALSE, FALSE, &sa, NULL, NULL,
1966 NULL, 0, advances, offsets);
1967 ok(hr == S_OK, "got 0x%08x\n", hr);
1968 ok(advances[0] == 1000.0f, "got %.2f\n", advances[0]);
1969 ok(advances[1] == 1000.0f, "got %.2f\n", advances[1]);
1970 ok(offsets[0].advanceOffset == 0.0f && offsets[0].ascenderOffset == 0.0f, "got %.2f,%.2f\n",
1971 offsets[0].advanceOffset, offsets[0].ascenderOffset);
1973 /* isZeroWidthSpace */
1974 sa.shapes = DWRITE_SCRIPT_SHAPES_DEFAULT;
1975 advances[0] = advances[1] = 1.0f;
1976 memset(offsets, 0xcc, sizeof(offsets));
1977 glyphprops[0].isZeroWidthSpace = 1;
1978 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, clustermap, textprops,
1979 len, glyphs, glyphprops, len, fontface, 2048.0f, FALSE, FALSE, &sa, NULL, NULL,
1980 NULL, 0, advances, offsets);
1981 ok(hr == S_OK, "got 0x%08x\n", hr);
1982 ok(advances[0] == 0.0f, "got %.2f\n", advances[0]);
1983 ok(advances[1] == 1000.0f, "got %.2f\n", advances[1]);
1984 ok(offsets[0].advanceOffset == 0.0f && offsets[0].ascenderOffset == 0.0f, "got %.2f,%.2f\n",
1985 offsets[0].advanceOffset, offsets[0].ascenderOffset);
1987 IDWriteTextAnalyzer_Release(analyzer);
1988 IDWriteFontFace_Release(fontface);
1989 DELETE_FONTFILE(path);
1992 struct spacing_test {
1993 FLOAT leading;
1994 FLOAT trailing;
1995 FLOAT min_advance;
1996 FLOAT advances[3];
1997 FLOAT offsets[3];
1998 FLOAT modified_advances[3];
1999 FLOAT modified_offsets[3];
2000 BOOL single_cluster;
2001 DWRITE_SHAPING_GLYPH_PROPERTIES props[3];
2004 static const struct spacing_test spacing_tests[] =
2006 /* Default spacing glyph properties. */
2007 #define P_S { 0 }
2008 /* isZeroWidthSpace */
2009 #define P_Z { 0, 0, 0, 1, 0 }
2010 /* isDiacritic */
2011 #define P_D { 0, 0, 1, 0, 0 }
2012 /* isDiacritic + isZeroWidthSpace, that's how diacritics are shaped. */
2013 #define P_D_Z { 0, 0, 1, 1, 0 }
2015 { 0.0, 0.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 } }, /* 0 */
2016 { 0.0, 0.0, 2.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 } },
2017 { 1.0, 0.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 11.0, 12.0 }, { 3.0, 4.0 } },
2018 { 1.0, 1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 12.0, 13.0 }, { 3.0, 4.0 } },
2019 { 1.0, -1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 3.0, 4.0 } },
2020 { 0.0, -10.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 0.0, 1.0 }, { 2.0, 3.0 } }, /* 5 */
2021 { -5.0, -4.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 5.0, 5.0 }, { -1.0, -0.5 } },
2022 { -5.0, -5.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 5.0, 5.0 }, { -0.5, 0.0 } },
2023 { 2.0, 0.0, 5.0, { 1.0, 2.0 }, { 2.0, 3.0 }, { 7.0, 7.0 }, { 6.0, 6.5 } },
2024 { 2.0, 1.0, 5.0, { 1.0, 2.0 }, { 2.0, 3.0 }, { 8.0, 8.0 }, { 6.0, 6.5 } },
2025 { 2.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 7.0, 7.0 }, { 4.0, 5.0 } }, /* 10 */
2026 { 1.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 6.0, 6.0 }, { 3.0, 4.0 } },
2027 { -10.0, 1.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 6.0, 6.0 }, { -3.0, -3.0 } },
2028 { 0.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 5.0, 5.0 }, { 2.0, 3.0 } },
2029 { 1.0, -10.0, -5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 0.0, 0.0 }, { 2.0, 3.0 } },
2030 { -10.0, 1.0, 5.0, { 8.0, 11.0 }, { 2.0, 3.0 }, { 6.0, 6.0 }, { -1.0, -3.0 } }, /* 15 */
2031 /* cluster of more than 1 glyph */
2032 { 0.0f, 0.0f, 0.0f, { 10.0f, 11.0f }, { 2.0f, 3.0f }, { 10.0f, 11.0f }, { 2.0f, 3.0f }, TRUE },
2033 { 1.0f, 0.0f, 0.0f, { 10.0f, 11.0f }, { 2.0f, 3.5f }, { 11.0f, 11.0f }, { 3.0f, 3.5f }, TRUE },
2034 { 1.0f, 1.0f, 0.0f, { 10.0f, 11.0f }, { 2.0f, 3.0f }, { 11.0f, 12.0f }, { 3.0f, 3.0f }, TRUE },
2035 { 1.0, -1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 11.0, 10.0 }, { 3.0, 3.0 }, TRUE },
2036 { 0.0, -10.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 1.0 }, { 2.0, 3.0 }, TRUE }, /* 20 */
2037 { 0.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 1.0 }, { 2.0, 3.0 }, TRUE },
2038 { 1.0, -10.0, -5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 0.0, 0.0 }, { 2.0, 3.0 }, TRUE },
2039 { -5.0, -10.0, 4.0, { 10.0, 11.0, 12.0 }, { 2.0, 3.0, 4.0 }, { 5.0, 11.0, 2.0 }, { -3.0, 3.0, 4.0 }, TRUE },
2040 { -10.0, -10.0, 4.0, { 10.0, 11.0, 12.0 }, { 2.0, 3.0, 4.0 }, { 0.0, 11.0, 2.0 }, { -8.0, 3.0, 4.0 }, TRUE },
2041 { -10.0, -10.0, 5.0, { 10.0, 1.0, 12.0 }, { 2.0, 3.0, 4.0 }, { 1.0, 1.0, 3.0 }, { -7.0, 3.0, 4.0 }, TRUE }, /* 25 */
2042 { -10.0, 1.0, 5.0, { 10.0, 1.0, 2.0 }, { 2.0, 3.0, 4.0 }, { 2.0, 1.0, 3.0 }, { -6.0, 3.0, 4.0 }, TRUE },
2043 { 1.0, -10.0, 5.0, { 2.0, 1.0, 10.0 }, { 2.0, 3.0, 4.0 }, { 3.0, 1.0, 2.0 }, { 3.0, 3.0, 4.0 }, TRUE },
2044 { -10.0, -10.0, 5.0, { 11.0, 1.0, 11.0 }, { 2.0, 3.0, 4.0 }, { 2.0, 1.0, 2.0 }, { -7.0, 3.0, 4.0 }, TRUE },
2045 /* isZeroWidthSpace set */
2046 { 1.0, 0.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 12.0 }, { 2.0, 4.0 }, FALSE, { P_Z, P_S } },
2047 { 1.0, 1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 13.0 }, { 2.0, 4.0 }, FALSE, { P_Z, P_S } }, /* 30 */
2048 { 1.0, -1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 3.0, 3.0 }, FALSE, { P_S, P_Z } },
2049 { 0.0, -10.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 1.0 }, { 2.0, 3.0 }, FALSE, { P_Z, P_S } },
2050 { -5.0, -4.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 5.0, 11.0 }, { -1.0, 3.0 }, FALSE, { P_S, P_Z } },
2051 { -5.0, -5.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 }, FALSE, { P_Z, P_Z } },
2052 { 2.0, 0.0, 5.0, { 1.0, 2.0 }, { 2.0, 3.0 }, { 7.0, 2.0 }, { 6.0, 3.0 }, FALSE, { P_S, P_Z } }, /* 35 */
2053 { 2.0, 1.0, 5.0, { 1.0, 2.0 }, { 2.0, 3.0 }, { 8.0, 2.0 }, { 6.0, 3.0 }, FALSE, { P_S, P_Z } },
2054 { 2.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 }, FALSE, { P_Z, P_Z } },
2055 { 1.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 6.0, 11.0 }, { 3.0, 3.0 }, FALSE, { P_S, P_Z } },
2056 { -10.0, 1.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 }, FALSE, { P_Z, P_Z } },
2057 { 0.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 5.0, 11.0 }, { 2.0, 3.0 }, FALSE, { P_S, P_Z } }, /* 40 */
2058 { 1.0, -10.0, -5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 0.0, 0.0 }, { 2.0, 3.0 }, FALSE, { P_Z, P_S } },
2059 { -10.0, 1.0, 5.0, { 8.0, 11.0 }, { 2.0, 3.0 }, { 6.0, 11.0 }, { -1.0, 3.0 }, FALSE, { P_S, P_Z } },
2060 /* isDiacritic */
2061 { 1.0, 0.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 11.0, 12.0 }, { 3.0, 4.0 }, FALSE, { P_D, P_S } },
2062 { 1.0, 1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 12.0, 13.0 }, { 3.0, 4.0 }, FALSE, { P_D, P_S } },
2063 { 1.0, -1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 3.0, 4.0 }, FALSE, { P_S, P_D } }, /* 45 */
2064 { 0.0, -10.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 0.0, 1.0 }, { 2.0, 3.0 }, FALSE, { P_D, P_S } },
2065 { -5.0, -4.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 5.0, 5.0 }, { -1.0, -0.5 }, FALSE, { P_S, P_D } },
2066 { -5.0, -5.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 5.0, 5.0 }, { -0.5, 0.0 }, FALSE, { P_D, P_D } },
2067 { 2.0, 0.0, 5.0, { 1.0, 2.0 }, { 2.0, 3.0 }, { 7.0, 7.0 }, { 6.0, 6.5 }, FALSE, { P_S, P_D } },
2068 { 2.0, 1.0, 5.0, { 1.0, 2.0 }, { 2.0, 3.0 }, { 8.0, 8.0 }, { 6.0, 6.5 }, FALSE, { P_S, P_D } }, /* 50 */
2069 { 2.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 7.0, 7.0 }, { 4.0, 5.0 }, FALSE, { P_D, P_D } },
2070 { 1.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 6.0, 6.0 }, { 3.0, 4.0 }, FALSE, { P_S, P_D } },
2071 { -10.0, 1.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 6.0, 6.0 }, { -3.0, -3.0 }, FALSE, { P_D, P_D } },
2072 { 0.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 5.0, 5.0 }, { 2.0, 3.0 }, FALSE, { P_S, P_D } },
2073 { 1.0, -10.0, -5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 0.0, 0.0 }, { 2.0, 3.0 }, FALSE, { P_D, P_S } }, /* 55 */
2074 { -10.0, 1.0, 5.0, { 8.0, 11.0 }, { 2.0, 3.0 }, { 6.0, 6.0 }, { -1.0, -3.0 }, FALSE, { P_S, P_D } },
2075 /* isZeroWidthSpace in a cluster */
2076 { 1.0, 0.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 12.0 }, { 3.0, 4.0 }, TRUE, { P_Z, P_S } },
2077 { 1.0, 1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 13.0 }, { 3.0, 4.0 }, TRUE, { P_Z, P_S } },
2078 { 1.0, -1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 3.0, 4.0 }, TRUE, { P_S, P_Z } },
2079 { 0.0, -10.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 1.0 }, { 2.0, 3.0 }, TRUE, { P_Z, P_S } }, /* 60 */
2080 { -5.0, -4.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 1.0, 11.0 }, { -3.0, 7.0 }, TRUE, { P_S, P_Z } },
2081 { -5.0, -5.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 }, TRUE, { P_Z, P_Z } },
2082 { 0.0, 0.0, 5.0, { 1.0, 2.0 }, { 2.0, 3.0 }, { 3.0, 2.0 }, { 3.0, 2.0 }, TRUE, { P_S, P_Z } },
2083 { 0.0, 0.0, 5.0, { 1.0, 2.0, 1.0 }, { 2.0, 3.0, 5.0 }, { 1.5, 2.0, 1.5 }, { 2.5, 3.0, 5.0 }, TRUE, { P_S, P_Z, P_S } },
2084 { 0.0, 0.0, 5.0, { 1.0, 2.0, 1.0 }, { 2.0, 3.0, 5.0 }, { 1.5, 2.5, 1.0 }, { 2.5, 3.0, 4.5 }, TRUE, { P_S, P_S, P_Z } },
2085 { 0.0, 0.0, 5.0, { 1.0, 2.0, 1.0 }, { 2.0, 3.0, 5.0 }, { 2.0, 2.0, 1.0 }, { 2.5, 2.5, 4.5 }, TRUE, { P_S, P_Z, P_Z } },
2086 { 0.0, 0.0, 5.0, { 1.0, 2.0, 1.0 }, { 2.0, 3.0, 5.0 }, { 1.0, 2.0, 1.0 }, { 2.0, 3.0, 5.0 }, TRUE, { P_Z, P_Z, P_Z } },
2087 { 2.0, 1.0, 1.0, { 1.0, 2.0, 3.0 }, { 2.0, 3.0, 4.0 }, { 3.0, 2.0, 4.0 }, { 4.0, 3.0, 4.0 }, TRUE, { P_S, P_Z, P_S } },
2088 { 0.0, 0.0, 5.0, { 1.0, 2.0 }, { 2.0, 3.0 }, { 3.0, 2.0 }, { 3.0, 2.0 }, TRUE, { P_S, P_Z } },
2089 { 0.0, 0.0, 5.0, { 1.0, 2.0, 6.0 }, { 2.0, 3.0, 4.0 }, { 1.0, 2.0, 6.0 }, { 2.0, 3.0, 4.0 }, TRUE, { P_S, P_Z, P_S } },
2090 { 2.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 }, TRUE, { P_Z, P_Z } }, /* 65 */
2091 { 1.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 1.0, 11.0 }, { 3.0, 13.0 }, TRUE, { P_S, P_Z } },
2092 { -10.0, 1.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 }, TRUE, { P_Z, P_Z } },
2093 { 0.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 0.0, 11.0 }, { 2.0, 13.0 }, TRUE, { P_S, P_Z } },
2094 { 1.0, -10.0, -5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 0.0, 0.0 }, { 2.0, 3.0 }, TRUE, { P_Z, P_S } },
2095 { -10.0, 1.0, 5.0, { 8.0, 11.0 }, { 2.0, 3.0 }, { -1.0, 11.0 }, { -8.0, 2.0 }, TRUE, { P_S, P_Z } }, /* 70 */
2096 /* isDiacritic in a cluster */
2097 { 1.0, 0.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 11.0, 11.0 }, { 3.0, 3.0 }, TRUE, { P_D, P_S } },
2098 { 1.0, 1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 11.0, 12.0 }, { 3.0, 3.0 }, TRUE, { P_D, P_S } },
2099 { 1.0, -1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 11.0, 10.0 }, { 3.0, 3.0 }, TRUE, { P_S, P_D } },
2100 { 0.0, -10.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 1.0 }, { 2.0, 3.0 }, TRUE, { P_D, P_S } },
2101 { -5.0, -4.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 5.0, 7.0 }, { -3.0, 3.0 }, TRUE, { P_S, P_D } }, /* 75 */
2102 { -5.0, -5.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 5.0, 6.0 }, { -3.0, 3.0 }, TRUE, { P_D, P_D } },
2103 { 2.0, 0.0, 5.0, { 1.0, 2.0 }, { 2.0, 3.0 }, { 4.0, 3.0 }, { 5.0, 3.0 }, TRUE, { P_S, P_D } },
2104 { 2.0, 1.0, 5.0, { 1.0, 2.0 }, { 2.0, 3.0 }, { 4.0, 4.0 }, { 5.0, 3.0 }, TRUE, { P_S, P_D } },
2105 { 2.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 12.0, 1.0 }, { 4.0, 3.0 }, TRUE, { P_D, P_D } },
2106 { 1.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 11.0, 1.0 }, { 3.0, 3.0 }, TRUE, { P_S, P_D } }, /* 80 */
2107 { -10.0, 1.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 0.0, 12.0 }, { -8.0, 3.0 }, TRUE, { P_D, P_D } },
2108 { 0.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 1.0 }, { 2.0, 3.0 }, TRUE, { P_S, P_D } },
2109 { 1.0, -10.0, -5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 0.0, 0.0 }, { 2.0, 3.0 }, TRUE, { P_D, P_S } },
2110 { -10.0, 1.0, 5.0, { 8.0, 11.0 }, { 2.0, 3.0 }, { -2.0, 12.0 }, { -8.0, 3.0 }, TRUE, { P_S, P_D } },
2111 /* isZeroWidthSpace + isDiacritic */
2112 { 1.0, 0.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 12.0 }, { 2.0, 4.0 }, FALSE, { P_D_Z, P_S } }, /* 85 */
2113 { 1.0, 1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 13.0 }, { 2.0, 4.0 }, FALSE, { P_D_Z, P_S } },
2114 { 1.0, -1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 3.0, 3.0 }, FALSE, { P_S, P_D_Z } },
2115 { 0.0, -10.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 1.0 }, { 2.0, 3.0 }, FALSE, { P_D_Z, P_S } },
2116 { -5.0, -4.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 5.0, 11.0 }, { -1.0, 3.0 }, FALSE, { P_S, P_D_Z } },
2117 { -5.0, -5.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 }, FALSE, { P_D_Z, P_D_Z } }, /* 90 */
2118 { 2.0, 0.0, 5.0, { 1.0, 2.0 }, { 2.0, 3.0 }, { 7.0, 2.0 }, { 6.0, 3.0 }, FALSE, { P_S, P_D_Z } },
2119 { 2.0, 1.0, 5.0, { 1.0, 2.0 }, { 2.0, 3.0 }, { 8.0, 2.0 }, { 6.0, 3.0 }, FALSE, { P_S, P_D_Z } },
2120 { 2.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 }, FALSE, { P_D_Z, P_D_Z } },
2121 { 1.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 6.0, 11.0 }, { 3.0, 3.0 }, FALSE, { P_S, P_D_Z } },
2122 { -10.0, 1.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 }, FALSE, { P_D_Z, P_D_Z } }, /* 95 */
2123 { 0.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 5.0, 11.0 }, { 2.0, 3.0 }, FALSE, { P_S, P_D_Z } },
2124 { 1.0, -10.0, -5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 0.0, 0.0 }, { 2.0, 3.0 }, FALSE, { P_D_Z, P_S } },
2125 { -10.0, 1.0, 5.0, { 8.0, 11.0 }, { 2.0, 3.0 }, { 6.0, 11.0 }, { -1.0, 3.0 }, FALSE, { P_S, P_D_Z } },
2126 /* isZeroWidthSpace + isDiacritic in a cluster */
2127 { 1.0, 0.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 12.0 }, { 3.0, 4.0 }, TRUE, { P_D_Z, P_S } },
2128 { 1.0, 1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 13.0 }, { 3.0, 4.0 }, TRUE, { P_D_Z, P_S } }, /* 100 */
2129 { 1.0, -1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 3.0, 4.0 }, TRUE, { P_S, P_D_Z } },
2130 { 0.0, -10.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 1.0 }, { 2.0, 3.0 }, TRUE, { P_D_Z, P_S } },
2131 { -5.0, -4.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 1.0, 11.0 }, { -3.0, 7.0 }, TRUE, { P_S, P_D_Z } },
2132 { -5.0, -5.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 }, TRUE, { P_D_Z, P_D_Z } },
2133 { 2.0, 0.0, 5.0, { 1.0, 2.0 }, { 2.0, 3.0 }, { 5.0, 2.0 }, { 5.0, 2.0 }, TRUE, { P_S, P_D_Z } }, /* 105 */
2134 { 2.0, 1.0, 5.0, { 1.0, 2.0 }, { 2.0, 3.0 }, { 6.0, 2.0 }, { 5.0, 1.0 }, TRUE, { P_S, P_D_Z } },
2135 { 2.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 }, TRUE, { P_D_Z, P_D_Z } },
2136 { 1.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 1.0, 11.0 }, { 3.0, 13.0 }, TRUE, { P_S, P_D_Z } },
2137 { -10.0, 1.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 }, TRUE, { P_D_Z, P_D_Z } },
2138 { 0.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 0.0, 11.0 }, { 2.0, 13.0 }, TRUE, { P_S, P_D_Z } }, /* 110 */
2139 { 1.0, -10.0, -5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 0.0, 0.0 }, { 2.0, 3.0 }, TRUE, { P_D_Z, P_S } },
2140 { -10.0, 1.0, 5.0, { 8.0, 11.0 }, { 2.0, 3.0 }, { -1.0, 11.0 }, { -8.0, 2.0 }, TRUE, { P_S, P_D_Z } },
2142 #undef P_S
2143 #undef P_D
2144 #undef P_Z
2145 #undef P_D_Z
2148 static void test_ApplyCharacterSpacing(void)
2150 DWRITE_SHAPING_GLYPH_PROPERTIES props[3];
2151 IDWriteTextAnalyzer1 *analyzer1;
2152 IDWriteTextAnalyzer *analyzer;
2153 UINT16 clustermap[2];
2154 HRESULT hr;
2155 int i;
2157 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
2158 ok(hr == S_OK, "got 0x%08x\n", hr);
2160 hr = IDWriteTextAnalyzer_QueryInterface(analyzer, &IID_IDWriteTextAnalyzer1, (void**)&analyzer1);
2161 IDWriteTextAnalyzer_Release(analyzer);
2162 if (hr != S_OK) {
2163 win_skip("ApplyCharacterSpacing() is not supported.\n");
2164 return;
2167 for (i = 0; i < ARRAY_SIZE(spacing_tests); i++) {
2168 const struct spacing_test *ptr = spacing_tests + i;
2169 DWRITE_GLYPH_OFFSET offsets[3];
2170 UINT32 glyph_count;
2171 FLOAT advances[3];
2173 offsets[0].advanceOffset = ptr->offsets[0];
2174 offsets[1].advanceOffset = ptr->offsets[1];
2175 offsets[2].advanceOffset = ptr->offsets[2];
2176 /* Ascender offsets are never touched as spacing applies in reading direction only,
2177 we'll only test them to see if they are not changed */
2178 offsets[0].ascenderOffset = 23.0;
2179 offsets[1].ascenderOffset = 32.0;
2180 offsets[2].ascenderOffset = 31.0;
2182 advances[0] = advances[1] = 123.45f;
2183 memcpy(props, ptr->props, sizeof(props));
2184 glyph_count = ptr->advances[2] > 0.0 ? 3 : 2;
2185 if (ptr->single_cluster)
2187 clustermap[0] = 0;
2188 clustermap[1] = 0;
2189 props[0].isClusterStart = 1;
2191 else
2193 /* trivial case with one glyph per cluster */
2194 clustermap[0] = 0;
2195 clustermap[1] = 1;
2196 props[0].isClusterStart = props[1].isClusterStart = 1;
2199 hr = IDWriteTextAnalyzer1_ApplyCharacterSpacing(analyzer1,
2200 ptr->leading,
2201 ptr->trailing,
2202 ptr->min_advance,
2203 ARRAY_SIZE(clustermap),
2204 glyph_count,
2205 clustermap,
2206 ptr->advances,
2207 offsets,
2208 props,
2209 advances,
2210 offsets);
2211 ok(hr == (ptr->min_advance < 0.0f ? E_INVALIDARG : S_OK), "%d: got 0x%08x\n", i, hr);
2213 if (hr == S_OK) {
2214 ok(ptr->modified_advances[0] == advances[0], "%d: got advance[0] %.2f, expected %.2f\n", i, advances[0], ptr->modified_advances[0]);
2215 ok(ptr->modified_advances[1] == advances[1], "%d: got advance[1] %.2f, expected %.2f\n", i, advances[1], ptr->modified_advances[1]);
2216 if (glyph_count > 2)
2217 ok(ptr->modified_advances[2] == advances[2], "%d: got advance[2] %.2f, expected %.2f\n", i, advances[2], ptr->modified_advances[2]);
2219 ok(ptr->modified_offsets[0] == offsets[0].advanceOffset, "%d: got offset[0] %.2f, expected %.2f\n", i,
2220 offsets[0].advanceOffset, ptr->modified_offsets[0]);
2221 ok(ptr->modified_offsets[1] == offsets[1].advanceOffset, "%d: got offset[1] %.2f, expected %.2f\n", i,
2222 offsets[1].advanceOffset, ptr->modified_offsets[1]);
2223 if (glyph_count > 2)
2224 ok(ptr->modified_offsets[2] == offsets[2].advanceOffset, "%d: got offset[2] %.2f, expected %.2f\n", i,
2225 offsets[2].advanceOffset, ptr->modified_offsets[2]);
2227 ok(offsets[0].ascenderOffset == 23.0, "%d: unexpected ascenderOffset %.2f\n", i, offsets[0].ascenderOffset);
2228 ok(offsets[1].ascenderOffset == 32.0, "%d: unexpected ascenderOffset %.2f\n", i, offsets[1].ascenderOffset);
2229 ok(offsets[2].ascenderOffset == 31.0, "%d: unexpected ascenderOffset %.2f\n", i, offsets[2].ascenderOffset);
2231 else {
2232 ok(ptr->modified_advances[0] == advances[0], "%d: got advance[0] %.2f, expected %.2f\n", i, advances[0], ptr->modified_advances[0]);
2233 ok(ptr->modified_advances[1] == advances[1], "%d: got advance[1] %.2f, expected %.2f\n", i, advances[1], ptr->modified_advances[1]);
2234 ok(ptr->offsets[0] == offsets[0].advanceOffset, "%d: got offset[0] %.2f, expected %.2f\n", i,
2235 offsets[0].advanceOffset, ptr->modified_offsets[0]);
2236 ok(ptr->offsets[1] == offsets[1].advanceOffset, "%d: got offset[1] %.2f, expected %.2f\n", i,
2237 offsets[1].advanceOffset, ptr->modified_offsets[1]);
2238 ok(offsets[0].ascenderOffset == 23.0, "%d: unexpected ascenderOffset %.2f\n", i, offsets[0].ascenderOffset);
2239 ok(offsets[1].ascenderOffset == 32.0, "%d: unexpected ascenderOffset %.2f\n", i, offsets[1].ascenderOffset);
2242 /* same, with argument aliasing */
2243 memcpy(advances, ptr->advances, glyph_count * sizeof(*advances));
2244 offsets[0].advanceOffset = ptr->offsets[0];
2245 offsets[1].advanceOffset = ptr->offsets[1];
2246 offsets[2].advanceOffset = ptr->offsets[2];
2247 /* Ascender offsets are never touched as spacing applies in reading direction only,
2248 we'll only test them to see if they are not changed */
2249 offsets[0].ascenderOffset = 23.0f;
2250 offsets[1].ascenderOffset = 32.0f;
2251 offsets[2].ascenderOffset = 31.0f;
2253 hr = IDWriteTextAnalyzer1_ApplyCharacterSpacing(analyzer1,
2254 ptr->leading,
2255 ptr->trailing,
2256 ptr->min_advance,
2257 ARRAY_SIZE(clustermap),
2258 glyph_count,
2259 clustermap,
2260 advances,
2261 offsets,
2262 props,
2263 advances,
2264 offsets);
2265 ok(hr == (ptr->min_advance < 0.0f ? E_INVALIDARG : S_OK), "%d: got 0x%08x\n", i, hr);
2267 if (hr == S_OK) {
2268 ok(ptr->modified_advances[0] == advances[0], "%d: got advance[0] %.2f, expected %.2f\n", i, advances[0], ptr->modified_advances[0]);
2269 ok(ptr->modified_advances[1] == advances[1], "%d: got advance[1] %.2f, expected %.2f\n", i, advances[1], ptr->modified_advances[1]);
2270 if (glyph_count > 2)
2271 ok(ptr->modified_advances[2] == advances[2], "%d: got advance[2] %.2f, expected %.2f\n", i, advances[2], ptr->modified_advances[2]);
2273 ok(ptr->modified_offsets[0] == offsets[0].advanceOffset, "%d: got offset[0] %.2f, expected %.2f\n", i,
2274 offsets[0].advanceOffset, ptr->modified_offsets[0]);
2275 ok(ptr->modified_offsets[1] == offsets[1].advanceOffset, "%d: got offset[1] %.2f, expected %.2f\n", i,
2276 offsets[1].advanceOffset, ptr->modified_offsets[1]);
2277 if (glyph_count > 2)
2278 ok(ptr->modified_offsets[2] == offsets[2].advanceOffset, "%d: got offset[2] %.2f, expected %.2f\n", i,
2279 offsets[2].advanceOffset, ptr->modified_offsets[2]);
2281 ok(offsets[0].ascenderOffset == 23.0f, "%d: unexpected ascenderOffset %.2f\n", i, offsets[0].ascenderOffset);
2282 ok(offsets[1].ascenderOffset == 32.0f, "%d: unexpected ascenderOffset %.2f\n", i, offsets[1].ascenderOffset);
2283 ok(offsets[2].ascenderOffset == 31.0f, "%d: unexpected ascenderOffset %.2f\n", i, offsets[2].ascenderOffset);
2285 else {
2286 /* with aliased advances original values are retained */
2287 ok(ptr->advances[0] == advances[0], "%d: got advance[0] %.2f, expected %.2f\n", i, advances[0], ptr->advances[0]);
2288 ok(ptr->advances[1] == advances[1], "%d: got advance[1] %.2f, expected %.2f\n", i, advances[1], ptr->advances[1]);
2289 ok(ptr->offsets[0] == offsets[0].advanceOffset, "%d: got offset[0] %.2f, expected %.2f\n", i,
2290 offsets[0].advanceOffset, ptr->modified_offsets[0]);
2291 ok(ptr->offsets[1] == offsets[1].advanceOffset, "%d: got offset[1] %.2f, expected %.2f\n", i,
2292 offsets[1].advanceOffset, ptr->modified_offsets[1]);
2293 ok(offsets[0].ascenderOffset == 23.0f, "%d: unexpected ascenderOffset %.2f\n", i, offsets[0].ascenderOffset);
2294 ok(offsets[1].ascenderOffset == 32.0f, "%d: unexpected ascenderOffset %.2f\n", i, offsets[1].ascenderOffset);
2298 IDWriteTextAnalyzer1_Release(analyzer1);
2301 struct orientation_transf_test {
2302 DWRITE_GLYPH_ORIENTATION_ANGLE angle;
2303 BOOL is_sideways;
2304 DWRITE_MATRIX m;
2307 static const struct orientation_transf_test ot_tests[] = {
2308 { DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES, FALSE, { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 } },
2309 { DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES, FALSE, { 0.0, 1.0, -1.0, 0.0, 0.0, 0.0 } },
2310 { DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES, FALSE, { -1.0, 0.0, 0.0, -1.0, 0.0, 0.0 } },
2311 { DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES, FALSE, { 0.0, -1.0, 1.0, 0.0, 0.0, 0.0 } },
2312 { DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES, TRUE, { 0.0, 1.0, -1.0, 0.0, 0.0, 0.0 } },
2313 { DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES, TRUE, { -1.0, 0.0, 0.0, -1.0, 0.0, 0.0 } },
2314 { DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES, TRUE, { 0.0, -1.0, 1.0, 0.0, 0.0, 0.0 } },
2315 { DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES, TRUE, { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 } }
2318 static inline const char *dbgstr_matrix(const DWRITE_MATRIX *m)
2320 static char buff[64];
2321 sprintf(buff, "{%.2f, %.2f, %.2f, %.2f, %.2f, %.2f}", m->m11, m->m12,
2322 m->m21, m->m22, m->dx, m->dy);
2323 return buff;
2326 static void test_GetGlyphOrientationTransform(void)
2328 IDWriteTextAnalyzer2 *analyzer2;
2329 IDWriteTextAnalyzer1 *analyzer1;
2330 IDWriteTextAnalyzer *analyzer;
2331 FLOAT originx, originy;
2332 DWRITE_MATRIX m;
2333 HRESULT hr;
2334 int i;
2336 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
2337 ok(hr == S_OK, "got 0x%08x\n", hr);
2339 hr = IDWriteTextAnalyzer_QueryInterface(analyzer, &IID_IDWriteTextAnalyzer1, (void**)&analyzer1);
2340 IDWriteTextAnalyzer_Release(analyzer);
2341 if (hr != S_OK) {
2342 win_skip("GetGlyphOrientationTransform() is not supported.\n");
2343 return;
2346 /* invalid angle value */
2347 memset(&m, 0xcc, sizeof(m));
2348 hr = IDWriteTextAnalyzer1_GetGlyphOrientationTransform(analyzer1,
2349 DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES + 1, FALSE, &m);
2350 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
2351 ok(m.m11 == 0.0, "got %.2f\n", m.m11);
2353 for (i = 0; i < ARRAY_SIZE(ot_tests); i++) {
2354 memset(&m, 0, sizeof(m));
2355 hr = IDWriteTextAnalyzer1_GetGlyphOrientationTransform(analyzer1, ot_tests[i].angle,
2356 ot_tests[i].is_sideways, &m);
2357 ok(hr == S_OK, "got 0x%08x\n", hr);
2358 ok(!memcmp(&ot_tests[i].m, &m, sizeof(m)), "%d: wrong matrix %s\n", i, dbgstr_matrix(&m));
2361 hr = IDWriteTextAnalyzer1_QueryInterface(analyzer1, &IID_IDWriteTextAnalyzer2, (void**)&analyzer2);
2362 IDWriteTextAnalyzer1_Release(analyzer1);
2363 if (hr != S_OK) {
2364 win_skip("IDWriteTextAnalyzer2::GetGlyphOrientationTransform() is not supported.\n");
2365 return;
2368 /* invalid angle value */
2369 memset(&m, 0xcc, sizeof(m));
2370 hr = IDWriteTextAnalyzer2_GetGlyphOrientationTransform(analyzer2,
2371 DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES + 1, FALSE, 0.0, 0.0, &m);
2372 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
2373 ok(m.m11 == 0.0, "got %.2f\n", m.m11);
2375 originx = 50.0;
2376 originy = 60.0;
2377 for (i = 0; i < ARRAY_SIZE(ot_tests); i++) {
2378 DWRITE_GLYPH_ORIENTATION_ANGLE angle = DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES;
2379 DWRITE_MATRIX m_exp;
2381 memset(&m, 0, sizeof(m));
2383 /* zero offset gives same result as a call from IDWriteTextAnalyzer1 */
2384 hr = IDWriteTextAnalyzer2_GetGlyphOrientationTransform(analyzer2, ot_tests[i].angle,
2385 ot_tests[i].is_sideways, 0.0, 0.0, &m);
2386 ok(hr == S_OK, "got 0x%08x\n", hr);
2387 ok(!memcmp(&ot_tests[i].m, &m, sizeof(m)), "%d: wrong matrix %s\n", i, dbgstr_matrix(&m));
2389 m_exp = ot_tests[i].m;
2390 hr = IDWriteTextAnalyzer2_GetGlyphOrientationTransform(analyzer2, ot_tests[i].angle,
2391 ot_tests[i].is_sideways, originx, originy, &m);
2392 ok(hr == S_OK, "got 0x%08x\n", hr);
2394 /* 90 degrees more for sideways */
2395 if (ot_tests[i].is_sideways) {
2396 switch (ot_tests[i].angle)
2398 case DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES:
2399 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES;
2400 break;
2401 case DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES:
2402 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES;
2403 break;
2404 case DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES:
2405 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES;
2406 break;
2407 case DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES:
2408 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES;
2409 break;
2410 default:
2414 else
2415 angle = ot_tests[i].angle;
2417 /* set expected offsets */
2418 switch (angle)
2420 case DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES:
2421 break;
2422 case DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES:
2423 m_exp.dx = originx + originy;
2424 m_exp.dy = originy - originx;
2425 break;
2426 case DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES:
2427 m_exp.dx = originx + originx;
2428 m_exp.dy = originy + originy;
2429 break;
2430 case DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES:
2431 m_exp.dx = originx - originy;
2432 m_exp.dy = originy + originx;
2433 break;
2434 default:
2438 ok(!memcmp(&m_exp, &m, sizeof(m)), "%d: wrong matrix %s\n", i, dbgstr_matrix(&m));
2441 IDWriteTextAnalyzer2_Release(analyzer2);
2444 static void test_GetBaseline(void)
2446 DWRITE_SCRIPT_ANALYSIS sa = { 0 };
2447 IDWriteTextAnalyzer1 *analyzer1;
2448 IDWriteTextAnalyzer *analyzer;
2449 IDWriteFontFace *fontface;
2450 INT32 baseline;
2451 BOOL exists;
2452 HRESULT hr;
2454 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
2455 ok(hr == S_OK, "got 0x%08x\n", hr);
2457 hr = IDWriteTextAnalyzer_QueryInterface(analyzer, &IID_IDWriteTextAnalyzer1, (void**)&analyzer1);
2458 IDWriteTextAnalyzer_Release(analyzer);
2459 if (hr != S_OK) {
2460 win_skip("GetBaseline() is not supported.\n");
2461 return;
2464 fontface = create_fontface();
2466 /* Tahoma does not have a BASE table. */
2468 exists = TRUE;
2469 baseline = 456;
2470 hr = IDWriteTextAnalyzer1_GetBaseline(analyzer1, fontface, DWRITE_BASELINE_DEFAULT, FALSE,
2471 TRUE, sa, NULL, &baseline, &exists);
2472 ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
2473 ok(!baseline, "Unexpected baseline %d.\n", baseline);
2474 ok(!exists, "Unexpected flag %d.\n", exists);
2476 exists = TRUE;
2477 baseline = 456;
2478 hr = IDWriteTextAnalyzer1_GetBaseline(analyzer1, fontface, DWRITE_BASELINE_DEFAULT, FALSE,
2479 FALSE, sa, NULL, &baseline, &exists);
2480 ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
2481 ok(!baseline, "Unexpected baseline %d.\n", baseline);
2482 ok(!exists, "Unexpected flag %d.\n", exists);
2484 exists = TRUE;
2485 baseline = 0;
2486 hr = IDWriteTextAnalyzer1_GetBaseline(analyzer1, fontface, DWRITE_BASELINE_CENTRAL, FALSE,
2487 TRUE, sa, NULL, &baseline, &exists);
2488 ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
2489 ok(baseline != 0, "Unexpected baseline %d.\n", baseline);
2490 ok(!exists, "Unexpected flag %d.\n", exists);
2492 exists = TRUE;
2493 baseline = 0;
2494 hr = IDWriteTextAnalyzer1_GetBaseline(analyzer1, fontface, DWRITE_BASELINE_CENTRAL, FALSE,
2495 FALSE, sa, NULL, &baseline, &exists);
2496 ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
2497 ok(!baseline, "Unexpected baseline %d.\n", baseline);
2498 ok(!exists, "Unexpected flag %d.\n", exists);
2500 exists = TRUE;
2501 baseline = 456;
2502 hr = IDWriteTextAnalyzer1_GetBaseline(analyzer1, fontface, DWRITE_BASELINE_DEFAULT + 100, FALSE,
2503 TRUE, sa, NULL, &baseline, &exists);
2504 ok(hr == E_INVALIDARG, "Unexpected hr %#x.\n", hr);
2505 ok(!baseline, "Unexpected baseline %d.\n", baseline);
2506 ok(!exists, "Unexpected flag %d.\n", exists);
2508 IDWriteFontFace_Release(fontface);
2509 IDWriteTextAnalyzer1_Release(analyzer1);
2512 static inline BOOL float_eq(FLOAT left, FLOAT right)
2514 int x = *(int *)&left;
2515 int y = *(int *)&right;
2517 if (x < 0)
2518 x = INT_MIN - x;
2519 if (y < 0)
2520 y = INT_MIN - y;
2522 return abs(x - y) <= 8;
2525 static void test_GetGdiCompatibleGlyphPlacements(void)
2527 DWRITE_SHAPING_GLYPH_PROPERTIES glyphprops[1];
2528 DWRITE_SHAPING_TEXT_PROPERTIES textprops[1];
2529 DWRITE_SCRIPT_ANALYSIS sa = { 0 };
2530 IDWriteTextAnalyzer *analyzer;
2531 IDWriteFontFace *fontface;
2532 UINT16 clustermap[1];
2533 HRESULT hr;
2534 UINT32 count;
2535 UINT16 glyphs[1];
2536 FLOAT advance;
2537 DWRITE_GLYPH_OFFSET offsets[1];
2538 DWRITE_FONT_METRICS fontmetrics;
2539 float emsize;
2541 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
2542 ok(hr == S_OK, "got 0x%08x\n", hr);
2544 fontface = create_fontface();
2546 IDWriteFontFace_GetMetrics(fontface, &fontmetrics);
2548 count = 0;
2549 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, L"A", 1, fontface, FALSE, FALSE, &sa, NULL, NULL, NULL, NULL, 0, 1,
2550 clustermap, textprops, glyphs, glyphprops, &count);
2551 ok(hr == S_OK, "Failed to get glyphs, hr %#x.\n", hr);
2552 ok(count == 1, "got %u\n", count);
2554 for (emsize = 12.0f; emsize <= 20.0f; emsize += 1.0f)
2556 FLOAT compatadvance, expected, ppdip;
2557 DWRITE_GLYPH_METRICS metrics;
2559 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, L"A", clustermap, textprops, 1, glyphs, glyphprops,
2560 count, fontface, emsize, FALSE, FALSE, &sa, NULL, NULL, NULL, 0, &advance, offsets);
2561 ok(hr == S_OK, "Failed to get glyph placements, hr %#x.\n", hr);
2562 ok(advance > 0.0f, "Unexpected advance %f.\n", advance);
2564 /* 1 ppdip, no transform */
2565 ppdip = 1.0;
2566 hr = IDWriteFontFace_GetGdiCompatibleGlyphMetrics(fontface, emsize, ppdip, NULL, FALSE,
2567 glyphs, 1, &metrics, FALSE);
2568 ok(hr == S_OK, "got 0x%08x\n", hr);
2570 expected = floorf(metrics.advanceWidth * emsize * ppdip / fontmetrics.designUnitsPerEm + 0.5f) / ppdip;
2571 hr = IDWriteTextAnalyzer_GetGdiCompatibleGlyphPlacements(analyzer, L"A", clustermap, textprops, 1, glyphs,
2572 glyphprops, count, fontface, emsize, ppdip, NULL, FALSE, FALSE, FALSE, &sa, NULL, NULL, NULL, 0,
2573 &compatadvance, offsets);
2574 ok(hr == S_OK, "Failed to get glyph placements, hr %#x.\n", hr);
2575 ok(compatadvance == expected, "%.0f: got advance %f, expected %f, natural %f\n", emsize,
2576 compatadvance, expected, advance);
2578 /* 1.2 ppdip, no transform */
2579 ppdip = 1.2f;
2580 hr = IDWriteFontFace_GetGdiCompatibleGlyphMetrics(fontface, emsize, ppdip, NULL, FALSE,
2581 glyphs, 1, &metrics, FALSE);
2582 ok(hr == S_OK, "got 0x%08x\n", hr);
2584 expected = floorf(metrics.advanceWidth * emsize * ppdip / fontmetrics.designUnitsPerEm + 0.5f) / ppdip;
2585 hr = IDWriteTextAnalyzer_GetGdiCompatibleGlyphPlacements(analyzer, L"A", clustermap, textprops, 1, glyphs,
2586 glyphprops, count, fontface, emsize, ppdip, NULL, FALSE, FALSE, FALSE, &sa, NULL, NULL, NULL, 0,
2587 &compatadvance, offsets);
2588 ok(hr == S_OK, "Failed to get glyph placements, hr %#x.\n", hr);
2589 ok(float_eq(compatadvance, expected), "%.0f: got advance %f, expected %f, natural %f\n", emsize,
2590 compatadvance, expected, advance);
2593 IDWriteFontFace_Release(fontface);
2594 IDWriteTextAnalyzer_Release(analyzer);
2597 struct bidi_test
2599 const WCHAR text[BIDI_LEVELS_COUNT];
2600 DWRITE_READING_DIRECTION direction;
2601 UINT8 explicit[BIDI_LEVELS_COUNT];
2602 UINT8 resolved[BIDI_LEVELS_COUNT];
2603 BOOL todo;
2606 static const struct bidi_test bidi_tests[] = {
2608 { 0x645, 0x6cc, 0x200c, 0x6a9, 0x646, 0x645, 0 },
2609 DWRITE_READING_DIRECTION_RIGHT_TO_LEFT,
2610 { 1, 1, 1, 1, 1, 1 },
2611 { 1, 1, 1, 1, 1, 1 }
2614 { 0x645, 0x6cc, 0x200c, 0x6a9, 0x646, 0x645, 0 },
2615 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2616 { 0, 0, 0, 0, 0, 0 },
2617 { 1, 1, 1, 1, 1, 1 }
2620 { 0x200c, 0x645, 0x6cc, 0x6a9, 0x646, 0x645, 0 },
2621 DWRITE_READING_DIRECTION_RIGHT_TO_LEFT,
2622 { 1, 1, 1, 1, 1, 1 },
2623 { 1, 1, 1, 1, 1, 1 }
2626 { 0x200c, 0x645, 0x6cc, 0x6a9, 0x646, 0x645, 0 },
2627 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2628 { 0, 0, 0, 0, 0, 0 },
2629 { 0, 1, 1, 1, 1, 1 }
2632 { 'A', 0x200c, 'B', 0 },
2633 DWRITE_READING_DIRECTION_RIGHT_TO_LEFT,
2634 { 1, 1, 1 },
2635 { 2, 2, 2 }
2638 { 'A', 0x200c, 'B', 0 },
2639 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2640 { 0, 0, 0 },
2641 { 0, 0, 0 }
2644 { LRE, PDF, 'a', 'b', 0 },
2645 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2646 { 2, 2, 0, 0 },
2647 { 0, 0, 0, 0 },
2650 { 'a', LRE, PDF, 'b', 0 },
2651 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2652 { 0, 2, 2, 0 },
2653 { 0, 0, 0, 0 },
2656 { RLE, PDF, 'a', 'b', 0 },
2657 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2658 { 1, 1, 0, 0 },
2659 { 0, 0, 0, 0 },
2662 { 'a', RLE, PDF, 'b', 0 },
2663 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2664 { 0, 1, 1, 0 },
2665 { 0, 0, 0, 0 },
2668 { 'a', RLE, PDF, 'b', 0 },
2669 DWRITE_READING_DIRECTION_RIGHT_TO_LEFT,
2670 { 1, 3, 3, 1 },
2671 { 2, 2, 2, 2 },
2674 { LRE, PDF, 'a', 'b', 0 },
2675 DWRITE_READING_DIRECTION_RIGHT_TO_LEFT,
2676 { 2, 2, 1, 1 },
2677 { 1, 1, 2, 2 },
2680 { PDF, 'a', 'b', 0 },
2681 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2682 { 0, 0, 0, 0 },
2683 { 0, 0, 0, 0 }
2686 { LRE, 'a', 'b', PDF, 0 },
2687 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2688 { 2, 2, 2, 2 },
2689 { 0, 2, 2, 2 },
2690 TRUE
2693 { LRI, 'a', 'b', PDI, 0 },
2694 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2695 { 0, 0, 0, 0 },
2696 { 0, 0, 0, 0 },
2697 TRUE
2700 { RLI, 'a', 'b', PDI, 0 },
2701 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2702 { 0, 0, 0, 0 },
2703 { 0, 0, 0, 0 },
2704 TRUE
2707 { 0 }
2711 static void compare_bidi_levels(unsigned int seq, const struct bidi_test *test, UINT32 len, UINT8 *explicit, UINT8 *resolved)
2713 unsigned int i, failcount = 0;
2714 BOOL match;
2716 match = !memcmp(explicit, test->explicit, len);
2717 if (!match) {
2718 if (test->todo) {
2719 failcount++;
2720 todo_wine
2721 ok(0, "test %u: %s wrong explicit levels:\n", seq, wine_dbgstr_w(test->text));
2723 else
2724 ok(0, "test %u: %s wrong explicit levels:\n", seq, wine_dbgstr_w(test->text));
2726 for (i = 0; i < len; i++) {
2727 if (test->explicit[i] != explicit[i]) {
2728 if (test->todo) {
2729 failcount++;
2730 todo_wine
2731 ok(0, "\tat %u, explicit level %u, expected %u\n", i, explicit[i], test->explicit[i]);
2733 else
2734 ok(0, "\tat %u, explicit level %u, expected %u\n", i, explicit[i], test->explicit[i]);
2739 match = !memcmp(resolved, test->resolved, len);
2740 if (!match) {
2741 if (test->todo) {
2742 failcount++;
2743 todo_wine
2744 ok(0, "test %u: %s wrong resolved levels:\n", seq, wine_dbgstr_w(test->text));
2746 else
2747 ok(0, "test %u: %s wrong resolved levels:\n", seq, wine_dbgstr_w(test->text));
2749 for (i = 0; i < len; i++) {
2750 if (test->resolved[i] != resolved[i]) {
2751 if (test->todo) {
2752 failcount++;
2753 todo_wine
2754 ok(0, "\tat %u, resolved level %u, expected %u\n", i, resolved[i], test->resolved[i]);
2756 else
2757 ok(0, "\tat %u, resolved level %u, expected %u\n", i, resolved[i], test->resolved[i]);
2762 todo_wine_if(test->todo && failcount == 0)
2763 ok(1, "test %u: marked as \"todo_wine\" but succeeds\n", seq);
2766 static void test_AnalyzeBidi(void)
2768 const struct bidi_test *ptr = bidi_tests;
2769 IDWriteTextAnalyzer *analyzer;
2770 UINT32 i = 0;
2771 HRESULT hr;
2773 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
2774 ok(hr == S_OK, "got 0x%08x\n", hr);
2776 while (*ptr->text)
2778 UINT32 len;
2780 init_textsource(&analysissource, ptr->text, ptr->direction);
2782 len = lstrlenW(ptr->text);
2783 if (len > BIDI_LEVELS_COUNT) {
2784 ok(0, "test %u: increase BIDI_LEVELS_COUNT to at least %u\n", i, len);
2785 i++;
2786 ptr++;
2787 continue;
2790 memset(g_explicit_levels, 0, sizeof(g_explicit_levels));
2791 memset(g_resolved_levels, 0, sizeof(g_resolved_levels));
2792 hr = IDWriteTextAnalyzer_AnalyzeBidi(analyzer, &analysissource.IDWriteTextAnalysisSource_iface, 0,
2793 len, &analysissink);
2794 ok(hr == S_OK, "%u: got 0x%08x\n", i, hr);
2795 compare_bidi_levels(i, ptr, len, g_explicit_levels, g_resolved_levels);
2797 i++;
2798 ptr++;
2801 IDWriteTextAnalyzer_Release(analyzer);
2804 START_TEST(analyzer)
2806 HRESULT hr;
2808 hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_ISOLATED, &IID_IDWriteFactory, (IUnknown**)&factory);
2809 ok(hr == S_OK, "got 0x%08x\n", hr);
2810 if (hr != S_OK)
2812 win_skip("failed to create factory\n");
2813 return;
2816 init_call_sequences(sequences, NUM_CALL_SEQUENCES);
2817 init_call_sequences(expected_seq, 1);
2819 test_AnalyzeScript();
2820 test_AnalyzeLineBreakpoints();
2821 test_AnalyzeBidi();
2822 test_GetScriptProperties();
2823 test_GetTextComplexity();
2824 test_GetGlyphs();
2825 test_numbersubstitution();
2826 test_GetTypographicFeatures();
2827 test_GetGlyphPlacements();
2828 test_ApplyCharacterSpacing();
2829 test_GetGlyphOrientationTransform();
2830 test_GetBaseline();
2831 test_GetGdiCompatibleGlyphPlacements();
2833 IDWriteFactory_Release(factory);