dwrite/tests: Use ARRAY_SIZE() macro in tests.
[wine.git] / dlls / dwrite / tests / analyzer.c
blob93ee152c23c0a7b0f72a78f13c70cd7e60906e55
1 /*
2 * Text analyzing tests
4 * Copyright 2012-2014 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 "dwrite_3.h"
32 #include "wine/test.h"
34 #define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
36 static IDWriteFactory *factory;
37 static const WCHAR test_fontfile[] = {'w','i','n','e','_','t','e','s','t','_','f','o','n','t','.','t','t','f',0};
39 #define LRE 0x202a
40 #define RLE 0x202b
41 #define PDF 0x202c
42 #define LRO 0x202d
43 #define RLO 0x202e
44 #define LRI 0x2066
45 #define RLI 0x2067
46 #define FSI 0x2068
47 #define PDI 0x2069
49 enum analysis_kind {
50 ScriptAnalysis,
51 LastKind
54 static const char *get_analysis_kind_name(enum analysis_kind kind)
56 switch (kind)
58 case ScriptAnalysis:
59 return "ScriptAnalysis";
60 default:
61 return "unknown";
65 struct script_analysis {
66 UINT32 pos;
67 UINT32 len;
68 DWRITE_SCRIPT_SHAPES shapes;
71 struct call_entry {
72 enum analysis_kind kind;
73 struct script_analysis sa;
76 struct testcontext {
77 enum analysis_kind kind;
78 BOOL todo;
79 int *failcount;
80 const char *file;
81 int line;
84 struct call_sequence
86 int count;
87 int size;
88 struct call_entry *sequence;
91 #define NUM_CALL_SEQUENCES 1
92 #define ANALYZER_ID 0
93 static struct call_sequence *sequences[NUM_CALL_SEQUENCES];
94 static struct call_sequence *expected_seq[1];
96 static void add_call(struct call_sequence **seq, int sequence_index, const struct call_entry *call)
98 struct call_sequence *call_seq = seq[sequence_index];
100 if (!call_seq->sequence)
102 call_seq->size = 10;
103 call_seq->sequence = HeapAlloc(GetProcessHeap(), 0,
104 call_seq->size * sizeof (struct call_entry));
107 if (call_seq->count == call_seq->size)
109 call_seq->size *= 2;
110 call_seq->sequence = HeapReAlloc(GetProcessHeap(), 0,
111 call_seq->sequence,
112 call_seq->size * sizeof (struct call_entry));
115 assert(call_seq->sequence);
117 call_seq->sequence[call_seq->count++] = *call;
120 static inline void flush_sequence(struct call_sequence **seg, int sequence_index)
122 struct call_sequence *call_seq = seg[sequence_index];
124 HeapFree(GetProcessHeap(), 0, call_seq->sequence);
125 call_seq->sequence = NULL;
126 call_seq->count = call_seq->size = 0;
129 static void init_call_sequences(struct call_sequence **seq, int n)
131 int i;
133 for (i = 0; i < n; i++)
134 seq[i] = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct call_sequence));
137 static void test_uint(UINT32 actual, UINT32 expected, const char *name, const struct testcontext *ctxt)
139 if (expected != actual && ctxt->todo)
141 (*ctxt->failcount)++;
142 ok_(ctxt->file, ctxt->line) (0, "%s: \"%s\" expecting %u, got %u\n", get_analysis_kind_name(ctxt->kind), name, expected, actual);
144 else
145 ok_(ctxt->file, ctxt->line) (expected == actual, "%s: \"%s\" expecting %u, got %u\n", get_analysis_kind_name(ctxt->kind), name,
146 expected, actual);
149 static void ok_sequence_(struct call_sequence **seq, int sequence_index,
150 const struct call_entry *expected, const char *context, BOOL todo,
151 const char *file, int line)
153 struct call_sequence *call_seq = seq[sequence_index];
154 static const struct call_entry end_of_sequence = { LastKind };
155 const struct call_entry *actual, *sequence;
156 int failcount = 0;
157 struct testcontext ctxt;
159 add_call(seq, sequence_index, &end_of_sequence);
161 sequence = call_seq->sequence;
162 actual = sequence;
164 ctxt.failcount = &failcount;
165 ctxt.todo = todo;
166 ctxt.file = file;
167 ctxt.line = line;
169 while (expected->kind != LastKind && actual->kind != LastKind)
171 if (expected->kind == actual->kind)
173 ctxt.kind = expected->kind;
175 switch (actual->kind)
177 case ScriptAnalysis:
178 test_uint(actual->sa.pos, expected->sa.pos, "position", &ctxt);
179 test_uint(actual->sa.len, expected->sa.len, "length", &ctxt);
180 test_uint(actual->sa.shapes, expected->sa.shapes, "shapes", &ctxt);
181 break;
182 default:
183 ok(0, "%s: callback not handled, %s\n", context, get_analysis_kind_name(actual->kind));
185 expected++;
186 actual++;
188 else if (todo)
190 failcount++;
191 todo_wine
193 ok_(file, line) (0, "%s: call %s was expected, but got call %s instead\n",
194 context, get_analysis_kind_name(expected->kind), get_analysis_kind_name(actual->kind));
197 flush_sequence(seq, sequence_index);
198 return;
200 else
202 ok_(file, line) (0, "%s: call %s was expected, but got call %s instead\n",
203 context, get_analysis_kind_name(expected->kind), get_analysis_kind_name(actual->kind));
204 expected++;
205 actual++;
209 if (todo)
211 todo_wine
213 if (expected->kind != LastKind || actual->kind != LastKind)
215 failcount++;
216 ok_(file, line) (0, "%s: the call sequence is not complete: expected %s - actual %s\n",
217 context, get_analysis_kind_name(expected->kind), get_analysis_kind_name(actual->kind));
221 else if (expected->kind != LastKind || actual->kind != LastKind)
223 ok_(file, line) (0, "%s: the call sequence is not complete: expected %s - actual %s\n",
224 context, get_analysis_kind_name(expected->kind), get_analysis_kind_name(actual->kind));
227 if (todo && !failcount) /* succeeded yet marked todo */
229 todo_wine
231 ok_(file, line)(1, "%s: marked \"todo_wine\" but succeeds\n", context);
235 flush_sequence(seq, sequence_index);
238 #define ok_sequence(seq, index, exp, contx, todo) \
239 ok_sequence_(seq, index, (exp), (contx), (todo), __FILE__, __LINE__)
241 static HRESULT WINAPI analysissink_QueryInterface(IDWriteTextAnalysisSink *iface, REFIID riid, void **obj)
243 if (IsEqualIID(riid, &IID_IDWriteTextAnalysisSink) || IsEqualIID(riid, &IID_IUnknown))
245 *obj = iface;
246 return S_OK;
249 *obj = NULL;
250 return E_NOINTERFACE;
253 static ULONG WINAPI analysissink_AddRef(IDWriteTextAnalysisSink *iface)
255 return 2;
258 static ULONG WINAPI analysissink_Release(IDWriteTextAnalysisSink *iface)
260 return 1;
263 static HRESULT WINAPI analysissink_SetScriptAnalysis(IDWriteTextAnalysisSink *iface,
264 UINT32 position, UINT32 length, DWRITE_SCRIPT_ANALYSIS const* sa)
266 struct call_entry entry;
267 entry.kind = ScriptAnalysis;
268 entry.sa.pos = position;
269 entry.sa.len = length;
270 entry.sa.shapes = sa->shapes;
271 add_call(sequences, ANALYZER_ID, &entry);
272 return S_OK;
275 static DWRITE_SCRIPT_ANALYSIS g_sa;
276 static HRESULT WINAPI analysissink_SetScriptAnalysis2(IDWriteTextAnalysisSink *iface,
277 UINT32 position, UINT32 length, DWRITE_SCRIPT_ANALYSIS const* sa)
279 g_sa = *sa;
280 return S_OK;
283 #define BREAKPOINT_COUNT 20
284 static DWRITE_LINE_BREAKPOINT g_actual_bp[BREAKPOINT_COUNT];
286 static HRESULT WINAPI analysissink_SetLineBreakpoints(IDWriteTextAnalysisSink *iface,
287 UINT32 position,
288 UINT32 length,
289 DWRITE_LINE_BREAKPOINT const* breakpoints)
291 if (position + length > BREAKPOINT_COUNT) {
292 ok(0, "SetLineBreakpoints: reported pos=%u, len=%u overflows expected length %d\n", position, length, BREAKPOINT_COUNT);
293 return E_FAIL;
295 memcpy(&g_actual_bp[position], breakpoints, length*sizeof(DWRITE_LINE_BREAKPOINT));
296 return S_OK;
299 #define BIDI_LEVELS_COUNT 10
300 static UINT8 g_explicit_levels[BIDI_LEVELS_COUNT];
301 static UINT8 g_resolved_levels[BIDI_LEVELS_COUNT];
302 static HRESULT WINAPI analysissink_SetBidiLevel(IDWriteTextAnalysisSink *iface,
303 UINT32 position,
304 UINT32 length,
305 UINT8 explicitLevel,
306 UINT8 resolvedLevel)
308 if (position + length > BIDI_LEVELS_COUNT) {
309 ok(0, "SetBidiLevel: reported pos=%u, len=%u overflows expected length %d\n", position, length, BIDI_LEVELS_COUNT);
310 return E_FAIL;
312 memset(g_explicit_levels + position, explicitLevel, length);
313 memset(g_resolved_levels + position, resolvedLevel, length);
314 return S_OK;
317 static HRESULT WINAPI analysissink_SetNumberSubstitution(IDWriteTextAnalysisSink *iface,
318 UINT32 position,
319 UINT32 length,
320 IDWriteNumberSubstitution* substitution)
322 ok(0, "unexpected\n");
323 return E_NOTIMPL;
326 static IDWriteTextAnalysisSinkVtbl analysissinkvtbl = {
327 analysissink_QueryInterface,
328 analysissink_AddRef,
329 analysissink_Release,
330 analysissink_SetScriptAnalysis,
331 analysissink_SetLineBreakpoints,
332 analysissink_SetBidiLevel,
333 analysissink_SetNumberSubstitution
336 static IDWriteTextAnalysisSinkVtbl analysissinkvtbl2 = {
337 analysissink_QueryInterface,
338 analysissink_AddRef,
339 analysissink_Release,
340 analysissink_SetScriptAnalysis2,
341 analysissink_SetLineBreakpoints,
342 analysissink_SetBidiLevel,
343 analysissink_SetNumberSubstitution
346 static IDWriteTextAnalysisSink analysissink = { &analysissinkvtbl };
347 static IDWriteTextAnalysisSink analysissink2 = { &analysissinkvtbl2 };
349 static HRESULT WINAPI analysissource_QueryInterface(IDWriteTextAnalysisSource *iface,
350 REFIID riid, void **obj)
352 ok(0, "QueryInterface not expected\n");
353 return E_NOTIMPL;
356 static ULONG WINAPI analysissource_AddRef(IDWriteTextAnalysisSource *iface)
358 ok(0, "AddRef not expected\n");
359 return 2;
362 static ULONG WINAPI analysissource_Release(IDWriteTextAnalysisSource *iface)
364 ok(0, "Release not expected\n");
365 return 1;
368 struct testanalysissource
370 IDWriteTextAnalysisSource IDWriteTextAnalysisSource_iface;
371 const WCHAR *text;
372 UINT32 text_length;
373 DWRITE_READING_DIRECTION direction;
376 static void init_textsource(struct testanalysissource *source, const WCHAR *text,
377 DWRITE_READING_DIRECTION direction)
379 source->text = text;
380 source->text_length = lstrlenW(text);
381 source->direction = direction;
384 static inline struct testanalysissource *impl_from_IDWriteTextAnalysisSource(IDWriteTextAnalysisSource *iface)
386 return CONTAINING_RECORD(iface, struct testanalysissource, IDWriteTextAnalysisSource_iface);
389 static HRESULT WINAPI analysissource_GetTextAtPosition(IDWriteTextAnalysisSource *iface,
390 UINT32 position, WCHAR const** text, UINT32* text_len)
392 struct testanalysissource *source = impl_from_IDWriteTextAnalysisSource(iface);
394 if (position >= source->text_length)
396 *text = NULL;
397 *text_len = 0;
399 else
401 *text = source->text + position;
402 *text_len = source->text_length - position;
405 return S_OK;
408 static HRESULT WINAPI analysissource_GetTextBeforePosition(IDWriteTextAnalysisSource *iface,
409 UINT32 position, WCHAR const** text, UINT32* text_len)
411 ok(0, "unexpected\n");
412 return E_NOTIMPL;
415 static DWRITE_READING_DIRECTION WINAPI analysissource_GetParagraphReadingDirection(
416 IDWriteTextAnalysisSource *iface)
418 struct testanalysissource *source = impl_from_IDWriteTextAnalysisSource(iface);
419 return source->direction;
422 static HRESULT WINAPI analysissource_GetLocaleName(IDWriteTextAnalysisSource *iface,
423 UINT32 position, UINT32* text_len, WCHAR const** locale)
425 *locale = NULL;
426 *text_len = 0;
427 return S_OK;
430 static HRESULT WINAPI analysissource_GetNumberSubstitution(IDWriteTextAnalysisSource *iface,
431 UINT32 position, UINT32* text_len, IDWriteNumberSubstitution **substitution)
433 ok(0, "unexpected\n");
434 return E_NOTIMPL;
437 static IDWriteTextAnalysisSourceVtbl analysissourcevtbl = {
438 analysissource_QueryInterface,
439 analysissource_AddRef,
440 analysissource_Release,
441 analysissource_GetTextAtPosition,
442 analysissource_GetTextBeforePosition,
443 analysissource_GetParagraphReadingDirection,
444 analysissource_GetLocaleName,
445 analysissource_GetNumberSubstitution
448 static struct testanalysissource analysissource = { { &analysissourcevtbl } };
450 static IDWriteFontFace *create_fontface(void)
452 static const WCHAR tahomaW[] = {'T','a','h','o','m','a',0};
453 IDWriteGdiInterop *interop;
454 IDWriteFontFace *fontface;
455 IDWriteFont *font;
456 LOGFONTW logfont;
457 HRESULT hr;
459 hr = IDWriteFactory_GetGdiInterop(factory, &interop);
460 ok(hr == S_OK, "got 0x%08x\n", hr);
462 memset(&logfont, 0, sizeof(logfont));
463 logfont.lfHeight = 12;
464 logfont.lfWidth = 12;
465 logfont.lfWeight = FW_NORMAL;
466 logfont.lfItalic = 1;
467 lstrcpyW(logfont.lfFaceName, tahomaW);
469 hr = IDWriteGdiInterop_CreateFontFromLOGFONT(interop, &logfont, &font);
470 ok(hr == S_OK, "got 0x%08x\n", hr);
472 hr = IDWriteFont_CreateFontFace(font, &fontface);
473 ok(hr == S_OK, "got 0x%08x\n", hr);
475 IDWriteFont_Release(font);
476 IDWriteGdiInterop_Release(interop);
478 return fontface;
481 static WCHAR *create_testfontfile(const WCHAR *filename)
483 static WCHAR pathW[MAX_PATH];
484 DWORD written;
485 HANDLE file;
486 HRSRC res;
487 void *ptr;
489 GetTempPathW(ARRAY_SIZE(pathW), pathW);
490 lstrcatW(pathW, filename);
492 file = CreateFileW(pathW, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0);
493 ok(file != INVALID_HANDLE_VALUE, "file creation failed, at %s, error %d\n", wine_dbgstr_w(pathW),
494 GetLastError());
496 res = FindResourceA(GetModuleHandleA(NULL), (LPCSTR)MAKEINTRESOURCE(1), (LPCSTR)RT_RCDATA);
497 ok(res != 0, "couldn't find resource\n");
498 ptr = LockResource(LoadResource(GetModuleHandleA(NULL), res));
499 WriteFile(file, ptr, SizeofResource(GetModuleHandleA(NULL), res), &written, NULL);
500 ok(written == SizeofResource(GetModuleHandleA(NULL), res), "couldn't write resource\n");
501 CloseHandle(file);
503 return pathW;
506 #define DELETE_FONTFILE(filename) _delete_testfontfile(filename, __LINE__)
507 static void _delete_testfontfile(const WCHAR *filename, int line)
509 BOOL ret = DeleteFileW(filename);
510 ok_(__FILE__,line)(ret, "failed to delete file %s, error %d\n", wine_dbgstr_w(filename), GetLastError());
513 static IDWriteFontFace *create_testfontface(const WCHAR *filename)
515 IDWriteFontFace *face;
516 IDWriteFontFile *file;
517 HRESULT hr;
519 hr = IDWriteFactory_CreateFontFileReference(factory, filename, NULL, &file);
520 ok(hr == S_OK, "got 0x%08x\n",hr);
522 hr = IDWriteFactory_CreateFontFace(factory, DWRITE_FONT_FACE_TYPE_TRUETYPE, 1, &file, 0,
523 DWRITE_FONT_SIMULATIONS_NONE, &face);
524 ok(hr == S_OK, "got 0x%08x\n", hr);
525 IDWriteFontFile_Release(file);
527 return face;
530 struct sa_test {
531 const WCHAR string[50];
532 int item_count;
533 struct script_analysis sa[10];
536 static struct sa_test sa_tests[] = {
538 /* just 1 char string */
539 {'t',0}, 1,
540 { { 0, 1, DWRITE_SCRIPT_SHAPES_DEFAULT }}
543 {'t','e','s','t',0}, 1,
544 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT }}
547 {' ',' ',' ',' ','!','$','[','^','{','~',0}, 1,
548 { { 0, 10, DWRITE_SCRIPT_SHAPES_DEFAULT }}
551 {' ',' ',' ','1','2',' ',0}, 1,
552 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
555 /* digits only */
556 {'1','2',0}, 1,
557 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }}
560 /* Arabic */
561 {0x064a,0x064f,0x0633,0x0627,0x0648,0x0650,0x064a,0x0661,0}, 1,
562 { { 0, 8, DWRITE_SCRIPT_SHAPES_DEFAULT }}
565 /* Arabic */
566 {0x0627,0x0644,0x0635,0x0651,0x0650,0x062d,0x0629,0x064f,' ',0x062a,0x064e,
567 0x0627,0x062c,0x064c,' ',0x0639,0x064e,0x0644,0x0649,' ',
568 0x0631,0x064f,0x0624,0x0648,0x0633,0x0650,' ',0x0627,0x0644,
569 0x0623,0x0635,0x0650,0x062d,0x0651,0x064e,0x0627,0x0621,0x0650,0x06f0,0x06f5,0}, 1,
570 { { 0, 40, DWRITE_SCRIPT_SHAPES_DEFAULT }}
573 /* Arabic, Latin */
574 {'1','2','3','-','5','2',0x064a,0x064f,0x0633,0x0627,0x0648,0x0650,0x064a,'7','1','.',0}, 1,
575 { { 0, 16, DWRITE_SCRIPT_SHAPES_DEFAULT }}
578 /* Arabic, English */
579 {'A','B','C','-','D','E','F',' ',0x0621,0x0623,0x0624,0}, 2,
580 { { 0, 8, DWRITE_SCRIPT_SHAPES_DEFAULT },
581 { 8, 3, DWRITE_SCRIPT_SHAPES_DEFAULT },
585 /* leading space, Arabic, English */
586 {' ',0x0621,0x0623,0x0624,'A','B','C','-','D','E','F',0}, 2,
587 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT },
588 { 4, 7, DWRITE_SCRIPT_SHAPES_DEFAULT },
592 /* English, Arabic, trailing space */
593 {'A','B','C','-','D','E','F',0x0621,0x0623,0x0624,' ',0}, 2,
594 { { 0, 7, DWRITE_SCRIPT_SHAPES_DEFAULT },
595 { 7, 4, DWRITE_SCRIPT_SHAPES_DEFAULT },
599 /* C1 Controls, Latin-1 Supplement */
600 {0x80,0x90,0x9f,0xa0,0xc0,0xb8,0xbf,0xc0,0xff,0}, 2,
601 { { 0, 3, DWRITE_SCRIPT_SHAPES_NO_VISUAL },
602 { 3, 6, DWRITE_SCRIPT_SHAPES_DEFAULT },
606 /* Latin Extended-A */
607 {0x100,0x120,0x130,0x140,0x150,0x160,0x170,0x17f,0}, 1,
608 { { 0, 8, DWRITE_SCRIPT_SHAPES_DEFAULT }}
611 /* Latin Extended-B */
612 {0x180,0x190,0x1bf,0x1c0,0x1c3,0x1c4,0x1cc,0x1dc,0x1ff,0x217,0x21b,0x24f,0}, 1,
613 { { 0, 12, DWRITE_SCRIPT_SHAPES_DEFAULT }}
616 /* IPA Extensions */
617 {0x250,0x260,0x270,0x290,0x2af,0}, 1,
618 { { 0, 5, DWRITE_SCRIPT_SHAPES_DEFAULT }}
621 /* Spacing Modifier Letters */
622 {0x2b0,0x2ba,0x2d7,0x2dd,0x2ef,0x2ff,0}, 1,
623 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
626 /* Combining Diacritical Marks */
627 {0x300,0x320,0x340,0x345,0x350,0x36f,0}, 1,
628 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
631 /* Greek and Coptic */
632 {0x370,0x388,0x3d8,0x3e1,0x3e2,0x3fa,0x3ff,0}, 3,
633 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT },
634 { 4, 1, DWRITE_SCRIPT_SHAPES_DEFAULT },
635 { 5, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }
639 /* Cyrillic and Cyrillic Supplement */
640 {0x400,0x40f,0x410,0x44f,0x450,0x45f,0x460,0x481,0x48a,0x4f0,0x4fa,0x4ff,0x500,0x510,0x520,0}, 1,
641 { { 0, 15, DWRITE_SCRIPT_SHAPES_DEFAULT }}
644 /* Armenian */
645 {0x531,0x540,0x559,0x55f,0x570,0x589,0x58a,0}, 1,
646 { { 0, 7, DWRITE_SCRIPT_SHAPES_DEFAULT }}
649 /* Hebrew */
650 {0x5e9,0x5dc,0x5d5,0x5dd,0}, 1,
651 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT }}
654 /* Latin, Hebrew, Latin */
655 {'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,
656 { { 0, 9, DWRITE_SCRIPT_SHAPES_DEFAULT },
657 { 9, 10, DWRITE_SCRIPT_SHAPES_DEFAULT },
658 { 19, 10, DWRITE_SCRIPT_SHAPES_DEFAULT }}
661 /* Syriac */
662 {0x710,0x712,0x712,0x714,'.',0}, 1,
663 { { 0, 5, DWRITE_SCRIPT_SHAPES_DEFAULT }}
666 /* Arabic Supplement */
667 {0x750,0x760,0x76d,'.',0}, 1,
668 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT }}
671 /* Thaana */
672 {0x780,0x78e,0x798,0x7a6,0x7b0,'.',0}, 1,
673 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
676 /* N'Ko */
677 {0x7c0,0x7ca,0x7e8,0x7eb,0x7f6,'.',0}, 1,
678 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
681 /* Thaana */
682 {0x780,0x798,0x7a5,0x7a6,0x7b0,'.',0}, 1,
683 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
686 /* Devanagari */
687 {0x926,0x947,0x935,0x928,0x93e,0x917,0x930,0x940,'.',0}, 1,
688 { { 0, 9, DWRITE_SCRIPT_SHAPES_DEFAULT }}
691 /* Bengali */
692 {0x9ac,0x9be,0x982,0x9b2,0x9be,'.',0}, 1,
693 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
696 /* Gurmukhi */
697 {0xa17,0xa41,0xa30,0xa2e,0xa41,0xa16,0xa40,'.',0}, 1,
698 { { 0, 8, DWRITE_SCRIPT_SHAPES_DEFAULT }}
701 /* Gujarati */
702 {0xa97,0xac1,0xa9c,0xab0,0xabe,0xaa4,0xac0,'.',0}, 1,
703 { { 0, 8, DWRITE_SCRIPT_SHAPES_DEFAULT }}
706 /* Oriya */
707 {0xb13,0xb21,0xb3c,0xb3f,0xb06,'.',0}, 1,
708 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
711 /* Tamil */
712 {0xba4,0xbae,0xbbf,0xbb4,0xbcd,'.',0}, 1,
713 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
716 /* Telugu */
717 {0xc24,0xc46,0xc32,0xc41,0xc17,0xc41,'.',0}, 1,
718 { { 0, 7, DWRITE_SCRIPT_SHAPES_DEFAULT }}
721 /* Kannada */
722 {0xc95,0xca8,0xccd,0xca8,0xca1,'.',0}, 1,
723 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
726 /* Malayalam */
727 {0xd2e,0xd32,0xd2f,0xd3e,0xd33,0xd02,'.',0}, 1,
728 { { 0, 7, DWRITE_SCRIPT_SHAPES_DEFAULT }}
731 /* Sinhala */
732 {0xd82,0xd85,0xd9a,0xdcf,'.',0}, 1,
733 { { 0, 5, DWRITE_SCRIPT_SHAPES_DEFAULT }}
736 /* Thai */
737 {0x0e04,0x0e27,0x0e32,0x0e21,0x0e1e,0x0e22,0x0e32,0x0e22,0x0e32,0x0e21,
738 0x0e2d,0x0e22,0x0e39,0x0e48,0x0e17,0x0e35,0x0e48,0x0e44,0x0e2b,0x0e19,
739 0x0e04,0x0e27,0x0e32,0x0e21,0x0e2a, 0x0e33,0x0e40,0x0e23,0x0e47,0x0e08,
740 0x0e2d,0x0e22,0x0e39,0x0e48,0x0e17,0x0e35,0x0e48,0x0e19,0x0e31,0x0e48,0x0e19,'.',0}, 1,
741 { { 0, 42, DWRITE_SCRIPT_SHAPES_DEFAULT }}
744 /* Lao */
745 {0xead,0xeb1,0xe81,0xeaa,0xead,0xe99,0xea5,0xeb2,0xea7,'.',0}, 1,
746 { { 0, 10, DWRITE_SCRIPT_SHAPES_DEFAULT }}
749 /* Tibetan */
750 {0xf04,0xf05,0xf0e,0x020,0xf51,0xf7c,0xf53,0xf0b,0xf5a,0xf53,0xf0b,
751 0xf51,0xf44,0xf0b,0xf54,0xf7c,0xf0d,'.',0}, 1,
752 { { 0, 18, DWRITE_SCRIPT_SHAPES_DEFAULT }}
755 /* Myanmar */
756 {0x1019,0x103c,0x1014,0x103a,0x1019,0x102c,0x1021,0x1000,0x1039,0x1001,0x101b,0x102c,'.',0}, 1,
757 { { 0, 13, DWRITE_SCRIPT_SHAPES_DEFAULT }}
760 /* Georgian */
761 {0x10a0,0x10d0,0x10da,0x10f1,0x10fb,0x2d00,'.',0}, 1,
762 { { 0, 7, DWRITE_SCRIPT_SHAPES_DEFAULT }}
765 /* Hangul */
766 {0x1100,0x1110,0x1160,0x1170,0x11a8,'.',0}, 1,
767 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
770 /* Ethiopic */
771 {0x130d,0x12d5,0x12dd,0}, 1,
772 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
775 /* Cherokee */
776 {0x13e3,0x13b3,0x13a9,0x0020,0x13a6,0x13ec,0x13c2,0x13af,0x13cd,0x13d7,0}, 1,
777 { { 0, 10, DWRITE_SCRIPT_SHAPES_DEFAULT }}
780 /* Canadian */
781 {0x1403,0x14c4,0x1483,0x144e,0x1450,0x1466,0}, 1,
782 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
785 /* Ogham */
786 {0x169b,0x1691,0x168c,0x1690,0x168b,0x169c,0}, 1,
787 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
790 /* Runic */
791 {0x16a0,0x16a1,0x16a2,0x16a3,0x16a4,0x16a5,0}, 1,
792 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
795 /* Khmer */
796 {0x1781,0x17c1,0x1798,0x179a,0x1797,0x17b6,0x179f,0x17b6,0x19e0,0}, 1,
797 { { 0, 9, DWRITE_SCRIPT_SHAPES_DEFAULT }}
800 /* Mongolian */
801 {0x182e,0x1823,0x1829,0x182d,0x1823,0x182f,0x0020,0x182a,0x1822,0x1834,0x1822,0x182d,0x180c,0}, 1,
802 { { 0, 13, DWRITE_SCRIPT_SHAPES_DEFAULT }}
805 /* Limbu */
806 {0x1900,0x1910,0x1920,0x1930,0}, 1,
807 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT }}
810 /* Tai Le */
811 {0x1956,0x196d,0x1970,0x1956,0x196c,0x1973,0x1951,0x1968,0x1952,0x1970,0}, 1,
812 { { 0, 10, DWRITE_SCRIPT_SHAPES_DEFAULT }}
815 /* New Tai Lue */
816 {0x1992,0x19c4,0}, 1,
817 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }}
820 /* Buginese */
821 {0x1a00,0x1a10,0}, 1,
822 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }}
825 /* Tai Tham */
826 {0x1a20,0x1a40,0x1a50,0}, 1,
827 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
830 /* Balinese */
831 {0x1b00,0x1b05,0x1b20,0}, 1,
832 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
835 /* Sundanese */
836 {0x1b80,0x1b85,0x1ba0,0}, 1,
837 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
840 /* Batak */
841 {0x1bc0,0x1be5,0x1bfc,0}, 1,
842 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
845 /* Lepcha */
846 {0x1c00,0x1c20,0x1c40,0}, 1,
847 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
850 /* Ol Chiki */
851 {0x1c50,0x1c5a,0x1c77,0}, 1,
852 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
855 /* Sundanese Supplement */
856 {0x1cc0,0x1cc5,0x1cc7,0}, 1,
857 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
860 /* Phonetic Extensions */
861 {0x1d00,0x1d40,0x1d70,0}, 1,
862 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
865 /* Combining diacritical marks */
866 {0x1dc0,0x300,0x1ddf,0}, 1,
867 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
870 /* Latin Extended Additional, Extended-C */
871 {0x1e00,0x1d00,0x2c60,0}, 1,
872 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
875 /* Greek Extended */
876 {0x3f0,0x1f00,0}, 1,
877 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }}
880 /* General Punctuation */
881 {0x1dc0,0x2000,0}, 1,
882 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }}
885 /* Superscripts and Subscripts */
886 {0x2070,0x2086,0x2000,0}, 1,
887 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
890 /* Currency, Combining Diacritical Marks for Symbols. Letterlike Symbols.. */
891 {0x20a0,0x20b8,0x2000,0x20d0,0x2100,0x2150,0x2190,0x2200,0x2300,0x2400,0x2440,0x2460,0x2500,0x2580,0x25a0,0x2600,
892 0x2700,0x27c0,0x27f0,0x2900,0x2980,0x2a00,0x2b00,0}, 1,
893 { { 0, 23, DWRITE_SCRIPT_SHAPES_DEFAULT }}
896 /* Braille */
897 {0x2800,0x2070,0x2000,0}, 1,
898 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
901 /* Glagolitic */
902 {0x2c00,0x2c12,0}, 1,
903 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }}
906 /* Coptic */
907 {0x2c80,0x3e2,0x1f00,0}, 2,
908 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT },
909 { 2, 1, DWRITE_SCRIPT_SHAPES_DEFAULT } }
912 /* Tifinagh */
913 {0x2d30,0x2d4a,0}, 1,
914 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }}
917 /* LRE/PDF */
918 {LRE,PDF,'a','b','c','\r',0}, 3,
919 { { 0, 2, DWRITE_SCRIPT_SHAPES_NO_VISUAL },
920 { 2, 3, DWRITE_SCRIPT_SHAPES_DEFAULT },
921 { 5, 1, DWRITE_SCRIPT_SHAPES_NO_VISUAL } }
924 /* LRE/PDF and other visual and non-visual codes from Common script range */
925 {LRE,PDF,'r','!',0x200b,'\r',0}, 3,
926 { { 0, 2, DWRITE_SCRIPT_SHAPES_NO_VISUAL },
927 { 2, 2, DWRITE_SCRIPT_SHAPES_DEFAULT },
928 { 4, 2, DWRITE_SCRIPT_SHAPES_NO_VISUAL } }
931 /* Inherited on its own */
932 {0x300,0x300,0}, 1,
933 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT } }
936 /* Inherited followed by Latin */
937 {0x300,0x300,'a',0}, 1,
938 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT } }
941 /* Inherited mixed with Arabic and Latin */
942 {0x300,'+',0x627,0x300,'a',0}, 2,
943 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT },
944 { 4, 1, DWRITE_SCRIPT_SHAPES_DEFAULT } }
947 {'a',0x300,'+',0x627,0x300,')','a',0}, 3,
948 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT },
949 { 3, 3, DWRITE_SCRIPT_SHAPES_DEFAULT },
950 { 6, 1, DWRITE_SCRIPT_SHAPES_DEFAULT } }
952 /* Paired punctuation */
954 {0x627,'(','a',')','a',0}, 2,
955 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT },
956 { 2, 3, DWRITE_SCRIPT_SHAPES_DEFAULT } }
959 {0x627,'[','a',']',0x627,0}, 3,
960 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT },
961 { 2, 2, DWRITE_SCRIPT_SHAPES_DEFAULT },
962 { 4, 1, DWRITE_SCRIPT_SHAPES_DEFAULT } }
964 /* Combining marks */
966 /* dotted circle - Common, followed by accent - Inherited */
967 {0x25cc,0x300,0}, 1,
968 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT } }
971 /* combining mark with explicit script value */
972 {0x25cc,0x300,0x5c4,0}, 1,
973 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT } }
976 /* inherited merges with following explicit script */
977 {0x25cc,0x300,'a',0}, 1,
978 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT } }
980 /* keep this as end test data marker */
981 { {0} }
984 static void init_expected_sa(struct call_sequence **seq, const struct sa_test *test)
986 static const struct call_entry end_of_sequence = { LastKind };
987 int i;
989 flush_sequence(seq, ANALYZER_ID);
991 /* add expected calls */
992 for (i = 0; i < test->item_count; i++)
994 struct call_entry call;
996 call.kind = ScriptAnalysis;
997 call.sa.pos = test->sa[i].pos;
998 call.sa.len = test->sa[i].len;
999 call.sa.shapes = test->sa[i].shapes;
1000 add_call(seq, 0, &call);
1003 /* and stop marker */
1004 add_call(seq, 0, &end_of_sequence);
1007 static void get_script_analysis(const WCHAR *str, DWRITE_SCRIPT_ANALYSIS *sa)
1009 IDWriteTextAnalyzer *analyzer;
1010 HRESULT hr;
1012 init_textsource(&analysissource, str, DWRITE_READING_DIRECTION_LEFT_TO_RIGHT);
1013 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
1014 ok(hr == S_OK, "got 0x%08x\n", hr);
1016 hr = IDWriteTextAnalyzer_AnalyzeScript(analyzer, &analysissource.IDWriteTextAnalysisSource_iface, 0,
1017 lstrlenW(analysissource.text), &analysissink2);
1018 ok(hr == S_OK, "got 0x%08x\n", hr);
1020 *sa = g_sa;
1023 static void test_AnalyzeScript(void)
1025 const struct sa_test *ptr = sa_tests;
1026 IDWriteTextAnalyzer *analyzer;
1027 HRESULT hr;
1029 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
1030 ok(hr == S_OK, "got 0x%08x\n", hr);
1032 while (*ptr->string)
1034 init_textsource(&analysissource, ptr->string, DWRITE_READING_DIRECTION_LEFT_TO_RIGHT);
1036 init_expected_sa(expected_seq, ptr);
1037 hr = IDWriteTextAnalyzer_AnalyzeScript(analyzer, &analysissource.IDWriteTextAnalysisSource_iface, 0,
1038 lstrlenW(ptr->string), &analysissink);
1039 ok(hr == S_OK, "got 0x%08x\n", hr);
1040 ok_sequence(sequences, ANALYZER_ID, expected_seq[0]->sequence, wine_dbgstr_w(ptr->string), FALSE);
1041 ptr++;
1044 IDWriteTextAnalyzer_Release(analyzer);
1047 struct linebreaks_test {
1048 const WCHAR text[BREAKPOINT_COUNT+1];
1049 DWRITE_LINE_BREAKPOINT bp[BREAKPOINT_COUNT];
1052 static struct linebreaks_test linebreaks_tests[] = {
1053 { {'A','-','B',' ','C',0x58a,'D',0x2010,'E',0x2012,'F',0x2013,'\t',0xc,0xb,0x2028,0x2029,0x200b,0},
1055 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1056 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 0, 0 },
1057 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1058 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 1, 0 },
1059 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1060 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 0, 0 },
1061 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1062 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 0, 0 },
1063 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1064 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 0, 0 },
1065 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1066 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1067 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 1, 0 },
1068 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_MUST_BREAK, 1, 0 },
1069 { DWRITE_BREAK_CONDITION_MUST_BREAK, DWRITE_BREAK_CONDITION_MUST_BREAK, 1, 0 },
1070 { DWRITE_BREAK_CONDITION_MUST_BREAK, DWRITE_BREAK_CONDITION_MUST_BREAK, 1, 0 },
1071 { DWRITE_BREAK_CONDITION_MUST_BREAK, DWRITE_BREAK_CONDITION_MUST_BREAK, 1, 0 },
1072 { DWRITE_BREAK_CONDITION_MUST_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 0, 0 },
1075 /* Soft hyphen, visible word dividers */
1076 { {'A',0xad,'B',0x5be,'C',0xf0b,'D',0x1361,'E',0x17d8,'F',0x17da,'G',0},
1078 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1079 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 0, 1 },
1080 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1081 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 0, 0 },
1082 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1083 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 0, 0 },
1084 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1085 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 0, 0 },
1086 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1087 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 0, 0 },
1088 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1089 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 0, 0 },
1090 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 0, 0 },
1093 { { 0 } }
1096 static void compare_breakpoints(const struct linebreaks_test *test, DWRITE_LINE_BREAKPOINT *actual)
1098 static const char *conditions[] = {"N","CB","NB","B"};
1099 const WCHAR *text = test->text;
1100 int cmp = memcmp(test->bp, actual, sizeof(*actual)*BREAKPOINT_COUNT);
1101 ok(!cmp, "%s: got wrong breakpoint data\n", wine_dbgstr_w(test->text));
1102 if (cmp) {
1103 int i = 0;
1104 while (*text) {
1105 ok(!memcmp(&test->bp[i], &actual[i], sizeof(*actual)),
1106 "%s: got [%s, %s] (%s, %s), expected [%s, %s] (%s, %s)\n",
1107 wine_dbgstr_wn(&test->text[i], 1),
1108 conditions[g_actual_bp[i].breakConditionBefore],
1109 conditions[g_actual_bp[i].breakConditionAfter],
1110 g_actual_bp[i].isWhitespace ? "WS" : "0",
1111 g_actual_bp[i].isSoftHyphen ? "SHY" : "0",
1112 conditions[test->bp[i].breakConditionBefore],
1113 conditions[test->bp[i].breakConditionAfter],
1114 test->bp[i].isWhitespace ? "WS" : "0",
1115 test->bp[i].isSoftHyphen ? "SHY" : "0");
1116 if (g_actual_bp[i].isSoftHyphen)
1117 ok(!g_actual_bp[i].isWhitespace, "%s: soft hyphen marked as a whitespace\n",
1118 wine_dbgstr_wn(&test->text[i], 1));
1119 text++;
1120 i++;
1125 static void test_AnalyzeLineBreakpoints(void)
1127 static const WCHAR emptyW[] = {0};
1128 const struct linebreaks_test *ptr = linebreaks_tests;
1129 IDWriteTextAnalyzer *analyzer;
1130 UINT32 i = 0;
1131 HRESULT hr;
1133 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
1134 ok(hr == S_OK, "got 0x%08x\n", hr);
1136 init_textsource(&analysissource, emptyW, DWRITE_READING_DIRECTION_LEFT_TO_RIGHT);
1137 hr = IDWriteTextAnalyzer_AnalyzeLineBreakpoints(analyzer, &analysissource.IDWriteTextAnalysisSource_iface, 0, 0,
1138 &analysissink);
1139 ok(hr == S_OK, "got 0x%08x\n", hr);
1141 while (*ptr->text)
1143 UINT32 len;
1145 init_textsource(&analysissource, ptr->text, DWRITE_READING_DIRECTION_LEFT_TO_RIGHT);
1147 len = lstrlenW(ptr->text);
1148 if (len > BREAKPOINT_COUNT) {
1149 ok(0, "test %u: increase BREAKPOINT_COUNT to at least %u\n", i, len);
1150 i++;
1151 ptr++;
1152 continue;
1155 memset(g_actual_bp, 0, sizeof(g_actual_bp));
1156 hr = IDWriteTextAnalyzer_AnalyzeLineBreakpoints(analyzer, &analysissource.IDWriteTextAnalysisSource_iface,
1157 0, len, &analysissink);
1158 ok(hr == S_OK, "got 0x%08x\n", hr);
1159 compare_breakpoints(ptr, g_actual_bp);
1161 i++;
1162 ptr++;
1165 IDWriteTextAnalyzer_Release(analyzer);
1168 static void test_GetScriptProperties(void)
1170 IDWriteTextAnalyzer1 *analyzer1;
1171 IDWriteTextAnalyzer *analyzer;
1172 DWRITE_SCRIPT_ANALYSIS sa;
1173 DWRITE_SCRIPT_PROPERTIES props;
1174 HRESULT hr;
1176 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
1177 ok(hr == S_OK, "got 0x%08x\n", hr);
1179 hr = IDWriteTextAnalyzer_QueryInterface(analyzer, &IID_IDWriteTextAnalyzer1, (void**)&analyzer1);
1180 IDWriteTextAnalyzer_Release(analyzer);
1181 if (hr != S_OK) {
1182 win_skip("GetScriptProperties() is not supported.\n");
1183 return;
1186 sa.script = 1000;
1187 hr = IDWriteTextAnalyzer1_GetScriptProperties(analyzer1, sa, &props);
1188 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1190 if (0) /* crashes on native */
1191 hr = IDWriteTextAnalyzer1_GetScriptProperties(analyzer1, sa, NULL);
1193 sa.script = 0;
1194 hr = IDWriteTextAnalyzer1_GetScriptProperties(analyzer1, sa, &props);
1195 ok(hr == S_OK, "got 0x%08x\n", hr);
1197 IDWriteTextAnalyzer1_Release(analyzer1);
1200 struct textcomplexity_test {
1201 const WCHAR text[5];
1202 UINT32 length;
1203 BOOL simple;
1204 UINT32 len_read;
1207 static const struct textcomplexity_test textcomplexity_tests[] = {
1208 { {0}, 1, FALSE, 1 },
1209 { {0}, 0, TRUE, 0 },
1210 { {0x610,0}, 0, TRUE, 0 },
1211 { {'A','B','C','D',0}, 3, TRUE, 3 },
1212 { {'A','B','C','D',0}, 5, TRUE, 4 },
1213 { {'A','B','C','D',0}, 10, TRUE, 4 },
1214 { {'A',0x610,'C','D',0}, 1, TRUE, 1 },
1215 { {'A',0x610,'C','D',0}, 2, FALSE, 2 },
1216 { {0x610,'A','C','D',0}, 1, FALSE, 1 },
1217 { {0x610,'A','C','D',0}, 2, FALSE, 1 },
1218 { {0x610,0x610,'C','D',0}, 2, FALSE, 2 },
1219 { {0xd800,'A','B',0}, 1, FALSE, 1 },
1220 { {0xd800,'A','B',0}, 2, FALSE, 1 },
1221 { {0xdc00,'A','B',0}, 2, FALSE, 1 },
1222 { {0x202a,'A',0x202c,0}, 3, FALSE, 1 },
1223 { {0x200e,'A',0}, 2, FALSE, 1 },
1224 { {0x200f,'A',0}, 2, FALSE, 1 },
1225 { {0x202d,'A',0}, 2, FALSE, 1 },
1226 { {0x202e,'A',0}, 2, FALSE, 1 },
1230 static void test_GetTextComplexity(void)
1232 static const WCHAR textW[] = {'A','B','C',0};
1233 IDWriteTextAnalyzer1 *analyzer1;
1234 IDWriteTextAnalyzer *analyzer;
1235 IDWriteFontFace *fontface;
1236 UINT16 indices[10];
1237 BOOL simple;
1238 HRESULT hr;
1239 UINT32 len;
1240 int i;
1242 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
1243 ok(hr == S_OK, "got 0x%08x\n", hr);
1245 hr = IDWriteTextAnalyzer_QueryInterface(analyzer, &IID_IDWriteTextAnalyzer1, (void**)&analyzer1);
1246 IDWriteTextAnalyzer_Release(analyzer);
1247 if (hr != S_OK) {
1248 win_skip("GetTextComplexity() is not supported.\n");
1249 return;
1252 if (0) { /* crashes on native */
1253 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer1, NULL, 0, NULL, NULL, NULL, NULL);
1254 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer1, NULL, 0, NULL, NULL, &len, NULL);
1255 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer1, textW, 3, NULL, NULL, NULL, NULL);
1256 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer1, textW, 3, NULL, NULL, &len, NULL);
1257 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer1, textW, 3, NULL, &simple, NULL, NULL);
1260 len = 1;
1261 simple = TRUE;
1262 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer1, NULL, 0, NULL, &simple, &len, NULL);
1263 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1264 ok(len == 0, "got %d\n", len);
1265 ok(simple == FALSE, "got %d\n", simple);
1267 len = 1;
1268 simple = TRUE;
1269 indices[0] = 1;
1270 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer1, textW, 3, NULL, &simple, &len, NULL);
1271 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1272 ok(len == 0, "got %d\n", len);
1273 ok(simple == FALSE, "got %d\n", simple);
1274 ok(indices[0] == 1, "got %d\n", indices[0]);
1276 fontface = create_fontface();
1278 for (i = 0; i < ARRAY_SIZE(textcomplexity_tests); i++) {
1279 const struct textcomplexity_test *ptr = &textcomplexity_tests[i];
1280 len = 1;
1281 simple = !ptr->simple;
1282 indices[0] = 0;
1283 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer1, ptr->text, ptr->length, fontface, &simple, &len, indices);
1284 ok(hr == S_OK, "%d: got 0x%08x\n", i, hr);
1285 ok(len == ptr->len_read, "%d: read length: got %d, expected %d\n", i, len, ptr->len_read);
1286 ok(simple == ptr->simple, "%d: simple: got %d, expected %d\n", i, simple, ptr->simple);
1287 if (simple && ptr->length)
1288 ok(indices[0] > 0, "%d: got %d\n", i, indices[0]);
1289 else
1290 ok(indices[0] == 0, "%d: got %d\n", i, indices[0]);
1293 IDWriteFontFace_Release(fontface);
1294 IDWriteTextAnalyzer1_Release(analyzer1);
1297 static void test_numbersubstitution(void)
1299 static const WCHAR dummyW[] = {'d','u','m','m','y',0};
1300 IDWriteNumberSubstitution *substitution;
1301 HRESULT hr;
1303 /* locale is not specified, method does not require it */
1304 hr = IDWriteFactory_CreateNumberSubstitution(factory, DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE, NULL, FALSE, &substitution);
1305 ok(hr == S_OK, "got 0x%08x\n", hr);
1306 IDWriteNumberSubstitution_Release(substitution);
1308 /* invalid locale name, method does not require it */
1309 hr = IDWriteFactory_CreateNumberSubstitution(factory, DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE, dummyW, FALSE, &substitution);
1310 ok(hr == S_OK, "got 0x%08x\n", hr);
1311 IDWriteNumberSubstitution_Release(substitution);
1313 /* invalid method */
1314 hr = IDWriteFactory_CreateNumberSubstitution(factory, DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL+1, NULL, FALSE, &substitution);
1315 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1317 /* invalid method */
1318 hr = IDWriteFactory_CreateNumberSubstitution(factory, -1, NULL, FALSE, &substitution);
1319 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1321 /* invalid locale */
1322 hr = IDWriteFactory_CreateNumberSubstitution(factory, DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL, NULL, FALSE, &substitution);
1323 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1325 hr = IDWriteFactory_CreateNumberSubstitution(factory, DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL, dummyW, FALSE, &substitution);
1326 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1328 hr = IDWriteFactory_CreateNumberSubstitution(factory, DWRITE_NUMBER_SUBSTITUTION_METHOD_CONTEXTUAL, dummyW, FALSE, &substitution);
1329 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1331 hr = IDWriteFactory_CreateNumberSubstitution(factory, DWRITE_NUMBER_SUBSTITUTION_METHOD_NATIONAL, dummyW, FALSE, &substitution);
1332 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1334 /* invalid locale, but it's not needed for this method */
1335 hr = IDWriteFactory_CreateNumberSubstitution(factory, DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE, dummyW, FALSE, &substitution);
1336 ok(hr == S_OK, "got 0x%08x\n", hr);
1337 IDWriteNumberSubstitution_Release(substitution);
1340 static void get_fontface_glyphs(IDWriteFontFace *fontface, const WCHAR *str, UINT16 *glyphs)
1342 while (*str) {
1343 UINT32 codepoint = *str;
1344 HRESULT hr;
1346 hr = IDWriteFontFace_GetGlyphIndices(fontface, &codepoint, 1, glyphs++);
1347 ok(hr == S_OK, "got 0x%08x\n", hr);
1348 str++;
1352 static void get_fontface_advances(IDWriteFontFace *fontface, FLOAT emsize, const UINT16 *glyphs, FLOAT *advances, UINT32 count)
1354 DWRITE_FONT_METRICS fontmetrics;
1355 UINT32 i;
1357 IDWriteFontFace_GetMetrics(fontface, &fontmetrics);
1358 for (i = 0; i < count; i++) {
1359 DWRITE_GLYPH_METRICS metrics;
1360 HRESULT hr;
1362 hr = IDWriteFontFace_GetDesignGlyphMetrics(fontface, glyphs + i, 1, &metrics, FALSE);
1363 ok(hr == S_OK, "got 0x%08x\n", hr);
1365 advances[i] = (FLOAT)metrics.advanceWidth * emsize / (FLOAT)fontmetrics.designUnitsPerEm;
1369 static void test_GetGlyphs(void)
1371 static const WCHAR test1W[] = {'<','B',' ','C',0};
1372 static const WCHAR test2W[] = {'<','B','\t','C',0};
1373 static const WCHAR test3W[] = {0x202a,0x202c,0};
1374 DWRITE_SHAPING_GLYPH_PROPERTIES shapingprops[20];
1375 DWRITE_SHAPING_TEXT_PROPERTIES props[20];
1376 UINT32 maxglyphcount, actual_count;
1377 FLOAT advances[10], advances2[10];
1378 IDWriteTextAnalyzer *analyzer;
1379 IDWriteFontFace *fontface;
1380 DWRITE_SCRIPT_ANALYSIS sa;
1381 DWRITE_GLYPH_OFFSET offsets[10];
1382 UINT16 clustermap[10];
1383 UINT16 glyphs1[10];
1384 UINT16 glyphs2[10];
1385 HRESULT hr;
1387 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
1388 ok(hr == S_OK, "got 0x%08x\n", hr);
1390 fontface = create_fontface();
1392 maxglyphcount = 1;
1393 sa.script = 0;
1394 sa.shapes = DWRITE_SCRIPT_SHAPES_DEFAULT;
1395 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test1W, lstrlenW(test1W), fontface, FALSE, FALSE, &sa, NULL,
1396 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs1, shapingprops, &actual_count);
1397 ok(hr == E_NOT_SUFFICIENT_BUFFER, "got 0x%08x\n", hr);
1399 if (0) {
1400 /* NULL fontface - crashes on Windows */
1401 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test1W, lstrlenW(test1W), NULL, FALSE, FALSE, &sa, NULL,
1402 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs1, shapingprops, &actual_count);
1405 /* invalid script id */
1406 maxglyphcount = 10;
1407 actual_count = 0;
1408 sa.script = 999;
1409 sa.shapes = DWRITE_SCRIPT_SHAPES_DEFAULT;
1410 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test1W, lstrlenW(test1W), fontface, FALSE, FALSE, &sa, NULL,
1411 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs1, shapingprops, &actual_count);
1412 ok(hr == S_OK, "got 0x%08x\n", hr);
1413 ok(actual_count == 4, "got %d\n", actual_count);
1414 ok(sa.script == 999, "got %u\n", sa.script);
1416 /* no '\t' -> ' ' replacement */
1417 maxglyphcount = 10;
1418 actual_count = 0;
1419 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test1W, lstrlenW(test1W), fontface, FALSE, FALSE, &sa, NULL,
1420 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs1, shapingprops, &actual_count);
1421 ok(hr == S_OK, "got 0x%08x\n", hr);
1422 ok(actual_count == 4, "got %d\n", actual_count);
1424 actual_count = 0;
1425 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test2W, lstrlenW(test2W), fontface, FALSE, FALSE, &sa, NULL,
1426 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs2, shapingprops, &actual_count);
1427 ok(hr == S_OK, "got 0x%08x\n", hr);
1428 ok(actual_count == 4, "got %d\n", actual_count);
1429 ok(glyphs1[2] != glyphs2[2], "got %d\n", glyphs1[2]);
1431 /* check that mirroring works */
1432 maxglyphcount = 10;
1433 actual_count = 0;
1434 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test1W, lstrlenW(test1W), fontface, FALSE, FALSE, &sa, NULL,
1435 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs1, shapingprops, &actual_count);
1436 ok(hr == S_OK, "got 0x%08x\n", hr);
1437 ok(actual_count == 4, "got %d\n", actual_count);
1439 actual_count = 0;
1440 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test1W, lstrlenW(test1W), fontface, FALSE, TRUE, &sa, NULL,
1441 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs2, shapingprops, &actual_count);
1442 ok(hr == S_OK, "got 0x%08x\n", hr);
1443 ok(actual_count == 4, "got %d\n", actual_count);
1444 ok(glyphs1[0] != glyphs2[0], "got %d\n", glyphs1[0]);
1446 /* embedded control codes, with unknown script id 0 */
1447 get_fontface_glyphs(fontface, test3W, glyphs2);
1448 get_fontface_advances(fontface, 10.0, glyphs2, advances2, 2);
1450 actual_count = 0;
1451 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test3W, lstrlenW(test3W), fontface, FALSE, TRUE, &sa, NULL,
1452 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs1, shapingprops, &actual_count);
1453 ok(hr == S_OK, "got 0x%08x\n", hr);
1454 ok(actual_count == 2, "got %d\n", actual_count);
1455 ok(glyphs1[0] == glyphs2[0], "got %u, expected %u\n", glyphs1[0], glyphs2[0]);
1456 ok(glyphs1[1] == glyphs2[1], "got %u, expected %u\n", glyphs1[1], glyphs2[1]);
1457 ok(shapingprops[0].isClusterStart == 1, "got %d\n", shapingprops[0].isClusterStart);
1458 ok(shapingprops[0].isZeroWidthSpace == 0, "got %d\n", shapingprops[0].isZeroWidthSpace);
1459 ok(shapingprops[1].isClusterStart == 1, "got %d\n", shapingprops[1].isClusterStart);
1460 ok(shapingprops[1].isZeroWidthSpace == 0, "got %d\n", shapingprops[1].isZeroWidthSpace);
1461 ok(clustermap[0] == 0, "got %d\n", clustermap[0]);
1462 ok(clustermap[1] == 1, "got %d\n", clustermap[1]);
1464 memset(advances, 0, sizeof(advances));
1465 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, test3W, clustermap, props, lstrlenW(test3W),
1466 glyphs1, shapingprops, actual_count, fontface, 10.0, FALSE, FALSE, &sa, NULL, NULL,
1467 NULL, 0, advances, offsets);
1468 ok(hr == S_OK, "got 0x%08x\n", hr);
1469 ok(advances[0] == advances2[0], "got %.2f, expected %.2f\n", advances[0], advances2[0]);
1470 ok(advances[1] == advances2[1], "got %.2f, expected %.2f\n", advances[1], advances2[1]);
1472 /* embedded control codes with proper script */
1473 sa.script = 0;
1474 get_script_analysis(test3W, &sa);
1475 ok(sa.script != 0, "got %d\n", sa.script);
1476 actual_count = 0;
1477 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test3W, lstrlenW(test3W), fontface, FALSE, FALSE, &sa, NULL,
1478 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs1, shapingprops, &actual_count);
1479 ok(hr == S_OK, "got 0x%08x\n", hr);
1480 ok(actual_count == 2, "got %d\n", actual_count);
1481 ok(glyphs1[0] == glyphs2[0], "got %u, expected %u\n", glyphs1[0], glyphs2[0]);
1482 ok(glyphs1[1] == glyphs2[1], "got %u, expected %u\n", glyphs1[1], glyphs2[1]);
1483 ok(shapingprops[0].isClusterStart == 1, "got %d\n", shapingprops[0].isClusterStart);
1484 ok(shapingprops[0].isZeroWidthSpace == 0, "got %d\n", shapingprops[0].isZeroWidthSpace);
1485 ok(shapingprops[1].isClusterStart == 1, "got %d\n", shapingprops[1].isClusterStart);
1486 ok(shapingprops[1].isZeroWidthSpace == 0, "got %d\n", shapingprops[1].isZeroWidthSpace);
1487 ok(clustermap[0] == 0, "got %d\n", clustermap[0]);
1488 ok(clustermap[1] == 1, "got %d\n", clustermap[1]);
1490 memset(advances, 0, sizeof(advances));
1491 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, test3W, clustermap, props, lstrlenW(test3W),
1492 glyphs1, shapingprops, actual_count, fontface, 10.0, FALSE, FALSE, &sa, NULL, NULL,
1493 NULL, 0, advances, offsets);
1494 ok(hr == S_OK, "got 0x%08x\n", hr);
1495 ok(advances[0] == advances2[0], "got %.2f, expected %.2f\n", advances[0], advances2[0]);
1496 ok(advances[1] == advances2[1], "got %.2f, expected %.2f\n", advances[1], advances2[1]);
1498 /* DWRITE_SCRIPT_SHAPES_NO_VISUAL run */
1499 maxglyphcount = 10;
1500 actual_count = 0;
1501 sa.script = 0;
1502 sa.shapes = DWRITE_SCRIPT_SHAPES_NO_VISUAL;
1503 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test1W, lstrlenW(test1W), fontface, FALSE, FALSE, &sa, NULL,
1504 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs1, shapingprops, &actual_count);
1505 ok(hr == S_OK, "got 0x%08x\n", hr);
1506 ok(actual_count == 4, "got %d\n", actual_count);
1507 ok(sa.script == 0, "got %u\n", sa.script);
1508 ok(!shapingprops[0].isZeroWidthSpace, "got %d\n", shapingprops[0].isZeroWidthSpace);
1510 IDWriteTextAnalyzer_Release(analyzer);
1511 IDWriteFontFace_Release(fontface);
1514 static BOOL has_feature(const DWRITE_FONT_FEATURE_TAG *tags, UINT32 count, DWRITE_FONT_FEATURE_TAG feature)
1516 UINT32 i;
1518 for (i = 0; i < count; i++)
1519 if (tags[i] == feature) return TRUE;
1520 return FALSE;
1523 static void test_GetTypographicFeatures(void)
1525 static const WCHAR localeW[] = {'c','a','d','a','b','r','a',0};
1526 static const WCHAR arabicW[] = {0x064a,0x064f,0x0633,0};
1527 static const WCHAR abcW[] = {'a','b','c',0};
1528 DWRITE_FONT_FEATURE_TAG tags[20];
1529 IDWriteTextAnalyzer2 *analyzer2;
1530 IDWriteTextAnalyzer *analyzer;
1531 IDWriteFontFace *fontface;
1532 DWRITE_SCRIPT_ANALYSIS sa;
1533 UINT32 count;
1534 HRESULT hr;
1535 BOOL ret;
1537 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
1538 ok(hr == S_OK, "got 0x%08x\n", hr);
1540 hr = IDWriteTextAnalyzer_QueryInterface(analyzer, &IID_IDWriteTextAnalyzer2, (void**)&analyzer2);
1541 IDWriteTextAnalyzer_Release(analyzer);
1542 if (hr != S_OK) {
1543 win_skip("GetTypographicFeatures() is not supported.\n");
1544 return;
1547 fontface = create_fontface();
1549 get_script_analysis(abcW, &sa);
1550 count = 0;
1551 hr = IDWriteTextAnalyzer2_GetTypographicFeatures(analyzer2, fontface, sa, NULL, 0, &count, NULL);
1552 todo_wine {
1553 ok(hr == E_NOT_SUFFICIENT_BUFFER, "got 0x%08x\n", hr);
1554 ok(count > 0, "got %u\n", count);
1556 /* invalid locale name is ignored */
1557 get_script_analysis(abcW, &sa);
1558 count = 0;
1559 hr = IDWriteTextAnalyzer2_GetTypographicFeatures(analyzer2, fontface, sa, localeW, 0, &count, NULL);
1560 todo_wine {
1561 ok(hr == E_NOT_SUFFICIENT_BUFFER, "got 0x%08x\n", hr);
1562 ok(count > 0, "got %u\n", count);
1564 /* both GSUB and GPOS features are reported */
1565 get_script_analysis(arabicW, &sa);
1566 memset(tags, 0, sizeof(tags));
1567 count = 0;
1568 hr = IDWriteTextAnalyzer2_GetTypographicFeatures(analyzer2, fontface, sa, NULL, ARRAY_SIZE(tags), &count, tags);
1569 ok(hr == S_OK, "got 0x%08x\n", hr);
1570 todo_wine {
1571 ok(count > 0, "got %u\n", count);
1572 ret = has_feature(tags, count, DWRITE_FONT_FEATURE_TAG_CONTEXTUAL_ALTERNATES);
1573 ok(ret, "expected 'calt' feature\n");
1574 ret = has_feature(tags, count, DWRITE_FONT_FEATURE_TAG_MARK_TO_MARK_POSITIONING);
1575 ok(ret, "expected 'mkmk' feature\n");
1577 get_script_analysis(abcW, &sa);
1578 memset(tags, 0, sizeof(tags));
1579 count = 0;
1580 hr = IDWriteTextAnalyzer2_GetTypographicFeatures(analyzer2, fontface, sa, NULL, ARRAY_SIZE(tags), &count, tags);
1581 ok(hr == S_OK, "got 0x%08x\n", hr);
1582 todo_wine {
1583 ok(count > 0, "got %u\n", count);
1584 ret = has_feature(tags, count, DWRITE_FONT_FEATURE_TAG_GLYPH_COMPOSITION_DECOMPOSITION);
1585 ok(ret, "expected 'ccmp' feature\n");
1586 ret = has_feature(tags, count, DWRITE_FONT_FEATURE_TAG_MARK_TO_MARK_POSITIONING);
1587 ok(ret, "expected 'mkmk' feature\n");
1589 ret = has_feature(tags, count, DWRITE_FONT_FEATURE_TAG_CONTEXTUAL_ALTERNATES);
1590 ok(!ret, "unexpected 'calt' feature\n");
1592 IDWriteFontFace_Release(fontface);
1593 IDWriteTextAnalyzer2_Release(analyzer2);
1596 static void test_GetGlyphPlacements(void)
1598 DWRITE_SHAPING_GLYPH_PROPERTIES glyphprops[2];
1599 DWRITE_SHAPING_TEXT_PROPERTIES textprops[2];
1600 static const WCHAR aW[] = {'A','D',0};
1601 UINT16 clustermap[2], glyphs[2];
1602 DWRITE_GLYPH_OFFSET offsets[2];
1603 IDWriteTextAnalyzer *analyzer;
1604 IDWriteFontFace *fontface;
1605 DWRITE_SCRIPT_ANALYSIS sa;
1606 FLOAT advances[2];
1607 UINT32 count, len;
1608 WCHAR *path;
1609 HRESULT hr;
1611 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
1612 ok(hr == S_OK, "got 0x%08x\n", hr);
1614 path = create_testfontfile(test_fontfile);
1615 fontface = create_testfontface(path);
1617 get_script_analysis(aW, &sa);
1618 count = 0;
1619 len = lstrlenW(aW);
1620 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, aW, len, fontface, FALSE, FALSE, &sa, NULL,
1621 NULL, NULL, NULL, 0, len, clustermap, textprops, glyphs, glyphprops, &count);
1622 ok(hr == S_OK, "got 0x%08x\n", hr);
1623 ok(count == 2, "got %u\n", count);
1625 /* just return on zero glyphs */
1626 advances[0] = advances[1] = 1.0;
1627 offsets[0].advanceOffset = offsets[0].ascenderOffset = 2.0;
1628 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, clustermap, textprops,
1629 len, glyphs, glyphprops, 0, fontface, 0.0, FALSE, FALSE, &sa, NULL, NULL,
1630 NULL, 0, advances, offsets);
1631 ok(hr == S_OK, "got 0x%08x\n", hr);
1632 ok(advances[0] == 1.0, "got %.2f\n", advances[0]);
1633 ok(offsets[0].advanceOffset == 2.0 && offsets[0].ascenderOffset == 2.0, "got %.2f,%.2f\n",
1634 offsets[0].advanceOffset, offsets[0].ascenderOffset);
1636 /* advances/offsets are scaled with provided font emSize and designed eM box size */
1637 advances[0] = advances[1] = 1.0;
1638 memset(offsets, 0xcc, sizeof(offsets));
1639 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, clustermap, textprops,
1640 len, glyphs, glyphprops, len, fontface, 0.0, FALSE, FALSE, &sa, NULL, NULL,
1641 NULL, 0, advances, offsets);
1642 ok(hr == S_OK, "got 0x%08x\n", hr);
1643 ok(advances[0] == 0.0, "got %.2f\n", advances[0]);
1644 ok(offsets[0].advanceOffset == 0.0 && offsets[0].ascenderOffset == 0.0, "got %.2f,%.2f\n",
1645 offsets[0].advanceOffset, offsets[0].ascenderOffset);
1647 advances[0] = advances[1] = 1.0;
1648 memset(offsets, 0xcc, sizeof(offsets));
1649 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, clustermap, textprops,
1650 len, glyphs, glyphprops, len, fontface, 2048.0, FALSE, FALSE, &sa, NULL, NULL,
1651 NULL, 0, advances, offsets);
1652 ok(hr == S_OK, "got 0x%08x\n", hr);
1653 ok(advances[0] == 1000.0, "got %.2f\n", advances[0]);
1654 ok(offsets[0].advanceOffset == 0.0 && offsets[0].ascenderOffset == 0.0, "got %.2f,%.2f\n",
1655 offsets[0].advanceOffset, offsets[0].ascenderOffset);
1657 advances[0] = advances[1] = 1.0;
1658 memset(offsets, 0xcc, sizeof(offsets));
1659 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, clustermap, textprops,
1660 len, glyphs, glyphprops, len, fontface, 1024.0, FALSE, FALSE, &sa, NULL, NULL,
1661 NULL, 0, advances, offsets);
1662 ok(hr == S_OK, "got 0x%08x\n", hr);
1663 ok(advances[0] == 500.0, "got %.2f\n", advances[0]);
1664 ok(advances[1] == 500.0, "got %.2f\n", advances[1]);
1665 ok(offsets[0].advanceOffset == 0.0 && offsets[0].ascenderOffset == 0.0, "got %.2f,%.2f\n",
1666 offsets[0].advanceOffset, offsets[0].ascenderOffset);
1668 advances[0] = advances[1] = 1.0;
1669 memset(offsets, 0xcc, sizeof(offsets));
1670 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, clustermap, textprops,
1671 len, glyphs, glyphprops, len, fontface, 20.48, FALSE, FALSE, &sa, NULL, NULL,
1672 NULL, 0, advances, offsets);
1673 ok(hr == S_OK, "got 0x%08x\n", hr);
1674 ok(advances[0] == 10.0, "got %.2f\n", advances[0]);
1675 ok(advances[1] == 10.0, "got %.2f\n", advances[1]);
1676 ok(offsets[0].advanceOffset == 0.0 && offsets[0].ascenderOffset == 0.0, "got %.2f,%.2f\n",
1677 offsets[0].advanceOffset, offsets[0].ascenderOffset);
1679 /* without clustermap */
1680 advances[0] = advances[1] = 1.0;
1681 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, NULL, textprops,
1682 len, glyphs, glyphprops, len, fontface, 1024.0, FALSE, FALSE, &sa, NULL, NULL,
1683 NULL, 0, advances, offsets);
1684 ok(hr == S_OK, "got 0x%08x\n", hr);
1685 ok(advances[0] == 500.0, "got %.2f\n", advances[0]);
1686 ok(advances[1] == 500.0, "got %.2f\n", advances[1]);
1688 /* it's happy to use negative size too */
1689 advances[0] = advances[1] = 1.0;
1690 memset(offsets, 0xcc, sizeof(offsets));
1691 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, clustermap, textprops,
1692 len, glyphs, glyphprops, len, fontface, -10.24, FALSE, FALSE, &sa, NULL, NULL,
1693 NULL, 0, advances, offsets);
1694 ok(hr == S_OK, "got 0x%08x\n", hr);
1695 ok(advances[0] == -5.0, "got %.2f\n", advances[0]);
1696 ok(offsets[0].advanceOffset == 0.0 && offsets[0].ascenderOffset == 0.0, "got %.2f,%.2f\n",
1697 offsets[0].advanceOffset, offsets[0].ascenderOffset);
1699 /* DWRITE_SCRIPT_SHAPES_NO_VISUAL has no effect on placement */
1700 sa.shapes = DWRITE_SCRIPT_SHAPES_NO_VISUAL;
1701 advances[0] = advances[1] = 1.0f;
1702 memset(offsets, 0xcc, sizeof(offsets));
1703 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, clustermap, textprops,
1704 len, glyphs, glyphprops, len, fontface, 2048.0f, FALSE, FALSE, &sa, NULL, NULL,
1705 NULL, 0, advances, offsets);
1706 ok(hr == S_OK, "got 0x%08x\n", hr);
1707 ok(advances[0] == 1000.0f, "got %.2f\n", advances[0]);
1708 ok(advances[1] == 1000.0f, "got %.2f\n", advances[1]);
1709 ok(offsets[0].advanceOffset == 0.0f && offsets[0].ascenderOffset == 0.0f, "got %.2f,%.2f\n",
1710 offsets[0].advanceOffset, offsets[0].ascenderOffset);
1712 /* isZeroWidthSpace */
1713 sa.shapes = DWRITE_SCRIPT_SHAPES_DEFAULT;
1714 advances[0] = advances[1] = 1.0f;
1715 memset(offsets, 0xcc, sizeof(offsets));
1716 glyphprops[0].isZeroWidthSpace = 1;
1717 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, clustermap, textprops,
1718 len, glyphs, glyphprops, len, fontface, 2048.0f, FALSE, FALSE, &sa, NULL, NULL,
1719 NULL, 0, advances, offsets);
1720 ok(hr == S_OK, "got 0x%08x\n", hr);
1721 ok(advances[0] == 0.0f, "got %.2f\n", advances[0]);
1722 ok(advances[1] == 1000.0f, "got %.2f\n", advances[1]);
1723 ok(offsets[0].advanceOffset == 0.0f && offsets[0].ascenderOffset == 0.0f, "got %.2f,%.2f\n",
1724 offsets[0].advanceOffset, offsets[0].ascenderOffset);
1726 IDWriteTextAnalyzer_Release(analyzer);
1727 IDWriteFontFace_Release(fontface);
1728 DELETE_FONTFILE(path);
1731 struct spacing_test {
1732 FLOAT leading;
1733 FLOAT trailing;
1734 FLOAT min_advance;
1735 FLOAT advances[3];
1736 FLOAT offsets[3];
1737 FLOAT modified_advances[3];
1738 FLOAT modified_offsets[3];
1739 BOOL single_cluster;
1740 DWRITE_SHAPING_GLYPH_PROPERTIES props[3];
1743 static const struct spacing_test spacing_tests[] = {
1744 { 0.0, 0.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 } }, /* 0 */
1745 { 0.0, 0.0, 2.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 } },
1746 { 1.0, 0.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 11.0, 12.0 }, { 3.0, 4.0 } },
1747 { 1.0, 1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 12.0, 13.0 }, { 3.0, 4.0 } },
1748 { 1.0, -1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 3.0, 4.0 } },
1749 { 0.0, -10.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 0.0, 1.0 }, { 2.0, 3.0 } }, /* 5 */
1750 { -5.0, -4.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 5.0, 5.0 }, { -1.0, -0.5 } },
1751 { -5.0, -5.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 5.0, 5.0 }, { -0.5, 0.0 } },
1752 { 2.0, 0.0, 5.0, { 1.0, 2.0 }, { 2.0, 3.0 }, { 7.0, 7.0 }, { 6.0, 6.5 } },
1753 { 2.0, 1.0, 5.0, { 1.0, 2.0 }, { 2.0, 3.0 }, { 8.0, 8.0 }, { 6.0, 6.5 } },
1754 { 2.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 7.0, 7.0 }, { 4.0, 5.0 } }, /* 10 */
1755 { 1.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 6.0, 6.0 }, { 3.0, 4.0 } },
1756 { -10.0, 1.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 6.0, 6.0 }, { -3.0, -3.0 } },
1757 { 0.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 5.0, 5.0 }, { 2.0, 3.0 } },
1758 { 1.0, -10.0, -5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 0.0, 0.0 }, { 2.0, 3.0 } },
1759 { -10.0, 1.0, 5.0, { 8.0, 11.0 }, { 2.0, 3.0 }, { 6.0, 6.0 }, { -1.0, -3.0 } }, /* 15 */
1760 /* cluster of more than 1 glyph */
1761 { 0.0, 0.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 }, TRUE },
1762 { 1.0, 0.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.5 }, { 11.0, 11.0 }, { 3.0, 3.5 }, TRUE },
1763 { 1.0, 1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 11.0, 12.0 }, { 3.0, 3.0 }, TRUE },
1764 { 1.0, -1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 11.0, 10.0 }, { 3.0, 3.0 }, TRUE },
1765 { 0.0, -10.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 1.0 }, { 2.0, 3.0 }, TRUE }, /* 20 */
1766 { 0.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 1.0 }, { 2.0, 3.0 }, TRUE },
1767 { 1.0, -10.0, -5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 0.0, 0.0 }, { 2.0, 3.0 }, TRUE },
1768 { -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 },
1769 { -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 },
1770 { -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 */
1771 { -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 },
1772 { 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 },
1773 { -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 },
1774 /* isZeroWidthSpace set */
1775 { 1.0, 0.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 12.0 }, { 2.0, 4.0 }, FALSE, { { 0, 0, 0, 1, 0 }, { 0 } } },
1776 { 1.0, 1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 13.0 }, { 2.0, 4.0 }, FALSE, { { 0, 0, 0, 1, 0 }, { 0 } } }, /* 30 */
1777 { 1.0, -1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 3.0, 3.0 }, FALSE, { { 0 }, { 0, 0, 0, 1, 0 } } },
1778 { 0.0, -10.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 1.0 }, { 2.0, 3.0 }, FALSE, { { 0, 0, 0, 1, 0 }, { 0 } } },
1779 { -5.0, -4.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 5.0, 11.0 }, { -1.0, 3.0 }, FALSE, { { 0 }, { 0, 0, 0, 1, 0 } } },
1780 { -5.0, -5.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 }, FALSE, { { 0, 0, 0, 1, 0 }, { 0, 0, 0, 1, 0 }} },
1781 { 2.0, 0.0, 5.0, { 1.0, 2.0 }, { 2.0, 3.0 }, { 7.0, 2.0 }, { 6.0, 3.0 }, FALSE, { { 0 }, { 0, 0, 0, 1, 0 } } }, /* 35 */
1782 { 2.0, 1.0, 5.0, { 1.0, 2.0 }, { 2.0, 3.0 }, { 8.0, 2.0 }, { 6.0, 3.0 }, FALSE, { { 0 }, { 0, 0, 0, 1, 0 } } },
1783 { 2.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 }, FALSE, { { 0, 0, 0, 1, 0 }, { 0, 0, 0, 1, 0 } } },
1784 { 1.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 6.0, 11.0 }, { 3.0, 3.0 }, FALSE, { { 0 }, { 0, 0, 0, 1, 0 } } },
1785 { -10.0, 1.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 }, FALSE, { { 0, 0, 0, 1, 0 }, { 0, 0, 0, 1, 0 } } },
1786 { 0.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 5.0, 11.0 }, { 2.0, 3.0 }, FALSE, { { 0 }, { 0, 0, 0, 1, 0 } } }, /* 40 */
1787 { 1.0, -10.0, -5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 0.0, 0.0 }, { 2.0, 3.0 }, FALSE, { { 0, 0, 0, 1, 0 }, { 0 } } },
1788 { -10.0, 1.0, 5.0, { 8.0, 11.0 }, { 2.0, 3.0 }, { 6.0, 11.0 }, { -1.0, 3.0 }, FALSE, { { 0 }, { 0, 0, 0, 1, 0 } } },
1789 /* isDiacritic */
1790 { 1.0, 0.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 11.0, 12.0 }, { 3.0, 4.0 }, FALSE, { { 0, 0, 1, 0, 0 }, { 0 } } },
1791 { 1.0, 1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 12.0, 13.0 }, { 3.0, 4.0 }, FALSE, { { 0, 0, 1, 0, 0 }, { 0 } } },
1792 { 1.0, -1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 3.0, 4.0 }, FALSE, { { 0 }, { 0, 0, 1, 0, 0 } } }, /* 45 */
1793 { 0.0, -10.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 0.0, 1.0 }, { 2.0, 3.0 }, FALSE, { { 0, 0, 1, 0, 0 }, { 0 } } },
1794 { -5.0, -4.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 5.0, 5.0 }, { -1.0, -0.5 }, FALSE, { { 0 }, { 0, 0, 1, 0, 0 } } },
1795 { -5.0, -5.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 5.0, 5.0 }, { -0.5, 0.0 }, FALSE, { { 0, 0, 1, 0, 0 }, { 0, 0, 1, 0, 0 }} },
1796 { 2.0, 0.0, 5.0, { 1.0, 2.0 }, { 2.0, 3.0 }, { 7.0, 7.0 }, { 6.0, 6.5 }, FALSE, { { 0 }, { 0, 0, 1, 0, 0 } } },
1797 { 2.0, 1.0, 5.0, { 1.0, 2.0 }, { 2.0, 3.0 }, { 8.0, 8.0 }, { 6.0, 6.5 }, FALSE, { { 0 }, { 0, 0, 1, 0, 0 } } }, /* 50 */
1798 { 2.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 7.0, 7.0 }, { 4.0, 5.0 }, FALSE, { { 0, 0, 1, 0, 0 }, { 0, 0, 1, 0, 0 } } },
1799 { 1.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 6.0, 11.0 }, { 3.0, 3.0 }, FALSE, { { 0 }, { 0, 0, 0, 1, 0 } } },
1800 { -10.0, 1.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 6.0, 6.0 }, { -3.0, -3.0 }, FALSE, { { 0, 0, 1, 0, 0 }, { 0, 0, 1, 0, 0 } } },
1801 { 0.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 5.0, 5.0 }, { 2.0, 3.0 }, FALSE, { { 0 }, { 0, 0, 1, 0, 0 } } },
1802 { 1.0, -10.0, -5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 0.0, 0.0 }, { 2.0, 3.0 }, FALSE, { { 0, 0, 1, 0, 0 }, { 0 } } }, /* 55 */
1803 { -10.0, 1.0, 5.0, { 8.0, 11.0 }, { 2.0, 3.0 }, { 6.0, 6.0 }, { -1.0, -3.0 }, FALSE, { { 0 }, { 0, 0, 1, 0, 0 } } },
1806 static void test_ApplyCharacterSpacing(void)
1808 DWRITE_SHAPING_GLYPH_PROPERTIES props[3];
1809 IDWriteTextAnalyzer1 *analyzer1;
1810 IDWriteTextAnalyzer *analyzer;
1811 UINT16 clustermap[2];
1812 HRESULT hr;
1813 int i;
1815 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
1816 ok(hr == S_OK, "got 0x%08x\n", hr);
1818 hr = IDWriteTextAnalyzer_QueryInterface(analyzer, &IID_IDWriteTextAnalyzer1, (void**)&analyzer1);
1819 IDWriteTextAnalyzer_Release(analyzer);
1820 if (hr != S_OK) {
1821 win_skip("ApplyCharacterSpacing() is not supported.\n");
1822 return;
1825 for (i = 0; i < ARRAY_SIZE(spacing_tests); i++) {
1826 const struct spacing_test *ptr = spacing_tests + i;
1827 DWRITE_GLYPH_OFFSET offsets[3];
1828 UINT32 glyph_count;
1829 FLOAT advances[3];
1831 offsets[0].advanceOffset = ptr->offsets[0];
1832 offsets[1].advanceOffset = ptr->offsets[1];
1833 offsets[2].advanceOffset = ptr->offsets[2];
1834 /* Ascender offsets are never touched as spacing applies in reading direction only,
1835 we'll only test them to see if they are not changed */
1836 offsets[0].ascenderOffset = 23.0;
1837 offsets[1].ascenderOffset = 32.0;
1838 offsets[2].ascenderOffset = 31.0;
1840 glyph_count = ptr->advances[2] > 0.0 ? 3 : 2;
1841 if (ptr->single_cluster) {
1842 clustermap[0] = 0;
1843 clustermap[1] = 0;
1845 else {
1846 /* trivial case with one glyph per cluster */
1847 clustermap[0] = 0;
1848 clustermap[1] = 1;
1851 advances[0] = advances[1] = 123.45;
1852 memcpy(props, ptr->props, sizeof(props));
1854 hr = IDWriteTextAnalyzer1_ApplyCharacterSpacing(analyzer1,
1855 ptr->leading,
1856 ptr->trailing,
1857 ptr->min_advance,
1858 ARRAY_SIZE(clustermap),
1859 glyph_count,
1860 clustermap,
1861 ptr->advances,
1862 offsets,
1863 props,
1864 advances,
1865 offsets);
1866 ok(hr == (ptr->min_advance < 0.0f ? E_INVALIDARG : S_OK), "%d: got 0x%08x\n", i, hr);
1868 if (hr == S_OK) {
1869 ok(ptr->modified_advances[0] == advances[0], "%d: got advance[0] %.2f, expected %.2f\n", i, advances[0], ptr->modified_advances[0]);
1870 ok(ptr->modified_advances[1] == advances[1], "%d: got advance[1] %.2f, expected %.2f\n", i, advances[1], ptr->modified_advances[1]);
1871 if (glyph_count > 2)
1872 ok(ptr->modified_advances[2] == advances[2], "%d: got advance[2] %.2f, expected %.2f\n", i, advances[2], ptr->modified_advances[2]);
1874 ok(ptr->modified_offsets[0] == offsets[0].advanceOffset, "%d: got offset[0] %.2f, expected %.2f\n", i,
1875 offsets[0].advanceOffset, ptr->modified_offsets[0]);
1876 ok(ptr->modified_offsets[1] == offsets[1].advanceOffset, "%d: got offset[1] %.2f, expected %.2f\n", i,
1877 offsets[1].advanceOffset, ptr->modified_offsets[1]);
1878 if (glyph_count > 2)
1879 ok(ptr->modified_offsets[2] == offsets[2].advanceOffset, "%d: got offset[2] %.2f, expected %.2f\n", i,
1880 offsets[2].advanceOffset, ptr->modified_offsets[2]);
1882 ok(offsets[0].ascenderOffset == 23.0, "%d: unexpected ascenderOffset %.2f\n", i, offsets[0].ascenderOffset);
1883 ok(offsets[1].ascenderOffset == 32.0, "%d: unexpected ascenderOffset %.2f\n", i, offsets[1].ascenderOffset);
1884 ok(offsets[2].ascenderOffset == 31.0, "%d: unexpected ascenderOffset %.2f\n", i, offsets[2].ascenderOffset);
1886 else {
1887 ok(ptr->modified_advances[0] == advances[0], "%d: got advance[0] %.2f, expected %.2f\n", i, advances[0], ptr->modified_advances[0]);
1888 ok(ptr->modified_advances[1] == advances[1], "%d: got advance[1] %.2f, expected %.2f\n", i, advances[1], ptr->modified_advances[1]);
1889 ok(ptr->offsets[0] == offsets[0].advanceOffset, "%d: got offset[0] %.2f, expected %.2f\n", i,
1890 offsets[0].advanceOffset, ptr->modified_offsets[0]);
1891 ok(ptr->offsets[1] == offsets[1].advanceOffset, "%d: got offset[1] %.2f, expected %.2f\n", i,
1892 offsets[1].advanceOffset, ptr->modified_offsets[1]);
1893 ok(offsets[0].ascenderOffset == 23.0, "%d: unexpected ascenderOffset %.2f\n", i, offsets[0].ascenderOffset);
1894 ok(offsets[1].ascenderOffset == 32.0, "%d: unexpected ascenderOffset %.2f\n", i, offsets[1].ascenderOffset);
1897 /* same, with argument aliasing */
1898 memcpy(advances, ptr->advances, glyph_count * sizeof(*advances));
1899 offsets[0].advanceOffset = ptr->offsets[0];
1900 offsets[1].advanceOffset = ptr->offsets[1];
1901 offsets[2].advanceOffset = ptr->offsets[2];
1902 /* Ascender offsets are never touched as spacing applies in reading direction only,
1903 we'll only test them to see if they are not changed */
1904 offsets[0].ascenderOffset = 23.0f;
1905 offsets[1].ascenderOffset = 32.0f;
1906 offsets[2].ascenderOffset = 31.0f;
1908 hr = IDWriteTextAnalyzer1_ApplyCharacterSpacing(analyzer1,
1909 ptr->leading,
1910 ptr->trailing,
1911 ptr->min_advance,
1912 ARRAY_SIZE(clustermap),
1913 glyph_count,
1914 clustermap,
1915 advances,
1916 offsets,
1917 props,
1918 advances,
1919 offsets);
1920 ok(hr == (ptr->min_advance < 0.0f ? E_INVALIDARG : S_OK), "%d: got 0x%08x\n", i, hr);
1922 if (hr == S_OK) {
1923 ok(ptr->modified_advances[0] == advances[0], "%d: got advance[0] %.2f, expected %.2f\n", i, advances[0], ptr->modified_advances[0]);
1924 ok(ptr->modified_advances[1] == advances[1], "%d: got advance[1] %.2f, expected %.2f\n", i, advances[1], ptr->modified_advances[1]);
1925 if (glyph_count > 2)
1926 ok(ptr->modified_advances[2] == advances[2], "%d: got advance[2] %.2f, expected %.2f\n", i, advances[2], ptr->modified_advances[2]);
1928 ok(ptr->modified_offsets[0] == offsets[0].advanceOffset, "%d: got offset[0] %.2f, expected %.2f\n", i,
1929 offsets[0].advanceOffset, ptr->modified_offsets[0]);
1930 ok(ptr->modified_offsets[1] == offsets[1].advanceOffset, "%d: got offset[1] %.2f, expected %.2f\n", i,
1931 offsets[1].advanceOffset, ptr->modified_offsets[1]);
1932 if (glyph_count > 2)
1933 ok(ptr->modified_offsets[2] == offsets[2].advanceOffset, "%d: got offset[2] %.2f, expected %.2f\n", i,
1934 offsets[2].advanceOffset, ptr->modified_offsets[2]);
1936 ok(offsets[0].ascenderOffset == 23.0f, "%d: unexpected ascenderOffset %.2f\n", i, offsets[0].ascenderOffset);
1937 ok(offsets[1].ascenderOffset == 32.0f, "%d: unexpected ascenderOffset %.2f\n", i, offsets[1].ascenderOffset);
1938 ok(offsets[2].ascenderOffset == 31.0f, "%d: unexpected ascenderOffset %.2f\n", i, offsets[2].ascenderOffset);
1940 else {
1941 /* with aliased advances original values are retained */
1942 ok(ptr->advances[0] == advances[0], "%d: got advance[0] %.2f, expected %.2f\n", i, advances[0], ptr->advances[0]);
1943 ok(ptr->advances[1] == advances[1], "%d: got advance[1] %.2f, expected %.2f\n", i, advances[1], ptr->advances[1]);
1944 ok(ptr->offsets[0] == offsets[0].advanceOffset, "%d: got offset[0] %.2f, expected %.2f\n", i,
1945 offsets[0].advanceOffset, ptr->modified_offsets[0]);
1946 ok(ptr->offsets[1] == offsets[1].advanceOffset, "%d: got offset[1] %.2f, expected %.2f\n", i,
1947 offsets[1].advanceOffset, ptr->modified_offsets[1]);
1948 ok(offsets[0].ascenderOffset == 23.0f, "%d: unexpected ascenderOffset %.2f\n", i, offsets[0].ascenderOffset);
1949 ok(offsets[1].ascenderOffset == 32.0f, "%d: unexpected ascenderOffset %.2f\n", i, offsets[1].ascenderOffset);
1953 IDWriteTextAnalyzer1_Release(analyzer1);
1956 struct orientation_transf_test {
1957 DWRITE_GLYPH_ORIENTATION_ANGLE angle;
1958 BOOL is_sideways;
1959 DWRITE_MATRIX m;
1962 static const struct orientation_transf_test ot_tests[] = {
1963 { DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES, FALSE, { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 } },
1964 { DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES, FALSE, { 0.0, 1.0, -1.0, 0.0, 0.0, 0.0 } },
1965 { DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES, FALSE, { -1.0, 0.0, 0.0, -1.0, 0.0, 0.0 } },
1966 { DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES, FALSE, { 0.0, -1.0, 1.0, 0.0, 0.0, 0.0 } },
1967 { DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES, TRUE, { 0.0, 1.0, -1.0, 0.0, 0.0, 0.0 } },
1968 { DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES, TRUE, { -1.0, 0.0, 0.0, -1.0, 0.0, 0.0 } },
1969 { DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES, TRUE, { 0.0, -1.0, 1.0, 0.0, 0.0, 0.0 } },
1970 { DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES, TRUE, { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 } }
1973 static inline const char *dbgstr_matrix(const DWRITE_MATRIX *m)
1975 static char buff[64];
1976 sprintf(buff, "{%.2f, %.2f, %.2f, %.2f, %.2f, %.2f}", m->m11, m->m12,
1977 m->m21, m->m22, m->dx, m->dy);
1978 return buff;
1981 static void test_GetGlyphOrientationTransform(void)
1983 IDWriteTextAnalyzer2 *analyzer2;
1984 IDWriteTextAnalyzer1 *analyzer1;
1985 IDWriteTextAnalyzer *analyzer;
1986 FLOAT originx, originy;
1987 DWRITE_MATRIX m;
1988 HRESULT hr;
1989 int i;
1991 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
1992 ok(hr == S_OK, "got 0x%08x\n", hr);
1994 hr = IDWriteTextAnalyzer_QueryInterface(analyzer, &IID_IDWriteTextAnalyzer1, (void**)&analyzer1);
1995 IDWriteTextAnalyzer_Release(analyzer);
1996 if (hr != S_OK) {
1997 win_skip("GetGlyphOrientationTransform() is not supported.\n");
1998 return;
2001 /* invalid angle value */
2002 memset(&m, 0xcc, sizeof(m));
2003 hr = IDWriteTextAnalyzer1_GetGlyphOrientationTransform(analyzer1,
2004 DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES + 1, FALSE, &m);
2005 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
2006 ok(m.m11 == 0.0, "got %.2f\n", m.m11);
2008 for (i = 0; i < ARRAY_SIZE(ot_tests); i++) {
2009 memset(&m, 0, sizeof(m));
2010 hr = IDWriteTextAnalyzer1_GetGlyphOrientationTransform(analyzer1, ot_tests[i].angle,
2011 ot_tests[i].is_sideways, &m);
2012 ok(hr == S_OK, "got 0x%08x\n", hr);
2013 ok(!memcmp(&ot_tests[i].m, &m, sizeof(m)), "%d: wrong matrix %s\n", i, dbgstr_matrix(&m));
2016 hr = IDWriteTextAnalyzer1_QueryInterface(analyzer1, &IID_IDWriteTextAnalyzer2, (void**)&analyzer2);
2017 IDWriteTextAnalyzer1_Release(analyzer1);
2018 if (hr != S_OK) {
2019 win_skip("IDWriteTextAnalyzer2::GetGlyphOrientationTransform() is not supported.\n");
2020 return;
2023 /* invalid angle value */
2024 memset(&m, 0xcc, sizeof(m));
2025 hr = IDWriteTextAnalyzer2_GetGlyphOrientationTransform(analyzer2,
2026 DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES + 1, FALSE, 0.0, 0.0, &m);
2027 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
2028 ok(m.m11 == 0.0, "got %.2f\n", m.m11);
2030 originx = 50.0;
2031 originy = 60.0;
2032 for (i = 0; i < ARRAY_SIZE(ot_tests); i++) {
2033 DWRITE_GLYPH_ORIENTATION_ANGLE angle = DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES;
2034 DWRITE_MATRIX m_exp;
2036 memset(&m, 0, sizeof(m));
2038 /* zero offset gives same result as a call from IDWriteTextAnalyzer1 */
2039 hr = IDWriteTextAnalyzer2_GetGlyphOrientationTransform(analyzer2, ot_tests[i].angle,
2040 ot_tests[i].is_sideways, 0.0, 0.0, &m);
2041 ok(hr == S_OK, "got 0x%08x\n", hr);
2042 ok(!memcmp(&ot_tests[i].m, &m, sizeof(m)), "%d: wrong matrix %s\n", i, dbgstr_matrix(&m));
2044 m_exp = ot_tests[i].m;
2045 hr = IDWriteTextAnalyzer2_GetGlyphOrientationTransform(analyzer2, ot_tests[i].angle,
2046 ot_tests[i].is_sideways, originx, originy, &m);
2047 ok(hr == S_OK, "got 0x%08x\n", hr);
2049 /* 90 degrees more for sideways */
2050 if (ot_tests[i].is_sideways) {
2051 switch (ot_tests[i].angle)
2053 case DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES:
2054 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES;
2055 break;
2056 case DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES:
2057 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES;
2058 break;
2059 case DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES:
2060 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES;
2061 break;
2062 case DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES:
2063 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES;
2064 break;
2065 default:
2069 else
2070 angle = ot_tests[i].angle;
2072 /* set expected offsets */
2073 switch (angle)
2075 case DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES:
2076 break;
2077 case DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES:
2078 m_exp.dx = originx + originy;
2079 m_exp.dy = originy - originx;
2080 break;
2081 case DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES:
2082 m_exp.dx = originx + originx;
2083 m_exp.dy = originy + originy;
2084 break;
2085 case DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES:
2086 m_exp.dx = originx - originy;
2087 m_exp.dy = originy + originx;
2088 break;
2089 default:
2093 ok(!memcmp(&m_exp, &m, sizeof(m)), "%d: wrong matrix %s\n", i, dbgstr_matrix(&m));
2096 IDWriteTextAnalyzer2_Release(analyzer2);
2099 static void test_GetBaseline(void)
2101 DWRITE_SCRIPT_ANALYSIS sa = { 0 };
2102 IDWriteTextAnalyzer1 *analyzer1;
2103 IDWriteTextAnalyzer *analyzer;
2104 IDWriteFontFace *fontface;
2105 INT32 baseline;
2106 BOOL exists;
2107 HRESULT hr;
2109 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
2110 ok(hr == S_OK, "got 0x%08x\n", hr);
2112 hr = IDWriteTextAnalyzer_QueryInterface(analyzer, &IID_IDWriteTextAnalyzer1, (void**)&analyzer1);
2113 IDWriteTextAnalyzer_Release(analyzer);
2114 if (hr != S_OK) {
2115 win_skip("GetBaseline() is not supported.\n");
2116 return;
2119 fontface = create_fontface();
2121 /* Tahoma doesn't have BASE table, it doesn't work even with simulation enabled */
2122 exists = TRUE;
2123 baseline = 456;
2124 hr = IDWriteTextAnalyzer1_GetBaseline(analyzer1,
2125 fontface,
2126 DWRITE_BASELINE_DEFAULT,
2127 FALSE,
2128 TRUE,
2130 NULL,
2131 &baseline,
2132 &exists);
2133 todo_wine {
2134 ok(hr == S_OK, "got 0x%08x\n", hr);
2135 ok(baseline == 0, "got %d\n", baseline);
2136 ok(exists == FALSE, "got %d\n", exists);
2138 exists = TRUE;
2139 baseline = 456;
2140 hr = IDWriteTextAnalyzer1_GetBaseline(analyzer1,
2141 fontface,
2142 DWRITE_BASELINE_ROMAN,
2143 FALSE,
2144 TRUE,
2146 NULL,
2147 &baseline,
2148 &exists);
2149 todo_wine {
2150 ok(hr == S_OK, "got 0x%08x\n", hr);
2151 ok(baseline == 0, "got %d\n", baseline);
2152 ok(exists == FALSE, "got %d\n", exists);
2154 IDWriteFontFace_Release(fontface);
2155 IDWriteTextAnalyzer1_Release(analyzer1);
2158 static inline BOOL float_eq(FLOAT left, FLOAT right)
2160 int x = *(int *)&left;
2161 int y = *(int *)&right;
2163 if (x < 0)
2164 x = INT_MIN - x;
2165 if (y < 0)
2166 y = INT_MIN - y;
2168 return abs(x - y) <= 8;
2171 static void test_GetGdiCompatibleGlyphPlacements(void)
2173 static const WCHAR strW[] = {'A',0};
2174 DWRITE_SHAPING_GLYPH_PROPERTIES glyphprops[1];
2175 DWRITE_SHAPING_TEXT_PROPERTIES textprops[1];
2176 DWRITE_SCRIPT_ANALYSIS sa = { 0 };
2177 IDWriteTextAnalyzer *analyzer;
2178 IDWriteFontFace *fontface;
2179 UINT16 clustermap[1];
2180 HRESULT hr;
2181 UINT32 count;
2182 UINT16 glyphs[1];
2183 FLOAT advance;
2184 DWRITE_GLYPH_OFFSET offsets[1];
2185 DWRITE_FONT_METRICS fontmetrics;
2186 FLOAT emsize;
2188 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
2189 ok(hr == S_OK, "got 0x%08x\n", hr);
2191 fontface = create_fontface();
2193 IDWriteFontFace_GetMetrics(fontface, &fontmetrics);
2195 count = 0;
2196 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, strW, 1, fontface,
2197 FALSE, FALSE, &sa, NULL, NULL, NULL, NULL, 0, 1, clustermap,
2198 textprops, glyphs, glyphprops, &count);
2199 ok(hr == S_OK, "got 0x%08x\n", hr);
2200 ok(count == 1, "got %u\n", count);
2202 for (emsize = 12.0; emsize <= 20.0; emsize += 1.0) {
2203 FLOAT compatadvance, expected, ppdip;
2204 DWRITE_GLYPH_METRICS metrics;
2206 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, strW, clustermap,
2207 textprops, 1, glyphs, glyphprops, count, fontface, emsize, FALSE, FALSE,
2208 &sa, NULL, NULL, NULL, 0, &advance, offsets);
2209 ok(hr == S_OK, "got 0x%08x\n", hr);
2210 ok(advance > 0.0, "got %f\n", advance);
2212 /* 1 ppdip, no transform */
2213 ppdip = 1.0;
2214 hr = IDWriteFontFace_GetGdiCompatibleGlyphMetrics(fontface, emsize, ppdip, NULL, FALSE,
2215 glyphs, 1, &metrics, FALSE);
2216 ok(hr == S_OK, "got 0x%08x\n", hr);
2218 expected = floorf(metrics.advanceWidth * emsize * ppdip / fontmetrics.designUnitsPerEm + 0.5f) / ppdip;
2219 hr = IDWriteTextAnalyzer_GetGdiCompatibleGlyphPlacements(analyzer, strW,
2220 clustermap, textprops, 1, glyphs, glyphprops, count, fontface, emsize,
2221 ppdip, NULL, FALSE, FALSE, FALSE, &sa, NULL, NULL, NULL, 0, &compatadvance, offsets);
2222 ok(hr == S_OK, "got 0x%08x\n", hr);
2223 ok(compatadvance == expected, "%.0f: got advance %f, expected %f, natural %f\n", emsize,
2224 compatadvance, expected, advance);
2226 /* 1.2 ppdip, no transform */
2227 ppdip = 1.2;
2228 hr = IDWriteFontFace_GetGdiCompatibleGlyphMetrics(fontface, emsize, ppdip, NULL, FALSE,
2229 glyphs, 1, &metrics, FALSE);
2230 ok(hr == S_OK, "got 0x%08x\n", hr);
2232 expected = floorf(metrics.advanceWidth * emsize * ppdip / fontmetrics.designUnitsPerEm + 0.5f) / ppdip;
2233 hr = IDWriteTextAnalyzer_GetGdiCompatibleGlyphPlacements(analyzer, strW,
2234 clustermap, textprops, 1, glyphs, glyphprops, count, fontface, emsize,
2235 ppdip, NULL, FALSE, FALSE, FALSE, &sa, NULL, NULL, NULL, 0, &compatadvance, offsets);
2236 ok(hr == S_OK, "got 0x%08x\n", hr);
2237 ok(float_eq(compatadvance, expected), "%.0f: got advance %f, expected %f, natural %f\n", emsize,
2238 compatadvance, expected, advance);
2241 IDWriteFontFace_Release(fontface);
2242 IDWriteTextAnalyzer_Release(analyzer);
2245 struct bidi_test
2247 const WCHAR text[BIDI_LEVELS_COUNT];
2248 DWRITE_READING_DIRECTION direction;
2249 UINT8 explicit[BIDI_LEVELS_COUNT];
2250 UINT8 resolved[BIDI_LEVELS_COUNT];
2251 BOOL todo;
2254 static const struct bidi_test bidi_tests[] = {
2256 { 0x645, 0x6cc, 0x200c, 0x6a9, 0x646, 0x645, 0 },
2257 DWRITE_READING_DIRECTION_RIGHT_TO_LEFT,
2258 { 1, 1, 1, 1, 1, 1 },
2259 { 1, 1, 1, 1, 1, 1 }
2262 { 0x645, 0x6cc, 0x200c, 0x6a9, 0x646, 0x645, 0 },
2263 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2264 { 0, 0, 0, 0, 0, 0 },
2265 { 1, 1, 1, 1, 1, 1 }
2268 { 0x200c, 0x645, 0x6cc, 0x6a9, 0x646, 0x645, 0 },
2269 DWRITE_READING_DIRECTION_RIGHT_TO_LEFT,
2270 { 1, 1, 1, 1, 1, 1 },
2271 { 1, 1, 1, 1, 1, 1 }
2274 { 0x200c, 0x645, 0x6cc, 0x6a9, 0x646, 0x645, 0 },
2275 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2276 { 0, 0, 0, 0, 0, 0 },
2277 { 0, 1, 1, 1, 1, 1 }
2280 { 'A', 0x200c, 'B', 0 },
2281 DWRITE_READING_DIRECTION_RIGHT_TO_LEFT,
2282 { 1, 1, 1 },
2283 { 2, 2, 2 }
2286 { 'A', 0x200c, 'B', 0 },
2287 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2288 { 0, 0, 0 },
2289 { 0, 0, 0 }
2292 { LRE, PDF, 'a', 'b', 0 },
2293 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2294 { 2, 2, 0, 0 },
2295 { 0, 0, 0, 0 },
2298 { 'a', LRE, PDF, 'b', 0 },
2299 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2300 { 0, 2, 2, 0 },
2301 { 0, 0, 0, 0 },
2304 { RLE, PDF, 'a', 'b', 0 },
2305 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2306 { 1, 1, 0, 0 },
2307 { 0, 0, 0, 0 },
2310 { 'a', RLE, PDF, 'b', 0 },
2311 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2312 { 0, 1, 1, 0 },
2313 { 0, 0, 0, 0 },
2316 { 'a', RLE, PDF, 'b', 0 },
2317 DWRITE_READING_DIRECTION_RIGHT_TO_LEFT,
2318 { 1, 3, 3, 1 },
2319 { 2, 2, 2, 2 },
2322 { LRE, PDF, 'a', 'b', 0 },
2323 DWRITE_READING_DIRECTION_RIGHT_TO_LEFT,
2324 { 2, 2, 1, 1 },
2325 { 1, 1, 2, 2 },
2328 { PDF, 'a', 'b', 0 },
2329 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2330 { 0, 0, 0, 0 },
2331 { 0, 0, 0, 0 }
2334 { LRE, 'a', 'b', PDF, 0 },
2335 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2336 { 2, 2, 2, 2 },
2337 { 0, 2, 2, 2 },
2338 TRUE
2341 { LRI, 'a', 'b', PDI, 0 },
2342 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2343 { 0, 0, 0, 0 },
2344 { 0, 0, 0, 0 },
2345 TRUE
2348 { RLI, 'a', 'b', PDI, 0 },
2349 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2350 { 0, 0, 0, 0 },
2351 { 0, 0, 0, 0 },
2352 TRUE
2355 { 0 }
2359 static void compare_bidi_levels(unsigned int seq, const struct bidi_test *test, UINT32 len, UINT8 *explicit, UINT8 *resolved)
2361 unsigned int i, failcount = 0;
2362 BOOL match;
2364 match = !memcmp(explicit, test->explicit, len);
2365 if (!match) {
2366 if (test->todo) {
2367 failcount++;
2368 todo_wine
2369 ok(0, "test %u: %s wrong explicit levels:\n", seq, wine_dbgstr_w(test->text));
2371 else
2372 ok(0, "test %u: %s wrong explicit levels:\n", seq, wine_dbgstr_w(test->text));
2374 for (i = 0; i < len; i++) {
2375 if (test->explicit[i] != explicit[i]) {
2376 if (test->todo) {
2377 failcount++;
2378 todo_wine
2379 ok(0, "\tat %u, explicit level %u, expected %u\n", i, explicit[i], test->explicit[i]);
2381 else
2382 ok(0, "\tat %u, explicit level %u, expected %u\n", i, explicit[i], test->explicit[i]);
2387 match = !memcmp(resolved, test->resolved, len);
2388 if (!match) {
2389 if (test->todo) {
2390 failcount++;
2391 todo_wine
2392 ok(0, "test %u: %s wrong resolved levels:\n", seq, wine_dbgstr_w(test->text));
2394 else
2395 ok(0, "test %u: %s wrong resolved levels:\n", seq, wine_dbgstr_w(test->text));
2397 for (i = 0; i < len; i++) {
2398 if (test->resolved[i] != resolved[i]) {
2399 if (test->todo) {
2400 failcount++;
2401 todo_wine
2402 ok(0, "\tat %u, resolved level %u, expected %u\n", i, resolved[i], test->resolved[i]);
2404 else
2405 ok(0, "\tat %u, resolved level %u, expected %u\n", i, resolved[i], test->resolved[i]);
2410 todo_wine_if(test->todo && failcount == 0)
2411 ok(1, "test %u: marked as \"todo_wine\" but succeeds\n", seq);
2414 static void test_AnalyzeBidi(void)
2416 const struct bidi_test *ptr = bidi_tests;
2417 IDWriteTextAnalyzer *analyzer;
2418 UINT32 i = 0;
2419 HRESULT hr;
2421 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
2422 ok(hr == S_OK, "got 0x%08x\n", hr);
2424 while (*ptr->text)
2426 UINT32 len;
2428 init_textsource(&analysissource, ptr->text, ptr->direction);
2430 len = lstrlenW(ptr->text);
2431 if (len > BIDI_LEVELS_COUNT) {
2432 ok(0, "test %u: increase BIDI_LEVELS_COUNT to at least %u\n", i, len);
2433 i++;
2434 ptr++;
2435 continue;
2438 memset(g_explicit_levels, 0, sizeof(g_explicit_levels));
2439 memset(g_resolved_levels, 0, sizeof(g_resolved_levels));
2440 hr = IDWriteTextAnalyzer_AnalyzeBidi(analyzer, &analysissource.IDWriteTextAnalysisSource_iface, 0,
2441 len, &analysissink);
2442 ok(hr == S_OK, "%u: got 0x%08x\n", i, hr);
2443 compare_bidi_levels(i, ptr, len, g_explicit_levels, g_resolved_levels);
2445 i++;
2446 ptr++;
2449 IDWriteTextAnalyzer_Release(analyzer);
2452 START_TEST(analyzer)
2454 HRESULT hr;
2456 hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_ISOLATED, &IID_IDWriteFactory, (IUnknown**)&factory);
2457 ok(hr == S_OK, "got 0x%08x\n", hr);
2458 if (hr != S_OK)
2460 win_skip("failed to create factory\n");
2461 return;
2464 init_call_sequences(sequences, NUM_CALL_SEQUENCES);
2465 init_call_sequences(expected_seq, 1);
2467 test_AnalyzeScript();
2468 test_AnalyzeLineBreakpoints();
2469 test_AnalyzeBidi();
2470 test_GetScriptProperties();
2471 test_GetTextComplexity();
2472 test_GetGlyphs();
2473 test_numbersubstitution();
2474 test_GetTypographicFeatures();
2475 test_GetGlyphPlacements();
2476 test_ApplyCharacterSpacing();
2477 test_GetGlyphOrientationTransform();
2478 test_GetBaseline();
2479 test_GetGdiCompatibleGlyphPlacements();
2481 IDWriteFactory_Release(factory);