dwrite/tests: Some tests for control characters handling.
[wine/multimedia.git] / dlls / dwrite / tests / analyzer.c
blobffa4aed460c18aa6784a05dd5cffc9db7d9b4516
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>
25 #include "initguid.h"
26 #include "windows.h"
27 #include "dwrite.h"
28 #include "dwrite_2.h"
30 #include "wine/test.h"
32 static IDWriteFactory *factory;
33 static const WCHAR test_fontfile[] = {'w','i','n','e','_','t','e','s','t','_','f','o','n','t','.','t','t','f',0};
35 enum analysis_kind {
36 ScriptAnalysis,
37 LastKind
40 static const char *get_analysis_kind_name(enum analysis_kind kind)
42 switch (kind)
44 case ScriptAnalysis:
45 return "ScriptAnalysis";
46 default:
47 return "unknown";
51 struct script_analysis {
52 UINT32 pos;
53 UINT32 len;
54 DWRITE_SCRIPT_SHAPES shapes;
57 struct call_entry {
58 enum analysis_kind kind;
59 struct script_analysis sa;
62 struct testcontext {
63 enum analysis_kind kind;
64 BOOL todo;
65 int *failcount;
66 const char *file;
67 int line;
70 struct call_sequence
72 int count;
73 int size;
74 struct call_entry *sequence;
77 #define NUM_CALL_SEQUENCES 1
78 #define ANALYZER_ID 0
79 static struct call_sequence *sequences[NUM_CALL_SEQUENCES];
80 static struct call_sequence *expected_seq[1];
82 static void add_call(struct call_sequence **seq, int sequence_index, const struct call_entry *call)
84 struct call_sequence *call_seq = seq[sequence_index];
86 if (!call_seq->sequence)
88 call_seq->size = 10;
89 call_seq->sequence = HeapAlloc(GetProcessHeap(), 0,
90 call_seq->size * sizeof (struct call_entry));
93 if (call_seq->count == call_seq->size)
95 call_seq->size *= 2;
96 call_seq->sequence = HeapReAlloc(GetProcessHeap(), 0,
97 call_seq->sequence,
98 call_seq->size * sizeof (struct call_entry));
101 assert(call_seq->sequence);
103 call_seq->sequence[call_seq->count++] = *call;
106 static inline void flush_sequence(struct call_sequence **seg, int sequence_index)
108 struct call_sequence *call_seq = seg[sequence_index];
110 HeapFree(GetProcessHeap(), 0, call_seq->sequence);
111 call_seq->sequence = NULL;
112 call_seq->count = call_seq->size = 0;
115 static inline void flush_sequences(struct call_sequence **seq, int n)
117 int i;
118 for (i = 0; i < n; i++)
119 flush_sequence(seq, i);
122 static void init_call_sequences(struct call_sequence **seq, int n)
124 int i;
126 for (i = 0; i < n; i++)
127 seq[i] = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct call_sequence));
130 static void test_uint(UINT32 actual, UINT32 expected, const char *name, const struct testcontext *ctxt)
132 if (expected != actual && ctxt->todo)
134 (*ctxt->failcount)++;
135 ok_(ctxt->file, ctxt->line) (0, "%s: \"%s\" expecting %u, got %u\n", get_analysis_kind_name(ctxt->kind), name, expected, actual);
137 else
138 ok_(ctxt->file, ctxt->line) (expected == actual, "%s: \"%s\" expecting %u, got %u\n", get_analysis_kind_name(ctxt->kind), name,
139 expected, actual);
142 static void ok_sequence_(struct call_sequence **seq, int sequence_index,
143 const struct call_entry *expected, const char *context, BOOL todo,
144 const char *file, int line)
146 struct call_sequence *call_seq = seq[sequence_index];
147 static const struct call_entry end_of_sequence = { LastKind };
148 const struct call_entry *actual, *sequence;
149 int failcount = 0;
150 struct testcontext ctxt;
152 add_call(seq, sequence_index, &end_of_sequence);
154 sequence = call_seq->sequence;
155 actual = sequence;
157 ctxt.failcount = &failcount;
158 ctxt.todo = todo;
159 ctxt.file = file;
160 ctxt.line = line;
162 while (expected->kind != LastKind && actual->kind != LastKind)
164 if (expected->kind == actual->kind)
166 ctxt.kind = expected->kind;
168 switch (actual->kind)
170 case ScriptAnalysis:
171 test_uint(actual->sa.pos, expected->sa.pos, "position", &ctxt);
172 test_uint(actual->sa.len, expected->sa.len, "length", &ctxt);
173 test_uint(actual->sa.shapes, expected->sa.shapes, "shapes", &ctxt);
174 break;
175 default:
176 ok(0, "%s: callback not handled, %s\n", context, get_analysis_kind_name(actual->kind));
178 expected++;
179 actual++;
181 else if (todo)
183 failcount++;
184 todo_wine
186 ok_(file, line) (0, "%s: call %s was expected, but got call %s instead\n",
187 context, get_analysis_kind_name(expected->kind), get_analysis_kind_name(actual->kind));
190 flush_sequence(seq, sequence_index);
191 return;
193 else
195 ok_(file, line) (0, "%s: call %s was expected, but got call %s instead\n",
196 context, get_analysis_kind_name(expected->kind), get_analysis_kind_name(actual->kind));
197 expected++;
198 actual++;
202 if (todo)
204 todo_wine
206 if (expected->kind != LastKind || actual->kind != LastKind)
208 failcount++;
209 ok_(file, line) (0, "%s: the call sequence is not complete: expected %s - actual %s\n",
210 context, get_analysis_kind_name(expected->kind), get_analysis_kind_name(actual->kind));
214 else if (expected->kind != LastKind || actual->kind != LastKind)
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));
220 if (todo && !failcount) /* succeeded yet marked todo */
222 todo_wine
224 ok_(file, line)(1, "%s: marked \"todo_wine\" but succeeds\n", context);
228 flush_sequence(seq, sequence_index);
231 #define ok_sequence(seq, index, exp, contx, todo) \
232 ok_sequence_(seq, index, (exp), (contx), (todo), __FILE__, __LINE__)
234 static HRESULT WINAPI analysissink_QueryInterface(IDWriteTextAnalysisSink *iface, REFIID riid, void **obj)
236 if (IsEqualIID(riid, &IID_IDWriteTextAnalysisSink) || IsEqualIID(riid, &IID_IUnknown))
238 *obj = iface;
239 return S_OK;
242 *obj = NULL;
243 return E_NOINTERFACE;
246 static ULONG WINAPI analysissink_AddRef(IDWriteTextAnalysisSink *iface)
248 return 2;
251 static ULONG WINAPI analysissink_Release(IDWriteTextAnalysisSink *iface)
253 return 1;
256 static HRESULT WINAPI analysissink_SetScriptAnalysis(IDWriteTextAnalysisSink *iface,
257 UINT32 position, UINT32 length, DWRITE_SCRIPT_ANALYSIS const* sa)
259 struct call_entry entry;
261 entry.kind = ScriptAnalysis;
262 entry.sa.pos = position;
263 entry.sa.len = length;
264 entry.sa.shapes = sa->shapes;
265 add_call(sequences, ANALYZER_ID, &entry);
266 return S_OK;
269 static DWRITE_SCRIPT_ANALYSIS g_sa;
270 static HRESULT WINAPI analysissink_SetScriptAnalysis2(IDWriteTextAnalysisSink *iface,
271 UINT32 position, UINT32 length, DWRITE_SCRIPT_ANALYSIS const* sa)
273 g_sa = *sa;
274 return S_OK;
277 #define BREAKPOINT_COUNT 20
278 static DWRITE_LINE_BREAKPOINT g_actual_bp[BREAKPOINT_COUNT];
280 static HRESULT WINAPI analysissink_SetLineBreakpoints(IDWriteTextAnalysisSink *iface,
281 UINT32 position,
282 UINT32 length,
283 DWRITE_LINE_BREAKPOINT const* breakpoints)
285 if (position + length > BREAKPOINT_COUNT) {
286 ok(0, "SetLineBreakpoints: reported pos=%u, len=%u overflows expected length %d\n", position, length, BREAKPOINT_COUNT);
287 return E_FAIL;
289 memcpy(&g_actual_bp[position], breakpoints, length*sizeof(DWRITE_LINE_BREAKPOINT));
290 return S_OK;
293 static HRESULT WINAPI analysissink_SetBidiLevel(IDWriteTextAnalysisSink *iface,
294 UINT32 position,
295 UINT32 length,
296 UINT8 explicitLevel,
297 UINT8 resolvedLevel)
299 ok(0, "unexpected\n");
300 return E_NOTIMPL;
303 static HRESULT WINAPI analysissink_SetNumberSubstitution(IDWriteTextAnalysisSink *iface,
304 UINT32 position,
305 UINT32 length,
306 IDWriteNumberSubstitution* substitution)
308 ok(0, "unexpected\n");
309 return E_NOTIMPL;
312 static IDWriteTextAnalysisSinkVtbl analysissinkvtbl = {
313 analysissink_QueryInterface,
314 analysissink_AddRef,
315 analysissink_Release,
316 analysissink_SetScriptAnalysis,
317 analysissink_SetLineBreakpoints,
318 analysissink_SetBidiLevel,
319 analysissink_SetNumberSubstitution
322 static IDWriteTextAnalysisSinkVtbl analysissinkvtbl2 = {
323 analysissink_QueryInterface,
324 analysissink_AddRef,
325 analysissink_Release,
326 analysissink_SetScriptAnalysis2,
327 analysissink_SetLineBreakpoints,
328 analysissink_SetBidiLevel,
329 analysissink_SetNumberSubstitution
332 static IDWriteTextAnalysisSink analysissink = { &analysissinkvtbl };
333 static IDWriteTextAnalysisSink analysissink2 = { &analysissinkvtbl2 };
335 static HRESULT WINAPI analysissource_QueryInterface(IDWriteTextAnalysisSource *iface,
336 REFIID riid, void **obj)
338 ok(0, "QueryInterface not expected\n");
339 return E_NOTIMPL;
342 static ULONG WINAPI analysissource_AddRef(IDWriteTextAnalysisSource *iface)
344 ok(0, "AddRef not expected\n");
345 return 2;
348 static ULONG WINAPI analysissource_Release(IDWriteTextAnalysisSource *iface)
350 ok(0, "Release not expected\n");
351 return 1;
354 static const WCHAR *g_source;
356 static HRESULT WINAPI analysissource_GetTextAtPosition(IDWriteTextAnalysisSource *iface,
357 UINT32 position, WCHAR const** text, UINT32* text_len)
359 if (position >= lstrlenW(g_source))
361 *text = NULL;
362 *text_len = 0;
364 else
366 *text = &g_source[position];
367 *text_len = lstrlenW(g_source) - position;
370 return S_OK;
373 static HRESULT WINAPI analysissource_GetTextBeforePosition(IDWriteTextAnalysisSource *iface,
374 UINT32 position, WCHAR const** text, UINT32* text_len)
376 ok(0, "unexpected\n");
377 return E_NOTIMPL;
380 static DWRITE_READING_DIRECTION WINAPI analysissource_GetParagraphReadingDirection(
381 IDWriteTextAnalysisSource *iface)
383 ok(0, "unexpected\n");
384 return DWRITE_READING_DIRECTION_RIGHT_TO_LEFT;
387 static HRESULT WINAPI analysissource_GetLocaleName(IDWriteTextAnalysisSource *iface,
388 UINT32 position, UINT32* text_len, WCHAR const** locale)
390 *locale = NULL;
391 *text_len = 0;
392 return S_OK;
395 static HRESULT WINAPI analysissource_GetNumberSubstitution(IDWriteTextAnalysisSource *iface,
396 UINT32 position, UINT32* text_len, IDWriteNumberSubstitution **substitution)
398 ok(0, "unexpected\n");
399 return E_NOTIMPL;
402 static IDWriteTextAnalysisSourceVtbl analysissourcevtbl = {
403 analysissource_QueryInterface,
404 analysissource_AddRef,
405 analysissource_Release,
406 analysissource_GetTextAtPosition,
407 analysissource_GetTextBeforePosition,
408 analysissource_GetParagraphReadingDirection,
409 analysissource_GetLocaleName,
410 analysissource_GetNumberSubstitution
413 static IDWriteTextAnalysisSource analysissource = { &analysissourcevtbl };
415 static IDWriteFontFace *create_fontface(void)
417 static const WCHAR tahomaW[] = {'T','a','h','o','m','a',0};
418 IDWriteGdiInterop *interop;
419 IDWriteFontFace *fontface;
420 IDWriteFont *font;
421 LOGFONTW logfont;
422 HRESULT hr;
424 hr = IDWriteFactory_GetGdiInterop(factory, &interop);
425 ok(hr == S_OK, "got 0x%08x\n", hr);
427 memset(&logfont, 0, sizeof(logfont));
428 logfont.lfHeight = 12;
429 logfont.lfWidth = 12;
430 logfont.lfWeight = FW_NORMAL;
431 logfont.lfItalic = 1;
432 lstrcpyW(logfont.lfFaceName, tahomaW);
434 hr = IDWriteGdiInterop_CreateFontFromLOGFONT(interop, &logfont, &font);
435 ok(hr == S_OK, "got 0x%08x\n", hr);
437 hr = IDWriteFont_CreateFontFace(font, &fontface);
438 ok(hr == S_OK, "got 0x%08x\n", hr);
440 IDWriteFont_Release(font);
441 IDWriteGdiInterop_Release(interop);
443 return fontface;
446 static WCHAR *create_testfontfile(const WCHAR *filename)
448 static WCHAR pathW[MAX_PATH];
449 DWORD written;
450 HANDLE file;
451 HRSRC res;
452 void *ptr;
454 GetTempPathW(sizeof(pathW)/sizeof(WCHAR), pathW);
455 lstrcatW(pathW, filename);
457 file = CreateFileW(pathW, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0);
458 ok(file != INVALID_HANDLE_VALUE, "file creation failed, at %s, error %d\n", wine_dbgstr_w(pathW),
459 GetLastError());
461 res = FindResourceA(GetModuleHandleA(NULL), (LPCSTR)MAKEINTRESOURCE(1), (LPCSTR)RT_RCDATA);
462 ok(res != 0, "couldn't find resource\n");
463 ptr = LockResource(LoadResource(GetModuleHandleA(NULL), res));
464 WriteFile(file, ptr, SizeofResource(GetModuleHandleA(NULL), res), &written, NULL);
465 ok(written == SizeofResource(GetModuleHandleA(NULL), res), "couldn't write resource\n");
466 CloseHandle(file);
468 return pathW;
471 #define DELETE_FONTFILE(filename) _delete_testfontfile(filename, __LINE__)
472 static void _delete_testfontfile(const WCHAR *filename, int line)
474 BOOL ret = DeleteFileW(filename);
475 ok_(__FILE__,line)(ret, "failed to delete file %s, error %d\n", wine_dbgstr_w(filename), GetLastError());
478 static IDWriteFontFace *create_testfontface(const WCHAR *filename)
480 IDWriteFontFace *face;
481 IDWriteFontFile *file;
482 HRESULT hr;
484 hr = IDWriteFactory_CreateFontFileReference(factory, filename, NULL, &file);
485 ok(hr == S_OK, "got 0x%08x\n",hr);
487 hr = IDWriteFactory_CreateFontFace(factory, DWRITE_FONT_FACE_TYPE_TRUETYPE, 1, &file, 0,
488 DWRITE_FONT_SIMULATIONS_NONE, &face);
489 ok(hr == S_OK, "got 0x%08x\n", hr);
490 IDWriteFontFile_Release(file);
492 return face;
495 struct sa_test {
496 const WCHAR string[50];
497 int item_count;
498 struct script_analysis sa[10];
501 static struct sa_test sa_tests[] = {
503 /* just 1 char string */
504 {'t',0}, 1,
505 { { 0, 1, DWRITE_SCRIPT_SHAPES_DEFAULT }}
508 {'t','e','s','t',0}, 1,
509 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT }}
512 {' ',' ',' ',' ','!','$','[','^','{','~',0}, 1,
513 { { 0, 10, DWRITE_SCRIPT_SHAPES_DEFAULT }}
516 {' ',' ',' ','1','2',' ',0}, 1,
517 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
520 /* digits only */
521 {'1','2',0}, 1,
522 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }}
525 /* Arabic */
526 {0x064a,0x064f,0x0633,0x0627,0x0648,0x0650,0x064a,0}, 1,
527 { { 0, 7, DWRITE_SCRIPT_SHAPES_DEFAULT }}
530 /* Arabic */
531 {0x0627,0x0644,0x0635,0x0651,0x0650,0x062d,0x0629,0x064f,' ',0x062a,0x064e,
532 0x0627,0x062c,0x064c,' ',0x0639,0x064e,0x0644,0x0649,' ',
533 0x0631,0x064f,0x0624,0x0648,0x0633,0x0650,' ',0x0627,0x0644,
534 0x0623,0x0635,0x0650,0x062d,0x0651,0x064e,0x0627,0x0621,0x0650,0x06f0,0x06f5,0}, 1,
535 { { 0, 40, DWRITE_SCRIPT_SHAPES_DEFAULT }}
538 /* Arabic, Latin */
539 {'1','2','3','-','5','2',0x064a,0x064f,0x0633,0x0627,0x0648,0x0650,0x064a,'7','1','.',0}, 1,
540 { { 0, 16, DWRITE_SCRIPT_SHAPES_DEFAULT }}
543 /* Arabic, English */
544 {'A','B','C','-','D','E','F',' ',0x0621,0x0623,0x0624,0}, 2,
545 { { 0, 8, DWRITE_SCRIPT_SHAPES_DEFAULT },
546 { 8, 3, DWRITE_SCRIPT_SHAPES_DEFAULT },
550 /* leading space, Arabic, English */
551 {' ',0x0621,0x0623,0x0624,'A','B','C','-','D','E','F',0}, 2,
552 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT },
553 { 4, 7, DWRITE_SCRIPT_SHAPES_DEFAULT },
557 /* English, Arabic, trailing space */
558 {'A','B','C','-','D','E','F',0x0621,0x0623,0x0624,' ',0}, 2,
559 { { 0, 7, DWRITE_SCRIPT_SHAPES_DEFAULT },
560 { 7, 4, DWRITE_SCRIPT_SHAPES_DEFAULT },
564 /* C1 Controls, Latin-1 Supplement */
565 {0x80,0x90,0x9f,0xa0,0xc0,0xb8,0xbf,0xc0,0xff,0}, 2,
566 { { 0, 3, DWRITE_SCRIPT_SHAPES_NO_VISUAL },
567 { 3, 6, DWRITE_SCRIPT_SHAPES_DEFAULT },
571 /* Latin Extended-A */
572 {0x100,0x120,0x130,0x140,0x150,0x160,0x170,0x17f,0}, 1,
573 { { 0, 8, DWRITE_SCRIPT_SHAPES_DEFAULT }}
576 /* Latin Extended-B */
577 {0x180,0x190,0x1bf,0x1c0,0x1c3,0x1c4,0x1cc,0x1dc,0x1ff,0x217,0x21b,0x24f,0}, 1,
578 { { 0, 12, DWRITE_SCRIPT_SHAPES_DEFAULT }}
581 /* IPA Extensions */
582 {0x250,0x260,0x270,0x290,0x2af,0}, 1,
583 { { 0, 5, DWRITE_SCRIPT_SHAPES_DEFAULT }}
586 /* Spacing Modifier Letters */
587 {0x2b0,0x2ba,0x2d7,0x2dd,0x2ef,0x2ff,0}, 1,
588 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
591 /* Combining Diacritical Marks */
592 {0x300,0x320,0x340,0x345,0x350,0x36f,0}, 1,
593 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
596 /* Greek and Coptic */
597 {0x370,0x388,0x3d8,0x3e1,0x3e2,0x3fa,0x3ff,0}, 3,
598 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT },
599 { 4, 1, DWRITE_SCRIPT_SHAPES_DEFAULT },
600 { 5, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }
604 /* Cyrillic and Cyrillic Supplement */
605 {0x400,0x40f,0x410,0x44f,0x450,0x45f,0x460,0x481,0x48a,0x4f0,0x4fa,0x4ff,0x500,0x510,0x520,0}, 1,
606 { { 0, 15, DWRITE_SCRIPT_SHAPES_DEFAULT }}
609 /* Armenian */
610 {0x531,0x540,0x559,0x55f,0x570,0x589,0x58a,0}, 1,
611 { { 0, 7, DWRITE_SCRIPT_SHAPES_DEFAULT }}
614 /* Hebrew */
615 {0x5e9,0x5dc,0x5d5,0x5dd,0}, 1,
616 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT }}
619 /* Latin, Hebrew, Latin */
620 {'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,
621 { { 0, 9, DWRITE_SCRIPT_SHAPES_DEFAULT },
622 { 9, 10, DWRITE_SCRIPT_SHAPES_DEFAULT },
623 { 19, 10, DWRITE_SCRIPT_SHAPES_DEFAULT }}
626 /* Syriac */
627 {0x710,0x712,0x712,0x714,'.',0}, 1,
628 { { 0, 5, DWRITE_SCRIPT_SHAPES_DEFAULT }}
631 /* Arabic Supplement */
632 {0x750,0x760,0x76d,'.',0}, 1,
633 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT }}
636 /* Thaana */
637 {0x780,0x78e,0x798,0x7a6,0x7b0,'.',0}, 1,
638 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
641 /* N'Ko */
642 {0x7c0,0x7ca,0x7e8,0x7eb,0x7f6,'.',0}, 1,
643 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
646 /* Thaana */
647 {0x780,0x798,0x7a5,0x7a6,0x7b0,'.',0}, 1,
648 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
651 /* Devanagari */
652 {0x926,0x947,0x935,0x928,0x93e,0x917,0x930,0x940,'.',0}, 1,
653 { { 0, 9, DWRITE_SCRIPT_SHAPES_DEFAULT }}
656 /* Bengali */
657 {0x9ac,0x9be,0x982,0x9b2,0x9be,'.',0}, 1,
658 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
661 /* Gurmukhi */
662 {0xa17,0xa41,0xa30,0xa2e,0xa41,0xa16,0xa40,'.',0}, 1,
663 { { 0, 8, DWRITE_SCRIPT_SHAPES_DEFAULT }}
666 /* Gujarati */
667 {0xa97,0xac1,0xa9c,0xab0,0xabe,0xaa4,0xac0,'.',0}, 1,
668 { { 0, 8, DWRITE_SCRIPT_SHAPES_DEFAULT }}
671 /* Oriya */
672 {0xb13,0xb21,0xb3c,0xb3f,0xb06,'.',0}, 1,
673 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
676 /* Tamil */
677 {0xba4,0xbae,0xbbf,0xbb4,0xbcd,'.',0}, 1,
678 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
681 /* Telugu */
682 {0xc24,0xc46,0xc32,0xc41,0xc17,0xc41,'.',0}, 1,
683 { { 0, 7, DWRITE_SCRIPT_SHAPES_DEFAULT }}
686 /* Kannada */
687 {0xc95,0xca8,0xccd,0xca8,0xca1,'.',0}, 1,
688 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
691 /* Malayalam */
692 {0xd2e,0xd32,0xd2f,0xd3e,0xd33,0xd02,'.',0}, 1,
693 { { 0, 7, DWRITE_SCRIPT_SHAPES_DEFAULT }}
696 /* Sinhala */
697 {0xd82,0xd85,0xd9a,0xdcf,'.',0}, 1,
698 { { 0, 5, DWRITE_SCRIPT_SHAPES_DEFAULT }}
701 /* Thai */
702 {0x0e04,0x0e27,0x0e32,0x0e21,0x0e1e,0x0e22,0x0e32,0x0e22,0x0e32,0x0e21,
703 0x0e2d,0x0e22,0x0e39,0x0e48,0x0e17,0x0e35,0x0e48,0x0e44,0x0e2b,0x0e19,
704 0x0e04,0x0e27,0x0e32,0x0e21,0x0e2a, 0x0e33,0x0e40,0x0e23,0x0e47,0x0e08,
705 0x0e2d,0x0e22,0x0e39,0x0e48,0x0e17,0x0e35,0x0e48,0x0e19,0x0e31,0x0e48,0x0e19,'.',0}, 1,
706 { { 0, 42, DWRITE_SCRIPT_SHAPES_DEFAULT }}
709 /* Lao */
710 {0xead,0xeb1,0xe81,0xeaa,0xead,0xe99,0xea5,0xeb2,0xea7,'.',0}, 1,
711 { { 0, 10, DWRITE_SCRIPT_SHAPES_DEFAULT }}
714 /* Tibetan */
715 {0xf04,0xf05,0xf0e,0x020,0xf51,0xf7c,0xf53,0xf0b,0xf5a,0xf53,0xf0b,
716 0xf51,0xf44,0xf0b,0xf54,0xf7c,0xf0d,'.',0}, 1,
717 { { 0, 18, DWRITE_SCRIPT_SHAPES_DEFAULT }}
720 /* Myanmar */
721 {0x1019,0x103c,0x1014,0x103a,0x1019,0x102c,0x1021,0x1000,0x1039,0x1001,0x101b,0x102c,'.',0}, 1,
722 { { 0, 13, DWRITE_SCRIPT_SHAPES_DEFAULT }}
725 /* Georgian */
726 {0x10a0,0x10d0,0x10da,0x10f1,0x10fb,0x2d00,'.',0}, 1,
727 { { 0, 7, DWRITE_SCRIPT_SHAPES_DEFAULT }}
730 /* Hangul */
731 {0x1100,0x1110,0x1160,0x1170,0x11a8,'.',0}, 1,
732 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
735 /* Ethiopic */
736 {0x130d,0x12d5,0x12dd,0}, 1,
737 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
740 /* Cherokee */
741 {0x13e3,0x13b3,0x13a9,0x0020,0x13a6,0x13ec,0x13c2,0x13af,0x13cd,0x13d7,0}, 1,
742 { { 0, 10, DWRITE_SCRIPT_SHAPES_DEFAULT }}
745 /* Canadian */
746 {0x1403,0x14c4,0x1483,0x144e,0x1450,0x1466,0}, 1,
747 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
750 /* Ogham */
751 {0x169b,0x1691,0x168c,0x1690,0x168b,0x169c,0}, 1,
752 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
755 /* Runic */
756 {0x16a0,0x16a1,0x16a2,0x16a3,0x16a4,0x16a5,0}, 1,
757 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
760 /* Khmer */
761 {0x1781,0x17c1,0x1798,0x179a,0x1797,0x17b6,0x179f,0x17b6,0x19e0,0}, 1,
762 { { 0, 9, DWRITE_SCRIPT_SHAPES_DEFAULT }}
765 /* Mongolian */
766 {0x182e,0x1823,0x1829,0x182d,0x1823,0x182f,0x0020,0x182a,0x1822,0x1834,0x1822,0x182d,0x180c,0}, 1,
767 { { 0, 13, DWRITE_SCRIPT_SHAPES_DEFAULT }}
770 /* Limbu */
771 {0x1900,0x1910,0x1920,0x1930,0}, 1,
772 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT }}
775 /* Tai Le */
776 {0x1956,0x196d,0x1970,0x1956,0x196c,0x1973,0x1951,0x1968,0x1952,0x1970,0}, 1,
777 { { 0, 10, DWRITE_SCRIPT_SHAPES_DEFAULT }}
780 /* New Tai Lue */
781 {0x1992,0x19c4,0}, 1,
782 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }}
785 /* Buginese */
786 {0x1a00,0x1a10,0}, 1,
787 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }}
790 /* Tai Tham */
791 {0x1a20,0x1a40,0x1a50,0}, 1,
792 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
795 /* Balinese */
796 {0x1b00,0x1b05,0x1b20,0}, 1,
797 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
800 /* Sundanese */
801 {0x1b80,0x1b85,0x1ba0,0}, 1,
802 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
805 /* Batak */
806 {0x1bc0,0x1be5,0x1bfc,0}, 1,
807 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
810 /* Lepcha */
811 {0x1c00,0x1c20,0x1c40,0}, 1,
812 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
815 /* Ol Chiki */
816 {0x1c50,0x1c5a,0x1c77,0}, 1,
817 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
820 /* Sundanese Supplement */
821 {0x1cc0,0x1cc5,0x1cc7,0}, 1,
822 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
825 /* Phonetic Extensions */
826 {0x1d00,0x1d40,0x1d70,0}, 1,
827 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
830 /* Combining diacritical marks */
831 {0x1dc0,0x300,0x1ddf,0}, 1,
832 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
835 /* Latin Extended Additional, Extended-C */
836 {0x1e00,0x1d00,0x2c60,0}, 1,
837 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
840 /* Greek Extended */
841 {0x3f0,0x1f00,0}, 1,
842 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }}
845 /* General Punctuation */
846 {0x1dc0,0x2000,0}, 1,
847 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }}
850 /* Superscripts and Subscripts */
851 {0x2070,0x2086,0x2000,0}, 1,
852 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
855 /* Currency, Combining Diacritical Marks for Symbols. Letterlike Symbols.. */
856 {0x20a0,0x20b8,0x2000,0x20d0,0x2100,0x2150,0x2190,0x2200,0x2300,0x2400,0x2440,0x2460,0x2500,0x2580,0x25a0,0x2600,
857 0x2700,0x27c0,0x27f0,0x2900,0x2980,0x2a00,0x2b00,0}, 1,
858 { { 0, 23, DWRITE_SCRIPT_SHAPES_DEFAULT }}
861 /* Braille */
862 {0x2800,0x2070,0x2000,0}, 1,
863 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
866 /* Glagolitic */
867 {0x2c00,0x2c12,0}, 1,
868 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }}
871 /* Coptic */
872 {0x2c80,0x3e2,0x1f00,0}, 2,
873 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT },
874 { 2, 1, DWRITE_SCRIPT_SHAPES_DEFAULT } }
877 /* Tifinagh */
878 {0x2d30,0x2d4a,0}, 1,
879 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }}
882 /* LRE/PDF */
883 {0x202a,0x202c,'a','b','c','\r',0}, 3,
884 { { 0, 2, DWRITE_SCRIPT_SHAPES_NO_VISUAL },
885 { 2, 3, DWRITE_SCRIPT_SHAPES_DEFAULT },
886 { 5, 1, DWRITE_SCRIPT_SHAPES_NO_VISUAL } }
888 /* keep this as end marker */
889 { {0} }
892 static void init_expected_sa(struct call_sequence **seq, const struct sa_test *test)
894 static const struct call_entry end_of_sequence = { LastKind };
895 int i;
897 flush_sequence(seq, 0);
899 /* add expected calls */
900 for (i = 0; i < test->item_count; i++)
902 struct call_entry call;
904 call.kind = ScriptAnalysis;
905 call.sa.pos = test->sa[i].pos;
906 call.sa.len = test->sa[i].len;
907 call.sa.shapes = test->sa[i].shapes;
908 add_call(seq, 0, &call);
911 /* and stop marker */
912 add_call(seq, 0, &end_of_sequence);
915 static void get_script_analysis(const WCHAR *str, DWRITE_SCRIPT_ANALYSIS *sa)
917 IDWriteTextAnalyzer *analyzer;
918 HRESULT hr;
920 g_source = str;
922 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
923 ok(hr == S_OK, "got 0x%08x\n", hr);
925 hr = IDWriteTextAnalyzer_AnalyzeScript(analyzer, &analysissource, 0, lstrlenW(g_source), &analysissink2);
926 ok(hr == S_OK, "got 0x%08x\n", hr);
928 *sa = g_sa;
931 static void test_AnalyzeScript(void)
933 const struct sa_test *ptr = sa_tests;
934 IDWriteTextAnalyzer *analyzer;
935 HRESULT hr;
937 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
938 ok(hr == S_OK, "got 0x%08x\n", hr);
940 while (*ptr->string)
942 g_source = ptr->string;
944 init_expected_sa(expected_seq, ptr);
945 hr = IDWriteTextAnalyzer_AnalyzeScript(analyzer, &analysissource, 0, lstrlenW(g_source), &analysissink);
946 ok(hr == S_OK, "got 0x%08x\n", hr);
947 ok_sequence(sequences, ANALYZER_ID, expected_seq[0]->sequence, wine_dbgstr_w(ptr->string), FALSE);
948 ptr++;
951 IDWriteTextAnalyzer_Release(analyzer);
954 struct linebreaks_test {
955 const WCHAR text[BREAKPOINT_COUNT+1];
956 DWRITE_LINE_BREAKPOINT bp[BREAKPOINT_COUNT];
959 static struct linebreaks_test linebreaks_tests[] = {
960 { {'A','-','B',' ','C',0x58a,'D',0x2010,'E',0x2012,'F',0x2013,'\t',0},
962 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, FALSE, FALSE },
963 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, FALSE, FALSE },
964 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, FALSE, FALSE },
965 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, TRUE, FALSE },
966 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, FALSE, FALSE },
967 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, FALSE, FALSE },
968 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, FALSE, FALSE },
969 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, FALSE, FALSE },
970 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, FALSE, FALSE },
971 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, FALSE, FALSE },
972 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, FALSE, FALSE },
973 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, FALSE, FALSE },
974 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, TRUE, FALSE }
977 { { 0 } }
980 static void compare_breakpoints(const struct linebreaks_test *test, DWRITE_LINE_BREAKPOINT *actual)
982 const WCHAR *text = test->text;
983 int cmp = memcmp(test->bp, actual, sizeof(*actual)*BREAKPOINT_COUNT);
984 ok(!cmp, "%s: got wrong breakpoint data\n", wine_dbgstr_w(test->text));
985 if (cmp) {
986 int i = 0;
987 while (*text) {
988 ok(!memcmp(&test->bp[i], &actual[i], sizeof(*actual)),
989 "%s: got (%d, %d, %d, %d), expected (%d, %d, %d, %d)\n",
990 wine_dbgstr_wn(&test->text[i], 1),
991 g_actual_bp[i].breakConditionBefore,
992 g_actual_bp[i].breakConditionAfter,
993 g_actual_bp[i].isWhitespace,
994 g_actual_bp[i].isSoftHyphen,
995 test->bp[i].breakConditionBefore,
996 test->bp[i].breakConditionAfter,
997 test->bp[i].isWhitespace,
998 test->bp[i].isSoftHyphen);
999 text++;
1000 i++;
1005 static void test_AnalyzeLineBreakpoints(void)
1007 static const WCHAR emptyW[] = {0};
1008 const struct linebreaks_test *ptr = linebreaks_tests;
1009 IDWriteTextAnalyzer *analyzer;
1010 HRESULT hr;
1012 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
1013 ok(hr == S_OK, "got 0x%08x\n", hr);
1015 g_source = emptyW;
1016 hr = IDWriteTextAnalyzer_AnalyzeLineBreakpoints(analyzer, &analysissource, 0, 0, &analysissink);
1017 ok(hr == S_OK, "got 0x%08x\n", hr);
1019 while (*ptr->text)
1021 g_source = ptr->text;
1023 memset(g_actual_bp, 0, sizeof(g_actual_bp));
1024 hr = IDWriteTextAnalyzer_AnalyzeLineBreakpoints(analyzer, &analysissource, 0, lstrlenW(g_source), &analysissink);
1025 ok(hr == S_OK, "got 0x%08x\n", hr);
1026 compare_breakpoints(ptr, g_actual_bp);
1028 ptr++;
1031 IDWriteTextAnalyzer_Release(analyzer);
1034 static void test_GetScriptProperties(void)
1036 IDWriteTextAnalyzer1 *analyzer1;
1037 IDWriteTextAnalyzer *analyzer;
1038 DWRITE_SCRIPT_ANALYSIS sa;
1039 DWRITE_SCRIPT_PROPERTIES props;
1040 HRESULT hr;
1042 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
1043 ok(hr == S_OK, "got 0x%08x\n", hr);
1045 hr = IDWriteTextAnalyzer_QueryInterface(analyzer, &IID_IDWriteTextAnalyzer1, (void**)&analyzer1);
1046 IDWriteTextAnalyzer_Release(analyzer);
1047 if (hr != S_OK) {
1048 win_skip("GetScriptProperties() is not supported.\n");
1049 return;
1052 sa.script = 1000;
1053 hr = IDWriteTextAnalyzer1_GetScriptProperties(analyzer1, sa, &props);
1054 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1056 if (0) /* crashes on native */
1057 hr = IDWriteTextAnalyzer1_GetScriptProperties(analyzer1, sa, NULL);
1059 sa.script = 0;
1060 hr = IDWriteTextAnalyzer1_GetScriptProperties(analyzer1, sa, &props);
1061 ok(hr == S_OK, "got 0x%08x\n", hr);
1063 IDWriteTextAnalyzer1_Release(analyzer1);
1066 struct textcomplexity_test {
1067 const WCHAR text[5];
1068 UINT32 length;
1069 BOOL simple;
1070 UINT32 len_read;
1073 static const struct textcomplexity_test textcomplexity_tests[] = {
1074 { {0}, 1, FALSE, 1 },
1075 { {0}, 0, TRUE, 0 },
1076 { {0x610,0}, 0, TRUE, 0 },
1077 { {'A','B','C','D',0}, 3, TRUE, 3 },
1078 { {'A','B','C','D',0}, 5, TRUE, 4 },
1079 { {'A','B','C','D',0}, 10, TRUE, 4 },
1080 { {'A',0x610,'C','D',0}, 1, TRUE, 1 },
1081 { {'A',0x610,'C','D',0}, 2, FALSE, 2 },
1082 { {0x610,'A','C','D',0}, 1, FALSE, 1 },
1083 { {0x610,'A','C','D',0}, 2, FALSE, 1 },
1084 { {0x610,0x610,'C','D',0}, 2, FALSE, 2 },
1085 { {0xd800,'A','B',0}, 1, FALSE, 1 },
1086 { {0xd800,'A','B',0}, 2, FALSE, 1 },
1087 { {0xdc00,'A','B',0}, 2, FALSE, 1 }
1090 static void test_GetTextComplexity(void)
1092 static const WCHAR textW[] = {'A','B','C',0};
1093 IDWriteTextAnalyzer1 *analyzer1;
1094 IDWriteTextAnalyzer *analyzer;
1095 IDWriteFontFace *fontface;
1096 UINT16 indices[10];
1097 BOOL simple;
1098 HRESULT hr;
1099 UINT32 len;
1100 int i;
1102 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
1103 ok(hr == S_OK, "got 0x%08x\n", hr);
1105 hr = IDWriteTextAnalyzer_QueryInterface(analyzer, &IID_IDWriteTextAnalyzer1, (void**)&analyzer1);
1106 IDWriteTextAnalyzer_Release(analyzer);
1107 if (hr != S_OK) {
1108 win_skip("GetTextComplexity() is not supported.\n");
1109 return;
1112 if (0) { /* crashes on native */
1113 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer1, NULL, 0, NULL, NULL, NULL, NULL);
1114 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer1, NULL, 0, NULL, NULL, &len, NULL);
1115 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer1, textW, 3, NULL, NULL, NULL, NULL);
1116 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer1, textW, 3, NULL, NULL, &len, NULL);
1117 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer1, textW, 3, NULL, &simple, NULL, NULL);
1120 len = 1;
1121 simple = TRUE;
1122 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer1, NULL, 0, NULL, &simple, &len, NULL);
1123 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1124 ok(len == 0, "got %d\n", len);
1125 ok(simple == FALSE, "got %d\n", simple);
1127 len = 1;
1128 simple = TRUE;
1129 indices[0] = 1;
1130 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer1, textW, 3, NULL, &simple, &len, NULL);
1131 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1132 ok(len == 0, "got %d\n", len);
1133 ok(simple == FALSE, "got %d\n", simple);
1134 ok(indices[0] == 1, "got %d\n", indices[0]);
1136 fontface = create_fontface();
1138 for (i = 0; i < sizeof(textcomplexity_tests)/sizeof(struct textcomplexity_test); i++) {
1139 const struct textcomplexity_test *ptr = &textcomplexity_tests[i];
1140 len = 1;
1141 simple = !ptr->simple;
1142 indices[0] = 0;
1143 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer1, ptr->text, ptr->length, fontface, &simple, &len, indices);
1144 ok(hr == S_OK, "%d: got 0x%08x\n", i, hr);
1145 ok(len == ptr->len_read, "%d: read length: got %d, expected %d\n", i, len, ptr->len_read);
1146 ok(simple == ptr->simple, "%d: simple: got %d, expected %d\n", i, simple, ptr->simple);
1147 if (simple && ptr->length)
1148 ok(indices[0] > 0, "%d: got %d\n", i, indices[0]);
1149 else
1150 ok(indices[0] == 0, "%d: got %d\n", i, indices[0]);
1153 IDWriteFontFace_Release(fontface);
1154 IDWriteTextAnalyzer1_Release(analyzer1);
1157 static void test_numbersubstitution(void)
1159 static const WCHAR dummyW[] = {'d','u','m','m','y',0};
1160 IDWriteNumberSubstitution *substitution;
1161 HRESULT hr;
1163 hr = IDWriteFactory_CreateNumberSubstitution(factory, DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE, NULL, FALSE, &substitution);
1164 ok(hr == S_OK, "got 0x%08x\n", hr);
1165 IDWriteNumberSubstitution_Release(substitution);
1167 /* invalid method */
1168 hr = IDWriteFactory_CreateNumberSubstitution(factory, DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL+1, NULL, FALSE, &substitution);
1169 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1171 /* invalid method */
1172 hr = IDWriteFactory_CreateNumberSubstitution(factory, -1, NULL, FALSE, &substitution);
1173 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1175 /* invalid locale */
1176 hr = IDWriteFactory_CreateNumberSubstitution(factory, DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL, dummyW, FALSE, &substitution);
1177 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1179 hr = IDWriteFactory_CreateNumberSubstitution(factory, DWRITE_NUMBER_SUBSTITUTION_METHOD_CONTEXTUAL, dummyW, FALSE, &substitution);
1180 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1182 hr = IDWriteFactory_CreateNumberSubstitution(factory, DWRITE_NUMBER_SUBSTITUTION_METHOD_NATIONAL, dummyW, FALSE, &substitution);
1183 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1185 /* invalid locale, but it's not needed for this method */
1186 hr = IDWriteFactory_CreateNumberSubstitution(factory, DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE, dummyW, FALSE, &substitution);
1187 ok(hr == S_OK, "got 0x%08x\n", hr);
1188 IDWriteNumberSubstitution_Release(substitution);
1191 static void test_GetGlyphs(void)
1193 static const WCHAR test1W[] = {'<','B',' ','C',0};
1194 static const WCHAR test2W[] = {'<','B','\t','C',0};
1195 static const WCHAR test3W[] = {0x202a,0x202c,0};
1196 DWRITE_SHAPING_GLYPH_PROPERTIES shapingprops[20];
1197 DWRITE_SHAPING_TEXT_PROPERTIES props[20];
1198 UINT32 maxglyphcount, actual_count;
1199 IDWriteTextAnalyzer *analyzer;
1200 IDWriteFontFace *fontface;
1201 DWRITE_SCRIPT_ANALYSIS sa;
1202 DWRITE_GLYPH_OFFSET offsets[10];
1203 UINT16 clustermap[10];
1204 UINT16 glyphs1[10];
1205 UINT16 glyphs2[10];
1206 FLOAT advances[10];
1207 HRESULT hr;
1209 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
1210 ok(hr == S_OK, "got 0x%08x\n", hr);
1212 fontface = create_fontface();
1214 maxglyphcount = 1;
1215 sa.script = 0;
1216 sa.shapes = DWRITE_SCRIPT_SHAPES_DEFAULT;
1217 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test1W, lstrlenW(test1W), fontface, FALSE, FALSE, &sa, NULL,
1218 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs1, shapingprops, &actual_count);
1219 ok(hr == E_NOT_SUFFICIENT_BUFFER, "got 0x%08x\n", hr);
1221 if (0) {
1222 /* NULL fontface - crashes on Windows */
1223 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test1W, lstrlenW(test1W), NULL, FALSE, FALSE, &sa, NULL,
1224 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs1, shapingprops, &actual_count);
1227 /* invalid script id */
1228 maxglyphcount = 10;
1229 actual_count = 0;
1230 sa.script = 999;
1231 sa.shapes = DWRITE_SCRIPT_SHAPES_DEFAULT;
1232 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test1W, lstrlenW(test1W), fontface, FALSE, FALSE, &sa, NULL,
1233 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs1, shapingprops, &actual_count);
1234 ok(hr == S_OK, "got 0x%08x\n", hr);
1235 ok(actual_count == 4, "got %d\n", actual_count);
1236 ok(sa.script == 999, "got %u\n", sa.script);
1238 /* no '\t' -> ' ' replacement */
1239 maxglyphcount = 10;
1240 actual_count = 0;
1241 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test1W, lstrlenW(test1W), fontface, FALSE, FALSE, &sa, NULL,
1242 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs1, shapingprops, &actual_count);
1243 ok(hr == S_OK, "got 0x%08x\n", hr);
1244 ok(actual_count == 4, "got %d\n", actual_count);
1246 actual_count = 0;
1247 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test2W, lstrlenW(test2W), fontface, FALSE, FALSE, &sa, NULL,
1248 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs2, shapingprops, &actual_count);
1249 ok(hr == S_OK, "got 0x%08x\n", hr);
1250 ok(actual_count == 4, "got %d\n", actual_count);
1251 ok(glyphs1[2] != glyphs2[2], "got %d\n", glyphs1[2]);
1253 /* check that mirroring works */
1254 maxglyphcount = 10;
1255 actual_count = 0;
1256 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test1W, lstrlenW(test1W), fontface, FALSE, FALSE, &sa, NULL,
1257 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs1, shapingprops, &actual_count);
1258 ok(hr == S_OK, "got 0x%08x\n", hr);
1259 ok(actual_count == 4, "got %d\n", actual_count);
1261 actual_count = 0;
1262 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test1W, lstrlenW(test1W), fontface, FALSE, TRUE, &sa, NULL,
1263 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs2, shapingprops, &actual_count);
1264 ok(hr == S_OK, "got 0x%08x\n", hr);
1265 ok(actual_count == 4, "got %d\n", actual_count);
1266 ok(glyphs1[0] != glyphs2[0], "got %d\n", glyphs1[0]);
1268 /* embedded control codes, with unknown script id 0 */
1269 actual_count = 0;
1270 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test3W, lstrlenW(test3W), fontface, FALSE, TRUE, &sa, NULL,
1271 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs1, shapingprops, &actual_count);
1272 ok(hr == S_OK, "got 0x%08x\n", hr);
1273 ok(actual_count == 2, "got %d\n", actual_count);
1274 ok(glyphs1[0] == 0, "got %d\n", glyphs1[0]);
1275 ok(glyphs1[1] == 0, "got %d\n", glyphs1[1]);
1276 ok(shapingprops[0].isClusterStart == 1, "got %d\n", shapingprops[0].isClusterStart);
1277 ok(shapingprops[0].isZeroWidthSpace == 0, "got %d\n", shapingprops[0].isZeroWidthSpace);
1278 ok(shapingprops[1].isClusterStart == 1, "got %d\n", shapingprops[1].isClusterStart);
1279 ok(shapingprops[1].isZeroWidthSpace == 0, "got %d\n", shapingprops[1].isZeroWidthSpace);
1280 ok(clustermap[0] == 0, "got %d\n", clustermap[0]);
1281 ok(clustermap[1] == 1, "got %d\n", clustermap[1]);
1283 memset(advances, 0, sizeof(advances));
1284 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, test3W, clustermap, props, lstrlenW(test3W),
1285 glyphs1, shapingprops, actual_count, fontface, 10.0, FALSE, FALSE, &sa, NULL, NULL,
1286 NULL, 0, advances, offsets);
1287 ok(hr == S_OK, "got 0x%08x\n", hr);
1288 ok(advances[0] == 10.0, "got %.2f\n", advances[0]);
1289 ok(advances[1] == 10.0, "got %.2f\n", advances[1]);
1291 /* embedded control codes with proper script */
1292 sa.script = 0;
1293 get_script_analysis(test3W, &sa);
1294 ok(sa.script != 0, "got %d\n", sa.script);
1295 actual_count = 0;
1296 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test3W, lstrlenW(test3W), fontface, FALSE, FALSE, &sa, NULL,
1297 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs1, shapingprops, &actual_count);
1298 ok(hr == S_OK, "got 0x%08x\n", hr);
1299 ok(actual_count == 2, "got %d\n", actual_count);
1300 ok(glyphs1[0] == 0, "got %d\n", glyphs1[0]);
1301 ok(glyphs1[1] == 0, "got %d\n", glyphs1[1]);
1302 ok(shapingprops[0].isClusterStart == 1, "got %d\n", shapingprops[0].isClusterStart);
1303 ok(shapingprops[0].isZeroWidthSpace == 0, "got %d\n", shapingprops[0].isZeroWidthSpace);
1304 ok(shapingprops[1].isClusterStart == 1, "got %d\n", shapingprops[1].isClusterStart);
1305 ok(shapingprops[1].isZeroWidthSpace == 0, "got %d\n", shapingprops[1].isZeroWidthSpace);
1306 ok(clustermap[0] == 0, "got %d\n", clustermap[0]);
1307 ok(clustermap[1] == 1, "got %d\n", clustermap[1]);
1309 memset(advances, 0, sizeof(advances));
1310 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, test3W, clustermap, props, lstrlenW(test3W),
1311 glyphs1, shapingprops, actual_count, fontface, 10.0, FALSE, FALSE, &sa, NULL, NULL,
1312 NULL, 0, advances, offsets);
1313 ok(hr == S_OK, "got 0x%08x\n", hr);
1314 ok(advances[0] == 10.0, "got %.2f\n", advances[0]);
1315 ok(advances[1] == 10.0, "got %.2f\n", advances[1]);
1317 IDWriteTextAnalyzer_Release(analyzer);
1318 IDWriteFontFace_Release(fontface);
1321 static BOOL has_feature(const DWRITE_FONT_FEATURE_TAG *tags, UINT32 count, DWRITE_FONT_FEATURE_TAG feature)
1323 UINT32 i;
1325 for (i = 0; i < count; i++)
1326 if (tags[i] == feature) return TRUE;
1327 return FALSE;
1330 static void test_GetTypographicFeatures(void)
1332 static const WCHAR localeW[] = {'c','a','d','a','b','r','a',0};
1333 static const WCHAR arabicW[] = {0x064a,0x064f,0x0633,0};
1334 static const WCHAR abcW[] = {'a','b','c',0};
1335 DWRITE_FONT_FEATURE_TAG tags[20];
1336 IDWriteTextAnalyzer2 *analyzer2;
1337 IDWriteTextAnalyzer *analyzer;
1338 IDWriteFontFace *fontface;
1339 DWRITE_SCRIPT_ANALYSIS sa;
1340 UINT32 count;
1341 HRESULT hr;
1342 BOOL ret;
1344 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
1345 ok(hr == S_OK, "got 0x%08x\n", hr);
1347 hr = IDWriteTextAnalyzer_QueryInterface(analyzer, &IID_IDWriteTextAnalyzer2, (void**)&analyzer2);
1348 IDWriteTextAnalyzer_Release(analyzer);
1349 if (hr != S_OK) {
1350 win_skip("GetTypographicFeatures() is not supported.\n");
1351 return;
1354 fontface = create_fontface();
1356 get_script_analysis(abcW, &sa);
1357 count = 0;
1358 hr = IDWriteTextAnalyzer2_GetTypographicFeatures(analyzer2, fontface, sa, NULL, 0, &count, NULL);
1359 todo_wine {
1360 ok(hr == E_NOT_SUFFICIENT_BUFFER, "got 0x%08x\n", hr);
1361 ok(count > 0, "got %u\n", count);
1363 /* invalid locale name is ignored */
1364 get_script_analysis(abcW, &sa);
1365 count = 0;
1366 hr = IDWriteTextAnalyzer2_GetTypographicFeatures(analyzer2, fontface, sa, localeW, 0, &count, NULL);
1367 todo_wine {
1368 ok(hr == E_NOT_SUFFICIENT_BUFFER, "got 0x%08x\n", hr);
1369 ok(count > 0, "got %u\n", count);
1371 /* both GSUB and GPOS features are reported */
1372 get_script_analysis(arabicW, &sa);
1373 memset(tags, 0, sizeof(tags));
1374 count = 0;
1375 hr = IDWriteTextAnalyzer2_GetTypographicFeatures(analyzer2, fontface, sa, NULL, sizeof(tags)/sizeof(tags[0]), &count, tags);
1376 ok(hr == S_OK, "got 0x%08x\n", hr);
1377 todo_wine {
1378 ok(count > 0, "got %u\n", count);
1379 ret = has_feature(tags, count, DWRITE_FONT_FEATURE_TAG_CONTEXTUAL_ALTERNATES);
1380 ok(ret, "expected 'calt' feature\n");
1381 ret = has_feature(tags, count, DWRITE_FONT_FEATURE_TAG_MARK_TO_MARK_POSITIONING);
1382 ok(ret, "expected 'mkmk' feature\n");
1384 get_script_analysis(abcW, &sa);
1385 memset(tags, 0, sizeof(tags));
1386 count = 0;
1387 hr = IDWriteTextAnalyzer2_GetTypographicFeatures(analyzer2, fontface, sa, NULL, sizeof(tags)/sizeof(tags[0]), &count, tags);
1388 ok(hr == S_OK, "got 0x%08x\n", hr);
1389 todo_wine {
1390 ok(count > 0, "got %u\n", count);
1391 ret = has_feature(tags, count, DWRITE_FONT_FEATURE_TAG_GLYPH_COMPOSITION_DECOMPOSITION);
1392 ok(ret, "expected 'ccmp' feature\n");
1393 ret = has_feature(tags, count, DWRITE_FONT_FEATURE_TAG_MARK_TO_MARK_POSITIONING);
1394 ok(ret, "expected 'mkmk' feature\n");
1396 ret = has_feature(tags, count, DWRITE_FONT_FEATURE_TAG_CONTEXTUAL_ALTERNATES);
1397 ok(!ret, "unexpected 'calt' feature\n");
1399 IDWriteFontFace_Release(fontface);
1400 IDWriteTextAnalyzer2_Release(analyzer2);
1403 static void test_GetGlyphPlacements(void)
1405 DWRITE_SHAPING_GLYPH_PROPERTIES glyphprops[2];
1406 DWRITE_SHAPING_TEXT_PROPERTIES textprops[2];
1407 static const WCHAR aW[] = {'A','D',0};
1408 UINT16 clustermap[2], glyphs[2];
1409 DWRITE_GLYPH_OFFSET offsets[2];
1410 IDWriteTextAnalyzer *analyzer;
1411 IDWriteFontFace *fontface;
1412 DWRITE_SCRIPT_ANALYSIS sa;
1413 FLOAT advances[2];
1414 UINT32 count, len;
1415 WCHAR *path;
1416 HRESULT hr;
1418 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
1419 ok(hr == S_OK, "got 0x%08x\n", hr);
1421 path = create_testfontfile(test_fontfile);
1422 fontface = create_testfontface(path);
1424 get_script_analysis(aW, &sa);
1425 count = 0;
1426 len = lstrlenW(aW);
1427 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, aW, len, fontface, FALSE, FALSE, &sa, NULL,
1428 NULL, NULL, NULL, 0, len, clustermap, textprops, glyphs, glyphprops, &count);
1429 ok(hr == S_OK, "got 0x%08x\n", hr);
1430 ok(count == 2, "got %u\n", count);
1432 /* just return on zero glyphs */
1433 advances[0] = advances[1] = 1.0;
1434 offsets[0].advanceOffset = offsets[0].ascenderOffset = 2.0;
1435 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, clustermap, textprops,
1436 len, glyphs, glyphprops, 0, fontface, 0.0, FALSE, FALSE, &sa, NULL, NULL,
1437 NULL, 0, advances, offsets);
1438 ok(hr == S_OK, "got 0x%08x\n", hr);
1439 ok(advances[0] == 1.0, "got %.2f\n", advances[0]);
1440 ok(offsets[0].advanceOffset == 2.0 && offsets[0].ascenderOffset == 2.0, "got %.2f,%.2f\n",
1441 offsets[0].advanceOffset, offsets[0].ascenderOffset);
1443 /* advances/offsets are scaled with provided font emSize and designed eM box size */
1444 advances[0] = advances[1] = 1.0;
1445 memset(offsets, 0xcc, sizeof(offsets));
1446 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, clustermap, textprops,
1447 len, glyphs, glyphprops, len, fontface, 0.0, FALSE, FALSE, &sa, NULL, NULL,
1448 NULL, 0, advances, offsets);
1449 ok(hr == S_OK, "got 0x%08x\n", hr);
1450 ok(advances[0] == 0.0, "got %.2f\n", advances[0]);
1451 ok(offsets[0].advanceOffset == 0.0 && offsets[0].ascenderOffset == 0.0, "got %.2f,%.2f\n",
1452 offsets[0].advanceOffset, offsets[0].ascenderOffset);
1454 advances[0] = advances[1] = 1.0;
1455 memset(offsets, 0xcc, sizeof(offsets));
1456 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, clustermap, textprops,
1457 len, glyphs, glyphprops, len, fontface, 2048.0, FALSE, FALSE, &sa, NULL, NULL,
1458 NULL, 0, advances, offsets);
1459 ok(hr == S_OK, "got 0x%08x\n", hr);
1460 ok(advances[0] == 1000.0, "got %.2f\n", advances[0]);
1461 ok(offsets[0].advanceOffset == 0.0 && offsets[0].ascenderOffset == 0.0, "got %.2f,%.2f\n",
1462 offsets[0].advanceOffset, offsets[0].ascenderOffset);
1464 advances[0] = advances[1] = 1.0;
1465 memset(offsets, 0xcc, sizeof(offsets));
1466 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, clustermap, textprops,
1467 len, glyphs, glyphprops, len, fontface, 1024.0, FALSE, FALSE, &sa, NULL, NULL,
1468 NULL, 0, advances, offsets);
1469 ok(hr == S_OK, "got 0x%08x\n", hr);
1470 ok(advances[0] == 500.0, "got %.2f\n", advances[0]);
1471 ok(advances[1] == 500.0, "got %.2f\n", advances[1]);
1472 ok(offsets[0].advanceOffset == 0.0 && offsets[0].ascenderOffset == 0.0, "got %.2f,%.2f\n",
1473 offsets[0].advanceOffset, offsets[0].ascenderOffset);
1475 advances[0] = advances[1] = 1.0;
1476 memset(offsets, 0xcc, sizeof(offsets));
1477 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, clustermap, textprops,
1478 len, glyphs, glyphprops, len, fontface, 20.48, FALSE, FALSE, &sa, NULL, NULL,
1479 NULL, 0, advances, offsets);
1480 ok(hr == S_OK, "got 0x%08x\n", hr);
1481 ok(advances[0] == 10.0, "got %.2f\n", advances[0]);
1482 ok(advances[1] == 10.0, "got %.2f\n", advances[1]);
1483 ok(offsets[0].advanceOffset == 0.0 && offsets[0].ascenderOffset == 0.0, "got %.2f,%.2f\n",
1484 offsets[0].advanceOffset, offsets[0].ascenderOffset);
1486 /* without clustermap */
1487 advances[0] = advances[1] = 1.0;
1488 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, NULL, textprops,
1489 len, glyphs, glyphprops, len, fontface, 1024.0, FALSE, FALSE, &sa, NULL, NULL,
1490 NULL, 0, advances, offsets);
1491 ok(hr == S_OK, "got 0x%08x\n", hr);
1492 ok(advances[0] == 500.0, "got %.2f\n", advances[0]);
1493 ok(advances[1] == 500.0, "got %.2f\n", advances[1]);
1495 /* it's happy to use negative size too */
1496 advances[0] = advances[1] = 1.0;
1497 memset(offsets, 0xcc, sizeof(offsets));
1498 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, clustermap, textprops,
1499 len, glyphs, glyphprops, len, fontface, -10.24, FALSE, FALSE, &sa, NULL, NULL,
1500 NULL, 0, advances, offsets);
1501 ok(hr == S_OK, "got 0x%08x\n", hr);
1502 ok(advances[0] == -5.0, "got %.2f\n", advances[0]);
1503 ok(offsets[0].advanceOffset == 0.0 && offsets[0].ascenderOffset == 0.0, "got %.2f,%.2f\n",
1504 offsets[0].advanceOffset, offsets[0].ascenderOffset);
1506 IDWriteTextAnalyzer_Release(analyzer);
1507 IDWriteFontFace_Release(fontface);
1508 DELETE_FONTFILE(path);
1511 struct spacing_test {
1512 FLOAT leading;
1513 FLOAT trailing;
1514 FLOAT min_advance;
1515 FLOAT advances[3];
1516 FLOAT offsets[3];
1517 FLOAT modified_advances[3];
1518 FLOAT modified_offsets[3];
1519 BOOL single_cluster;
1520 BOOL is_ZWS[3];
1523 static const struct spacing_test spacing_tests[] = {
1524 { 0.0, 0.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 } }, /* 0 */
1525 { 0.0, 0.0, 2.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 } },
1526 { 1.0, 0.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 11.0, 12.0 }, { 3.0, 4.0 } },
1527 { 1.0, 1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 12.0, 13.0 }, { 3.0, 4.0 } },
1528 { 1.0, -1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 3.0, 4.0 } },
1529 { 0.0, -10.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 0.0, 1.0 }, { 2.0, 3.0 } }, /* 5 */
1530 { -5.0, -4.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 5.0, 5.0 }, { -1.0, -0.5 } },
1531 { -5.0, -5.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 5.0, 5.0 }, { -0.5, 0.0 } },
1532 { 2.0, 0.0, 5.0, { 1.0, 2.0 }, { 2.0, 3.0 }, { 7.0, 7.0 }, { 6.0, 6.5 } },
1533 { 2.0, 1.0, 5.0, { 1.0, 2.0 }, { 2.0, 3.0 }, { 8.0, 8.0 }, { 6.0, 6.5 } },
1534 { 2.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 7.0, 7.0 }, { 4.0, 5.0 } }, /* 10 */
1535 { 1.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 6.0, 6.0 }, { 3.0, 4.0 } },
1536 { -10.0, 1.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 6.0, 6.0 }, { -3.0, -3.0 } },
1537 { 0.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 5.0, 5.0 }, { 2.0, 3.0 } },
1538 { 1.0, -10.0, -5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 0.0, 0.0 }, { 2.0, 3.0 } },
1539 { -10.0, 1.0, 5.0, { 8.0, 11.0 }, { 2.0, 3.0 }, { 6.0, 6.0 }, { -1.0, -3.0 } }, /* 15 */
1540 /* cluster of more than 1 glyph */
1541 { 0.0, 0.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 }, TRUE },
1542 { 1.0, 0.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.5 }, { 11.0, 11.0 }, { 3.0, 3.5 }, TRUE },
1543 { 1.0, 1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 11.0, 12.0 }, { 3.0, 3.0 }, TRUE },
1544 { 1.0, -1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 11.0, 10.0 }, { 3.0, 3.0 }, TRUE },
1545 { 0.0, -10.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 1.0 }, { 2.0, 3.0 }, TRUE }, /* 20 */
1546 { 0.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 1.0 }, { 2.0, 3.0 }, TRUE },
1547 { 1.0, -10.0, -5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 0.0, 0.0 }, { 2.0, 3.0 }, TRUE },
1548 { -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 },
1549 { -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 },
1550 { -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 */
1551 { -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 },
1552 { 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 },
1553 { -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 },
1554 /* isZeroWidthSpace set */
1555 { 1.0, 0.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 12.0 }, { 2.0, 4.0 }, FALSE, { TRUE, FALSE } },
1556 { 1.0, 1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 13.0 }, { 2.0, 4.0 }, FALSE, { TRUE, FALSE } }, /* 30 */
1557 { 1.0, -1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 3.0, 3.0 }, FALSE, { FALSE, TRUE } },
1558 { 0.0, -10.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 1.0 }, { 2.0, 3.0 }, FALSE, { TRUE, FALSE } },
1559 { -5.0, -4.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 5.0, 11.0 }, { -1.0, 3.0 }, FALSE, { FALSE, TRUE } },
1560 { -5.0, -5.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 }, FALSE, { TRUE, TRUE } },
1561 { 2.0, 0.0, 5.0, { 1.0, 2.0 }, { 2.0, 3.0 }, { 7.0, 2.0 }, { 6.0, 3.0 }, FALSE, { FALSE, TRUE } }, /* 35 */
1562 { 2.0, 1.0, 5.0, { 1.0, 2.0 }, { 2.0, 3.0 }, { 8.0, 2.0 }, { 6.0, 3.0 }, FALSE, { FALSE, TRUE } },
1563 { 2.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 }, FALSE, { TRUE, TRUE } },
1564 { 1.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 6.0, 11.0 }, { 3.0, 3.0 }, FALSE, { FALSE, TRUE } },
1565 { -10.0, 1.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 }, FALSE, { TRUE, TRUE } },
1566 { 0.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 5.0, 11.0 }, { 2.0, 3.0 }, FALSE, { FALSE, TRUE } }, /* 40 */
1567 { 1.0, -10.0, -5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 0.0, 0.0 }, { 2.0, 3.0 }, FALSE, { TRUE, FALSE } },
1568 { -10.0, 1.0, 5.0, { 8.0, 11.0 }, { 2.0, 3.0 }, { 6.0, 11.0 }, { -1.0, 3.0 }, FALSE, { FALSE, TRUE } },
1571 static void test_ApplyCharacterSpacing(void)
1573 DWRITE_SHAPING_GLYPH_PROPERTIES props[3];
1574 IDWriteTextAnalyzer1 *analyzer1;
1575 IDWriteTextAnalyzer *analyzer;
1576 UINT16 clustermap[2];
1577 HRESULT hr;
1578 int i;
1580 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
1581 ok(hr == S_OK, "got 0x%08x\n", hr);
1583 hr = IDWriteTextAnalyzer_QueryInterface(analyzer, &IID_IDWriteTextAnalyzer1, (void**)&analyzer1);
1584 IDWriteTextAnalyzer_Release(analyzer);
1585 if (hr != S_OK) {
1586 win_skip("ApplyCharacterSpacing() is not supported.\n");
1587 return;
1590 for (i = 0; i < sizeof(spacing_tests)/sizeof(spacing_tests[0]); i++) {
1591 const struct spacing_test *ptr = spacing_tests + i;
1592 DWRITE_GLYPH_OFFSET offsets[3];
1593 UINT32 glyph_count;
1594 FLOAT advances[3];
1596 offsets[0].advanceOffset = ptr->offsets[0];
1597 offsets[1].advanceOffset = ptr->offsets[1];
1598 offsets[2].advanceOffset = ptr->offsets[2];
1599 /* Ascender offsets are never thouched as spacing applies in reading direction only,
1600 we'll only test them to see if they are not changed */
1601 offsets[0].ascenderOffset = 23.0;
1602 offsets[1].ascenderOffset = 32.0;
1603 offsets[2].ascenderOffset = 31.0;
1605 glyph_count = ptr->advances[2] > 0.0 ? 3 : 2;
1606 if (ptr->single_cluster) {
1607 clustermap[0] = 0;
1608 clustermap[1] = 0;
1610 else {
1611 /* trivial case with one glyph per cluster */
1612 clustermap[0] = 0;
1613 clustermap[1] = 1;
1616 advances[0] = advances[1] = 123.45;
1617 memset(props, 0, sizeof(props));
1618 props[0].isZeroWidthSpace = ptr->is_ZWS[0];
1619 props[1].isZeroWidthSpace = ptr->is_ZWS[1];
1620 props[2].isZeroWidthSpace = ptr->is_ZWS[2];
1622 hr = IDWriteTextAnalyzer1_ApplyCharacterSpacing(analyzer1,
1623 ptr->leading,
1624 ptr->trailing,
1625 ptr->min_advance,
1626 sizeof(clustermap)/sizeof(clustermap[0]),
1627 glyph_count,
1628 clustermap,
1629 ptr->advances,
1630 offsets,
1631 props,
1632 advances,
1633 offsets);
1634 /* invalid argument cases */
1635 if (ptr->min_advance < 0.0)
1636 ok(hr == E_INVALIDARG, "%d: got 0x%08x\n", i, hr);
1637 else
1638 ok(hr == S_OK, "%d: got 0x%08x\n", i, hr);
1639 if (hr == S_OK) {
1640 ok(ptr->modified_advances[0] == advances[0], "%d: got advance[0] %.2f, expected %.2f\n", i, advances[0], ptr->modified_advances[0]);
1641 ok(ptr->modified_advances[1] == advances[1], "%d: got advance[1] %.2f, expected %.2f\n", i, advances[1], ptr->modified_advances[1]);
1642 if (glyph_count > 2)
1643 ok(ptr->modified_advances[2] == advances[2], "%d: got advance[2] %.2f, expected %.2f\n", i, advances[2], ptr->modified_advances[2]);
1645 ok(ptr->modified_offsets[0] == offsets[0].advanceOffset, "%d: got offset[0] %.2f, expected %.2f\n", i,
1646 offsets[0].advanceOffset, ptr->modified_offsets[0]);
1647 ok(ptr->modified_offsets[1] == offsets[1].advanceOffset, "%d: got offset[1] %.2f, expected %.2f\n", i,
1648 offsets[1].advanceOffset, ptr->modified_offsets[1]);
1649 if (glyph_count > 2)
1650 ok(ptr->modified_offsets[2] == offsets[2].advanceOffset, "%d: got offset[2] %.2f, expected %.2f\n", i,
1651 offsets[2].advanceOffset, ptr->modified_offsets[2]);
1653 ok(offsets[0].ascenderOffset == 23.0, "%d: unexpected ascenderOffset %.2f\n", i, offsets[0].ascenderOffset);
1654 ok(offsets[1].ascenderOffset == 32.0, "%d: unexpected ascenderOffset %.2f\n", i, offsets[1].ascenderOffset);
1655 ok(offsets[2].ascenderOffset == 31.0, "%d: unexpected ascenderOffset %.2f\n", i, offsets[2].ascenderOffset);
1657 else {
1658 ok(ptr->modified_advances[0] == advances[0], "%d: got advance[0] %.2f, expected %.2f\n", i, advances[0], ptr->modified_advances[0]);
1659 ok(ptr->modified_advances[1] == advances[1], "%d: got advance[1] %.2f, expected %.2f\n", i, advances[1], ptr->modified_advances[1]);
1660 ok(ptr->offsets[0] == offsets[0].advanceOffset, "%d: got offset[0] %.2f, expected %.2f\n", i,
1661 offsets[0].advanceOffset, ptr->modified_offsets[0]);
1662 ok(ptr->offsets[1] == offsets[1].advanceOffset, "%d: got offset[1] %.2f, expected %.2f\n", i,
1663 offsets[1].advanceOffset, ptr->modified_offsets[1]);
1664 ok(offsets[0].ascenderOffset == 23.0, "%d: unexpected ascenderOffset %.2f\n", i, offsets[0].ascenderOffset);
1665 ok(offsets[1].ascenderOffset == 32.0, "%d: unexpected ascenderOffset %.2f\n", i, offsets[1].ascenderOffset);
1669 IDWriteTextAnalyzer1_Release(analyzer1);
1672 START_TEST(analyzer)
1674 HRESULT hr;
1676 hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_ISOLATED, &IID_IDWriteFactory, (IUnknown**)&factory);
1677 ok(hr == S_OK, "got 0x%08x\n", hr);
1678 if (hr != S_OK)
1680 win_skip("failed to create factory\n");
1681 return;
1684 init_call_sequences(sequences, NUM_CALL_SEQUENCES);
1685 init_call_sequences(expected_seq, 1);
1687 test_AnalyzeScript();
1688 test_AnalyzeLineBreakpoints();
1689 test_GetScriptProperties();
1690 test_GetTextComplexity();
1691 test_GetGlyphs();
1692 test_numbersubstitution();
1693 test_GetTypographicFeatures();
1694 test_GetGlyphPlacements();
1695 test_ApplyCharacterSpacing();
1697 IDWriteFactory_Release(factory);