gphoto2.ds: Set supported groups.
[wine.git] / dlls / dwrite / tests / analyzer.c
blobc5a0b0991f8e83ea521a478f65e16cf6edf9967b
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 static IDWriteFactory *factory;
35 static const WCHAR test_fontfile[] = {'w','i','n','e','_','t','e','s','t','_','f','o','n','t','.','t','t','f',0};
37 #define LRE 0x202a
38 #define RLE 0x202b
39 #define PDF 0x202c
40 #define LRO 0x202d
41 #define RLO 0x202e
42 #define LRI 0x2066
43 #define RLI 0x2067
44 #define FSI 0x2068
45 #define PDI 0x2069
47 enum analysis_kind {
48 ScriptAnalysis,
49 LastKind
52 static const char *get_analysis_kind_name(enum analysis_kind kind)
54 switch (kind)
56 case ScriptAnalysis:
57 return "ScriptAnalysis";
58 default:
59 return "unknown";
63 struct script_analysis {
64 UINT32 pos;
65 UINT32 len;
66 DWRITE_SCRIPT_SHAPES shapes;
69 struct call_entry {
70 enum analysis_kind kind;
71 struct script_analysis sa;
74 struct testcontext {
75 enum analysis_kind kind;
76 BOOL todo;
77 int *failcount;
78 const char *file;
79 int line;
82 struct call_sequence
84 int count;
85 int size;
86 struct call_entry *sequence;
89 #define NUM_CALL_SEQUENCES 1
90 #define ANALYZER_ID 0
91 static struct call_sequence *sequences[NUM_CALL_SEQUENCES];
92 static struct call_sequence *expected_seq[1];
94 static void add_call(struct call_sequence **seq, int sequence_index, const struct call_entry *call)
96 struct call_sequence *call_seq = seq[sequence_index];
98 if (!call_seq->sequence)
100 call_seq->size = 10;
101 call_seq->sequence = HeapAlloc(GetProcessHeap(), 0,
102 call_seq->size * sizeof (struct call_entry));
105 if (call_seq->count == call_seq->size)
107 call_seq->size *= 2;
108 call_seq->sequence = HeapReAlloc(GetProcessHeap(), 0,
109 call_seq->sequence,
110 call_seq->size * sizeof (struct call_entry));
113 assert(call_seq->sequence);
115 call_seq->sequence[call_seq->count++] = *call;
118 static inline void flush_sequence(struct call_sequence **seg, int sequence_index)
120 struct call_sequence *call_seq = seg[sequence_index];
122 HeapFree(GetProcessHeap(), 0, call_seq->sequence);
123 call_seq->sequence = NULL;
124 call_seq->count = call_seq->size = 0;
127 static void init_call_sequences(struct call_sequence **seq, int n)
129 int i;
131 for (i = 0; i < n; i++)
132 seq[i] = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct call_sequence));
135 static void test_uint(UINT32 actual, UINT32 expected, const char *name, const struct testcontext *ctxt)
137 if (expected != actual && ctxt->todo)
139 (*ctxt->failcount)++;
140 ok_(ctxt->file, ctxt->line) (0, "%s: \"%s\" expecting %u, got %u\n", get_analysis_kind_name(ctxt->kind), name, expected, actual);
142 else
143 ok_(ctxt->file, ctxt->line) (expected == actual, "%s: \"%s\" expecting %u, got %u\n", get_analysis_kind_name(ctxt->kind), name,
144 expected, actual);
147 static void ok_sequence_(struct call_sequence **seq, int sequence_index,
148 const struct call_entry *expected, const char *context, BOOL todo,
149 const char *file, int line)
151 struct call_sequence *call_seq = seq[sequence_index];
152 static const struct call_entry end_of_sequence = { LastKind };
153 const struct call_entry *actual, *sequence;
154 int failcount = 0;
155 struct testcontext ctxt;
157 add_call(seq, sequence_index, &end_of_sequence);
159 sequence = call_seq->sequence;
160 actual = sequence;
162 ctxt.failcount = &failcount;
163 ctxt.todo = todo;
164 ctxt.file = file;
165 ctxt.line = line;
167 while (expected->kind != LastKind && actual->kind != LastKind)
169 if (expected->kind == actual->kind)
171 ctxt.kind = expected->kind;
173 switch (actual->kind)
175 case ScriptAnalysis:
176 test_uint(actual->sa.pos, expected->sa.pos, "position", &ctxt);
177 test_uint(actual->sa.len, expected->sa.len, "length", &ctxt);
178 test_uint(actual->sa.shapes, expected->sa.shapes, "shapes", &ctxt);
179 break;
180 default:
181 ok(0, "%s: callback not handled, %s\n", context, get_analysis_kind_name(actual->kind));
183 expected++;
184 actual++;
186 else if (todo)
188 failcount++;
189 todo_wine
191 ok_(file, line) (0, "%s: call %s was expected, but got call %s instead\n",
192 context, get_analysis_kind_name(expected->kind), get_analysis_kind_name(actual->kind));
195 flush_sequence(seq, sequence_index);
196 return;
198 else
200 ok_(file, line) (0, "%s: call %s was expected, but got call %s instead\n",
201 context, get_analysis_kind_name(expected->kind), get_analysis_kind_name(actual->kind));
202 expected++;
203 actual++;
207 if (todo)
209 todo_wine
211 if (expected->kind != LastKind || actual->kind != LastKind)
213 failcount++;
214 ok_(file, line) (0, "%s: the call sequence is not complete: expected %s - actual %s\n",
215 context, get_analysis_kind_name(expected->kind), get_analysis_kind_name(actual->kind));
219 else if (expected->kind != LastKind || actual->kind != LastKind)
221 ok_(file, line) (0, "%s: the call sequence is not complete: expected %s - actual %s\n",
222 context, get_analysis_kind_name(expected->kind), get_analysis_kind_name(actual->kind));
225 if (todo && !failcount) /* succeeded yet marked todo */
227 todo_wine
229 ok_(file, line)(1, "%s: marked \"todo_wine\" but succeeds\n", context);
233 flush_sequence(seq, sequence_index);
236 #define ok_sequence(seq, index, exp, contx, todo) \
237 ok_sequence_(seq, index, (exp), (contx), (todo), __FILE__, __LINE__)
239 static HRESULT WINAPI analysissink_QueryInterface(IDWriteTextAnalysisSink *iface, REFIID riid, void **obj)
241 if (IsEqualIID(riid, &IID_IDWriteTextAnalysisSink) || IsEqualIID(riid, &IID_IUnknown))
243 *obj = iface;
244 return S_OK;
247 *obj = NULL;
248 return E_NOINTERFACE;
251 static ULONG WINAPI analysissink_AddRef(IDWriteTextAnalysisSink *iface)
253 return 2;
256 static ULONG WINAPI analysissink_Release(IDWriteTextAnalysisSink *iface)
258 return 1;
261 static HRESULT WINAPI analysissink_SetScriptAnalysis(IDWriteTextAnalysisSink *iface,
262 UINT32 position, UINT32 length, DWRITE_SCRIPT_ANALYSIS const* sa)
264 struct call_entry entry;
265 entry.kind = ScriptAnalysis;
266 entry.sa.pos = position;
267 entry.sa.len = length;
268 entry.sa.shapes = sa->shapes;
269 add_call(sequences, ANALYZER_ID, &entry);
270 return S_OK;
273 static DWRITE_SCRIPT_ANALYSIS g_sa;
274 static HRESULT WINAPI analysissink_SetScriptAnalysis2(IDWriteTextAnalysisSink *iface,
275 UINT32 position, UINT32 length, DWRITE_SCRIPT_ANALYSIS const* sa)
277 g_sa = *sa;
278 return S_OK;
281 #define BREAKPOINT_COUNT 20
282 static DWRITE_LINE_BREAKPOINT g_actual_bp[BREAKPOINT_COUNT];
284 static HRESULT WINAPI analysissink_SetLineBreakpoints(IDWriteTextAnalysisSink *iface,
285 UINT32 position,
286 UINT32 length,
287 DWRITE_LINE_BREAKPOINT const* breakpoints)
289 if (position + length > BREAKPOINT_COUNT) {
290 ok(0, "SetLineBreakpoints: reported pos=%u, len=%u overflows expected length %d\n", position, length, BREAKPOINT_COUNT);
291 return E_FAIL;
293 memcpy(&g_actual_bp[position], breakpoints, length*sizeof(DWRITE_LINE_BREAKPOINT));
294 return S_OK;
297 #define BIDI_LEVELS_COUNT 10
298 static UINT8 g_explicit_levels[BIDI_LEVELS_COUNT];
299 static UINT8 g_resolved_levels[BIDI_LEVELS_COUNT];
300 static HRESULT WINAPI analysissink_SetBidiLevel(IDWriteTextAnalysisSink *iface,
301 UINT32 position,
302 UINT32 length,
303 UINT8 explicitLevel,
304 UINT8 resolvedLevel)
306 if (position + length > BIDI_LEVELS_COUNT) {
307 ok(0, "SetBidiLevel: reported pos=%u, len=%u overflows expected length %d\n", position, length, BIDI_LEVELS_COUNT);
308 return E_FAIL;
310 memset(g_explicit_levels + position, explicitLevel, length);
311 memset(g_resolved_levels + position, resolvedLevel, length);
312 return S_OK;
315 static HRESULT WINAPI analysissink_SetNumberSubstitution(IDWriteTextAnalysisSink *iface,
316 UINT32 position,
317 UINT32 length,
318 IDWriteNumberSubstitution* substitution)
320 ok(0, "unexpected\n");
321 return E_NOTIMPL;
324 static IDWriteTextAnalysisSinkVtbl analysissinkvtbl = {
325 analysissink_QueryInterface,
326 analysissink_AddRef,
327 analysissink_Release,
328 analysissink_SetScriptAnalysis,
329 analysissink_SetLineBreakpoints,
330 analysissink_SetBidiLevel,
331 analysissink_SetNumberSubstitution
334 static IDWriteTextAnalysisSinkVtbl analysissinkvtbl2 = {
335 analysissink_QueryInterface,
336 analysissink_AddRef,
337 analysissink_Release,
338 analysissink_SetScriptAnalysis2,
339 analysissink_SetLineBreakpoints,
340 analysissink_SetBidiLevel,
341 analysissink_SetNumberSubstitution
344 static IDWriteTextAnalysisSink analysissink = { &analysissinkvtbl };
345 static IDWriteTextAnalysisSink analysissink2 = { &analysissinkvtbl2 };
347 static HRESULT WINAPI analysissource_QueryInterface(IDWriteTextAnalysisSource *iface,
348 REFIID riid, void **obj)
350 ok(0, "QueryInterface not expected\n");
351 return E_NOTIMPL;
354 static ULONG WINAPI analysissource_AddRef(IDWriteTextAnalysisSource *iface)
356 ok(0, "AddRef not expected\n");
357 return 2;
360 static ULONG WINAPI analysissource_Release(IDWriteTextAnalysisSource *iface)
362 ok(0, "Release not expected\n");
363 return 1;
366 struct testanalysissource
368 IDWriteTextAnalysisSource IDWriteTextAnalysisSource_iface;
369 const WCHAR *text;
370 UINT32 text_length;
371 DWRITE_READING_DIRECTION direction;
374 static void init_textsource(struct testanalysissource *source, const WCHAR *text,
375 DWRITE_READING_DIRECTION direction)
377 source->text = text;
378 source->text_length = lstrlenW(text);
379 source->direction = direction;
382 static inline struct testanalysissource *impl_from_IDWriteTextAnalysisSource(IDWriteTextAnalysisSource *iface)
384 return CONTAINING_RECORD(iface, struct testanalysissource, IDWriteTextAnalysisSource_iface);
387 static HRESULT WINAPI analysissource_GetTextAtPosition(IDWriteTextAnalysisSource *iface,
388 UINT32 position, WCHAR const** text, UINT32* text_len)
390 struct testanalysissource *source = impl_from_IDWriteTextAnalysisSource(iface);
392 if (position >= source->text_length)
394 *text = NULL;
395 *text_len = 0;
397 else
399 *text = source->text + position;
400 *text_len = source->text_length - position;
403 return S_OK;
406 static HRESULT WINAPI analysissource_GetTextBeforePosition(IDWriteTextAnalysisSource *iface,
407 UINT32 position, WCHAR const** text, UINT32* text_len)
409 ok(0, "unexpected\n");
410 return E_NOTIMPL;
413 static DWRITE_READING_DIRECTION WINAPI analysissource_GetParagraphReadingDirection(
414 IDWriteTextAnalysisSource *iface)
416 struct testanalysissource *source = impl_from_IDWriteTextAnalysisSource(iface);
417 return source->direction;
420 static HRESULT WINAPI analysissource_GetLocaleName(IDWriteTextAnalysisSource *iface,
421 UINT32 position, UINT32* text_len, WCHAR const** locale)
423 *locale = NULL;
424 *text_len = 0;
425 return S_OK;
428 static HRESULT WINAPI analysissource_GetNumberSubstitution(IDWriteTextAnalysisSource *iface,
429 UINT32 position, UINT32* text_len, IDWriteNumberSubstitution **substitution)
431 ok(0, "unexpected\n");
432 return E_NOTIMPL;
435 static IDWriteTextAnalysisSourceVtbl analysissourcevtbl = {
436 analysissource_QueryInterface,
437 analysissource_AddRef,
438 analysissource_Release,
439 analysissource_GetTextAtPosition,
440 analysissource_GetTextBeforePosition,
441 analysissource_GetParagraphReadingDirection,
442 analysissource_GetLocaleName,
443 analysissource_GetNumberSubstitution
446 static struct testanalysissource analysissource = { { &analysissourcevtbl } };
448 static IDWriteFontFace *create_fontface(void)
450 static const WCHAR tahomaW[] = {'T','a','h','o','m','a',0};
451 IDWriteGdiInterop *interop;
452 IDWriteFontFace *fontface;
453 IDWriteFont *font;
454 LOGFONTW logfont;
455 HRESULT hr;
457 hr = IDWriteFactory_GetGdiInterop(factory, &interop);
458 ok(hr == S_OK, "got 0x%08x\n", hr);
460 memset(&logfont, 0, sizeof(logfont));
461 logfont.lfHeight = 12;
462 logfont.lfWidth = 12;
463 logfont.lfWeight = FW_NORMAL;
464 logfont.lfItalic = 1;
465 lstrcpyW(logfont.lfFaceName, tahomaW);
467 hr = IDWriteGdiInterop_CreateFontFromLOGFONT(interop, &logfont, &font);
468 ok(hr == S_OK, "got 0x%08x\n", hr);
470 hr = IDWriteFont_CreateFontFace(font, &fontface);
471 ok(hr == S_OK, "got 0x%08x\n", hr);
473 IDWriteFont_Release(font);
474 IDWriteGdiInterop_Release(interop);
476 return fontface;
479 static WCHAR *create_testfontfile(const WCHAR *filename)
481 static WCHAR pathW[MAX_PATH];
482 DWORD written;
483 HANDLE file;
484 HRSRC res;
485 void *ptr;
487 GetTempPathW(sizeof(pathW)/sizeof(WCHAR), pathW);
488 lstrcatW(pathW, filename);
490 file = CreateFileW(pathW, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0);
491 ok(file != INVALID_HANDLE_VALUE, "file creation failed, at %s, error %d\n", wine_dbgstr_w(pathW),
492 GetLastError());
494 res = FindResourceA(GetModuleHandleA(NULL), (LPCSTR)MAKEINTRESOURCE(1), (LPCSTR)RT_RCDATA);
495 ok(res != 0, "couldn't find resource\n");
496 ptr = LockResource(LoadResource(GetModuleHandleA(NULL), res));
497 WriteFile(file, ptr, SizeofResource(GetModuleHandleA(NULL), res), &written, NULL);
498 ok(written == SizeofResource(GetModuleHandleA(NULL), res), "couldn't write resource\n");
499 CloseHandle(file);
501 return pathW;
504 #define DELETE_FONTFILE(filename) _delete_testfontfile(filename, __LINE__)
505 static void _delete_testfontfile(const WCHAR *filename, int line)
507 BOOL ret = DeleteFileW(filename);
508 ok_(__FILE__,line)(ret, "failed to delete file %s, error %d\n", wine_dbgstr_w(filename), GetLastError());
511 static IDWriteFontFace *create_testfontface(const WCHAR *filename)
513 IDWriteFontFace *face;
514 IDWriteFontFile *file;
515 HRESULT hr;
517 hr = IDWriteFactory_CreateFontFileReference(factory, filename, NULL, &file);
518 ok(hr == S_OK, "got 0x%08x\n",hr);
520 hr = IDWriteFactory_CreateFontFace(factory, DWRITE_FONT_FACE_TYPE_TRUETYPE, 1, &file, 0,
521 DWRITE_FONT_SIMULATIONS_NONE, &face);
522 ok(hr == S_OK, "got 0x%08x\n", hr);
523 IDWriteFontFile_Release(file);
525 return face;
528 struct sa_test {
529 const WCHAR string[50];
530 int item_count;
531 struct script_analysis sa[10];
534 static struct sa_test sa_tests[] = {
536 /* just 1 char string */
537 {'t',0}, 1,
538 { { 0, 1, DWRITE_SCRIPT_SHAPES_DEFAULT }}
541 {'t','e','s','t',0}, 1,
542 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT }}
545 {' ',' ',' ',' ','!','$','[','^','{','~',0}, 1,
546 { { 0, 10, DWRITE_SCRIPT_SHAPES_DEFAULT }}
549 {' ',' ',' ','1','2',' ',0}, 1,
550 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
553 /* digits only */
554 {'1','2',0}, 1,
555 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }}
558 /* Arabic */
559 {0x064a,0x064f,0x0633,0x0627,0x0648,0x0650,0x064a,0x0661,0}, 1,
560 { { 0, 8, DWRITE_SCRIPT_SHAPES_DEFAULT }}
563 /* Arabic */
564 {0x0627,0x0644,0x0635,0x0651,0x0650,0x062d,0x0629,0x064f,' ',0x062a,0x064e,
565 0x0627,0x062c,0x064c,' ',0x0639,0x064e,0x0644,0x0649,' ',
566 0x0631,0x064f,0x0624,0x0648,0x0633,0x0650,' ',0x0627,0x0644,
567 0x0623,0x0635,0x0650,0x062d,0x0651,0x064e,0x0627,0x0621,0x0650,0x06f0,0x06f5,0}, 1,
568 { { 0, 40, DWRITE_SCRIPT_SHAPES_DEFAULT }}
571 /* Arabic, Latin */
572 {'1','2','3','-','5','2',0x064a,0x064f,0x0633,0x0627,0x0648,0x0650,0x064a,'7','1','.',0}, 1,
573 { { 0, 16, DWRITE_SCRIPT_SHAPES_DEFAULT }}
576 /* Arabic, English */
577 {'A','B','C','-','D','E','F',' ',0x0621,0x0623,0x0624,0}, 2,
578 { { 0, 8, DWRITE_SCRIPT_SHAPES_DEFAULT },
579 { 8, 3, DWRITE_SCRIPT_SHAPES_DEFAULT },
583 /* leading space, Arabic, English */
584 {' ',0x0621,0x0623,0x0624,'A','B','C','-','D','E','F',0}, 2,
585 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT },
586 { 4, 7, DWRITE_SCRIPT_SHAPES_DEFAULT },
590 /* English, Arabic, trailing space */
591 {'A','B','C','-','D','E','F',0x0621,0x0623,0x0624,' ',0}, 2,
592 { { 0, 7, DWRITE_SCRIPT_SHAPES_DEFAULT },
593 { 7, 4, DWRITE_SCRIPT_SHAPES_DEFAULT },
597 /* C1 Controls, Latin-1 Supplement */
598 {0x80,0x90,0x9f,0xa0,0xc0,0xb8,0xbf,0xc0,0xff,0}, 2,
599 { { 0, 3, DWRITE_SCRIPT_SHAPES_NO_VISUAL },
600 { 3, 6, DWRITE_SCRIPT_SHAPES_DEFAULT },
604 /* Latin Extended-A */
605 {0x100,0x120,0x130,0x140,0x150,0x160,0x170,0x17f,0}, 1,
606 { { 0, 8, DWRITE_SCRIPT_SHAPES_DEFAULT }}
609 /* Latin Extended-B */
610 {0x180,0x190,0x1bf,0x1c0,0x1c3,0x1c4,0x1cc,0x1dc,0x1ff,0x217,0x21b,0x24f,0}, 1,
611 { { 0, 12, DWRITE_SCRIPT_SHAPES_DEFAULT }}
614 /* IPA Extensions */
615 {0x250,0x260,0x270,0x290,0x2af,0}, 1,
616 { { 0, 5, DWRITE_SCRIPT_SHAPES_DEFAULT }}
619 /* Spacing Modifier Letters */
620 {0x2b0,0x2ba,0x2d7,0x2dd,0x2ef,0x2ff,0}, 1,
621 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
624 /* Combining Diacritical Marks */
625 {0x300,0x320,0x340,0x345,0x350,0x36f,0}, 1,
626 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
629 /* Greek and Coptic */
630 {0x370,0x388,0x3d8,0x3e1,0x3e2,0x3fa,0x3ff,0}, 3,
631 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT },
632 { 4, 1, DWRITE_SCRIPT_SHAPES_DEFAULT },
633 { 5, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }
637 /* Cyrillic and Cyrillic Supplement */
638 {0x400,0x40f,0x410,0x44f,0x450,0x45f,0x460,0x481,0x48a,0x4f0,0x4fa,0x4ff,0x500,0x510,0x520,0}, 1,
639 { { 0, 15, DWRITE_SCRIPT_SHAPES_DEFAULT }}
642 /* Armenian */
643 {0x531,0x540,0x559,0x55f,0x570,0x589,0x58a,0}, 1,
644 { { 0, 7, DWRITE_SCRIPT_SHAPES_DEFAULT }}
647 /* Hebrew */
648 {0x5e9,0x5dc,0x5d5,0x5dd,0}, 1,
649 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT }}
652 /* Latin, Hebrew, Latin */
653 {'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,
654 { { 0, 9, DWRITE_SCRIPT_SHAPES_DEFAULT },
655 { 9, 10, DWRITE_SCRIPT_SHAPES_DEFAULT },
656 { 19, 10, DWRITE_SCRIPT_SHAPES_DEFAULT }}
659 /* Syriac */
660 {0x710,0x712,0x712,0x714,'.',0}, 1,
661 { { 0, 5, DWRITE_SCRIPT_SHAPES_DEFAULT }}
664 /* Arabic Supplement */
665 {0x750,0x760,0x76d,'.',0}, 1,
666 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT }}
669 /* Thaana */
670 {0x780,0x78e,0x798,0x7a6,0x7b0,'.',0}, 1,
671 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
674 /* N'Ko */
675 {0x7c0,0x7ca,0x7e8,0x7eb,0x7f6,'.',0}, 1,
676 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
679 /* Thaana */
680 {0x780,0x798,0x7a5,0x7a6,0x7b0,'.',0}, 1,
681 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
684 /* Devanagari */
685 {0x926,0x947,0x935,0x928,0x93e,0x917,0x930,0x940,'.',0}, 1,
686 { { 0, 9, DWRITE_SCRIPT_SHAPES_DEFAULT }}
689 /* Bengali */
690 {0x9ac,0x9be,0x982,0x9b2,0x9be,'.',0}, 1,
691 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
694 /* Gurmukhi */
695 {0xa17,0xa41,0xa30,0xa2e,0xa41,0xa16,0xa40,'.',0}, 1,
696 { { 0, 8, DWRITE_SCRIPT_SHAPES_DEFAULT }}
699 /* Gujarati */
700 {0xa97,0xac1,0xa9c,0xab0,0xabe,0xaa4,0xac0,'.',0}, 1,
701 { { 0, 8, DWRITE_SCRIPT_SHAPES_DEFAULT }}
704 /* Oriya */
705 {0xb13,0xb21,0xb3c,0xb3f,0xb06,'.',0}, 1,
706 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
709 /* Tamil */
710 {0xba4,0xbae,0xbbf,0xbb4,0xbcd,'.',0}, 1,
711 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
714 /* Telugu */
715 {0xc24,0xc46,0xc32,0xc41,0xc17,0xc41,'.',0}, 1,
716 { { 0, 7, DWRITE_SCRIPT_SHAPES_DEFAULT }}
719 /* Kannada */
720 {0xc95,0xca8,0xccd,0xca8,0xca1,'.',0}, 1,
721 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
724 /* Malayalam */
725 {0xd2e,0xd32,0xd2f,0xd3e,0xd33,0xd02,'.',0}, 1,
726 { { 0, 7, DWRITE_SCRIPT_SHAPES_DEFAULT }}
729 /* Sinhala */
730 {0xd82,0xd85,0xd9a,0xdcf,'.',0}, 1,
731 { { 0, 5, DWRITE_SCRIPT_SHAPES_DEFAULT }}
734 /* Thai */
735 {0x0e04,0x0e27,0x0e32,0x0e21,0x0e1e,0x0e22,0x0e32,0x0e22,0x0e32,0x0e21,
736 0x0e2d,0x0e22,0x0e39,0x0e48,0x0e17,0x0e35,0x0e48,0x0e44,0x0e2b,0x0e19,
737 0x0e04,0x0e27,0x0e32,0x0e21,0x0e2a, 0x0e33,0x0e40,0x0e23,0x0e47,0x0e08,
738 0x0e2d,0x0e22,0x0e39,0x0e48,0x0e17,0x0e35,0x0e48,0x0e19,0x0e31,0x0e48,0x0e19,'.',0}, 1,
739 { { 0, 42, DWRITE_SCRIPT_SHAPES_DEFAULT }}
742 /* Lao */
743 {0xead,0xeb1,0xe81,0xeaa,0xead,0xe99,0xea5,0xeb2,0xea7,'.',0}, 1,
744 { { 0, 10, DWRITE_SCRIPT_SHAPES_DEFAULT }}
747 /* Tibetan */
748 {0xf04,0xf05,0xf0e,0x020,0xf51,0xf7c,0xf53,0xf0b,0xf5a,0xf53,0xf0b,
749 0xf51,0xf44,0xf0b,0xf54,0xf7c,0xf0d,'.',0}, 1,
750 { { 0, 18, DWRITE_SCRIPT_SHAPES_DEFAULT }}
753 /* Myanmar */
754 {0x1019,0x103c,0x1014,0x103a,0x1019,0x102c,0x1021,0x1000,0x1039,0x1001,0x101b,0x102c,'.',0}, 1,
755 { { 0, 13, DWRITE_SCRIPT_SHAPES_DEFAULT }}
758 /* Georgian */
759 {0x10a0,0x10d0,0x10da,0x10f1,0x10fb,0x2d00,'.',0}, 1,
760 { { 0, 7, DWRITE_SCRIPT_SHAPES_DEFAULT }}
763 /* Hangul */
764 {0x1100,0x1110,0x1160,0x1170,0x11a8,'.',0}, 1,
765 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
768 /* Ethiopic */
769 {0x130d,0x12d5,0x12dd,0}, 1,
770 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
773 /* Cherokee */
774 {0x13e3,0x13b3,0x13a9,0x0020,0x13a6,0x13ec,0x13c2,0x13af,0x13cd,0x13d7,0}, 1,
775 { { 0, 10, DWRITE_SCRIPT_SHAPES_DEFAULT }}
778 /* Canadian */
779 {0x1403,0x14c4,0x1483,0x144e,0x1450,0x1466,0}, 1,
780 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
783 /* Ogham */
784 {0x169b,0x1691,0x168c,0x1690,0x168b,0x169c,0}, 1,
785 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
788 /* Runic */
789 {0x16a0,0x16a1,0x16a2,0x16a3,0x16a4,0x16a5,0}, 1,
790 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
793 /* Khmer */
794 {0x1781,0x17c1,0x1798,0x179a,0x1797,0x17b6,0x179f,0x17b6,0x19e0,0}, 1,
795 { { 0, 9, DWRITE_SCRIPT_SHAPES_DEFAULT }}
798 /* Mongolian */
799 {0x182e,0x1823,0x1829,0x182d,0x1823,0x182f,0x0020,0x182a,0x1822,0x1834,0x1822,0x182d,0x180c,0}, 1,
800 { { 0, 13, DWRITE_SCRIPT_SHAPES_DEFAULT }}
803 /* Limbu */
804 {0x1900,0x1910,0x1920,0x1930,0}, 1,
805 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT }}
808 /* Tai Le */
809 {0x1956,0x196d,0x1970,0x1956,0x196c,0x1973,0x1951,0x1968,0x1952,0x1970,0}, 1,
810 { { 0, 10, DWRITE_SCRIPT_SHAPES_DEFAULT }}
813 /* New Tai Lue */
814 {0x1992,0x19c4,0}, 1,
815 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }}
818 /* Buginese */
819 {0x1a00,0x1a10,0}, 1,
820 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }}
823 /* Tai Tham */
824 {0x1a20,0x1a40,0x1a50,0}, 1,
825 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
828 /* Balinese */
829 {0x1b00,0x1b05,0x1b20,0}, 1,
830 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
833 /* Sundanese */
834 {0x1b80,0x1b85,0x1ba0,0}, 1,
835 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
838 /* Batak */
839 {0x1bc0,0x1be5,0x1bfc,0}, 1,
840 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
843 /* Lepcha */
844 {0x1c00,0x1c20,0x1c40,0}, 1,
845 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
848 /* Ol Chiki */
849 {0x1c50,0x1c5a,0x1c77,0}, 1,
850 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
853 /* Sundanese Supplement */
854 {0x1cc0,0x1cc5,0x1cc7,0}, 1,
855 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
858 /* Phonetic Extensions */
859 {0x1d00,0x1d40,0x1d70,0}, 1,
860 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
863 /* Combining diacritical marks */
864 {0x1dc0,0x300,0x1ddf,0}, 1,
865 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
868 /* Latin Extended Additional, Extended-C */
869 {0x1e00,0x1d00,0x2c60,0}, 1,
870 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
873 /* Greek Extended */
874 {0x3f0,0x1f00,0}, 1,
875 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }}
878 /* General Punctuation */
879 {0x1dc0,0x2000,0}, 1,
880 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }}
883 /* Superscripts and Subscripts */
884 {0x2070,0x2086,0x2000,0}, 1,
885 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
888 /* Currency, Combining Diacritical Marks for Symbols. Letterlike Symbols.. */
889 {0x20a0,0x20b8,0x2000,0x20d0,0x2100,0x2150,0x2190,0x2200,0x2300,0x2400,0x2440,0x2460,0x2500,0x2580,0x25a0,0x2600,
890 0x2700,0x27c0,0x27f0,0x2900,0x2980,0x2a00,0x2b00,0}, 1,
891 { { 0, 23, DWRITE_SCRIPT_SHAPES_DEFAULT }}
894 /* Braille */
895 {0x2800,0x2070,0x2000,0}, 1,
896 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
899 /* Glagolitic */
900 {0x2c00,0x2c12,0}, 1,
901 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }}
904 /* Coptic */
905 {0x2c80,0x3e2,0x1f00,0}, 2,
906 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT },
907 { 2, 1, DWRITE_SCRIPT_SHAPES_DEFAULT } }
910 /* Tifinagh */
911 {0x2d30,0x2d4a,0}, 1,
912 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }}
915 /* LRE/PDF */
916 {LRE,PDF,'a','b','c','\r',0}, 3,
917 { { 0, 2, DWRITE_SCRIPT_SHAPES_NO_VISUAL },
918 { 2, 3, DWRITE_SCRIPT_SHAPES_DEFAULT },
919 { 5, 1, DWRITE_SCRIPT_SHAPES_NO_VISUAL } }
922 /* LRE/PDF and other visual and non-visual codes from Common script range */
923 {LRE,PDF,'r','!',0x200b,'\r',0}, 3,
924 { { 0, 2, DWRITE_SCRIPT_SHAPES_NO_VISUAL },
925 { 2, 2, DWRITE_SCRIPT_SHAPES_DEFAULT },
926 { 4, 2, DWRITE_SCRIPT_SHAPES_NO_VISUAL } }
929 /* Inherited on its own */
930 {0x300,0x300,0}, 1,
931 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT } }
934 /* Inherited followed by Latin */
935 {0x300,0x300,'a',0}, 1,
936 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT } }
939 /* Inherited mixed with Arabic and Latin */
940 {0x300,'+',0x627,0x300,'a',0}, 2,
941 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT },
942 { 4, 1, DWRITE_SCRIPT_SHAPES_DEFAULT } }
945 {'a',0x300,'+',0x627,0x300,')','a',0}, 3,
946 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT },
947 { 3, 3, DWRITE_SCRIPT_SHAPES_DEFAULT },
948 { 6, 1, DWRITE_SCRIPT_SHAPES_DEFAULT } }
950 /* Paired punctuation */
952 {0x627,'(','a',')','a',0}, 2,
953 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT },
954 { 2, 3, DWRITE_SCRIPT_SHAPES_DEFAULT } }
957 {0x627,'[','a',']',0x627,0}, 3,
958 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT },
959 { 2, 2, DWRITE_SCRIPT_SHAPES_DEFAULT },
960 { 4, 1, DWRITE_SCRIPT_SHAPES_DEFAULT } }
962 /* Combining marks */
964 /* dotted circle - Common, followed by accent - Inherited */
965 {0x25cc,0x300,0}, 1,
966 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT } }
969 /* combining mark with explicit script value */
970 {0x25cc,0x300,0x5c4,0}, 1,
971 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT } }
974 /* inherited merges with following explicit script */
975 {0x25cc,0x300,'a',0}, 1,
976 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT } }
978 /* keep this as end test data marker */
979 { {0} }
982 static void init_expected_sa(struct call_sequence **seq, const struct sa_test *test)
984 static const struct call_entry end_of_sequence = { LastKind };
985 int i;
987 flush_sequence(seq, ANALYZER_ID);
989 /* add expected calls */
990 for (i = 0; i < test->item_count; i++)
992 struct call_entry call;
994 call.kind = ScriptAnalysis;
995 call.sa.pos = test->sa[i].pos;
996 call.sa.len = test->sa[i].len;
997 call.sa.shapes = test->sa[i].shapes;
998 add_call(seq, 0, &call);
1001 /* and stop marker */
1002 add_call(seq, 0, &end_of_sequence);
1005 static void get_script_analysis(const WCHAR *str, DWRITE_SCRIPT_ANALYSIS *sa)
1007 IDWriteTextAnalyzer *analyzer;
1008 HRESULT hr;
1010 init_textsource(&analysissource, str, DWRITE_READING_DIRECTION_LEFT_TO_RIGHT);
1011 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
1012 ok(hr == S_OK, "got 0x%08x\n", hr);
1014 hr = IDWriteTextAnalyzer_AnalyzeScript(analyzer, &analysissource.IDWriteTextAnalysisSource_iface, 0,
1015 lstrlenW(analysissource.text), &analysissink2);
1016 ok(hr == S_OK, "got 0x%08x\n", hr);
1018 *sa = g_sa;
1021 static void test_AnalyzeScript(void)
1023 const struct sa_test *ptr = sa_tests;
1024 IDWriteTextAnalyzer *analyzer;
1025 HRESULT hr;
1027 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
1028 ok(hr == S_OK, "got 0x%08x\n", hr);
1030 while (*ptr->string)
1032 init_textsource(&analysissource, ptr->string, DWRITE_READING_DIRECTION_LEFT_TO_RIGHT);
1034 init_expected_sa(expected_seq, ptr);
1035 hr = IDWriteTextAnalyzer_AnalyzeScript(analyzer, &analysissource.IDWriteTextAnalysisSource_iface, 0,
1036 lstrlenW(ptr->string), &analysissink);
1037 ok(hr == S_OK, "got 0x%08x\n", hr);
1038 ok_sequence(sequences, ANALYZER_ID, expected_seq[0]->sequence, wine_dbgstr_w(ptr->string), FALSE);
1039 ptr++;
1042 IDWriteTextAnalyzer_Release(analyzer);
1045 struct linebreaks_test {
1046 const WCHAR text[BREAKPOINT_COUNT+1];
1047 DWRITE_LINE_BREAKPOINT bp[BREAKPOINT_COUNT];
1050 static struct linebreaks_test linebreaks_tests[] = {
1051 { {'A','-','B',' ','C',0x58a,'D',0x2010,'E',0x2012,'F',0x2013,'\t',0xc,0xb,0x2028,0x2029,0x200b,0},
1053 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1054 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 0, 0 },
1055 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1056 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 1, 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, 0, 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_MAY_NOT_BREAK, 0, 0 },
1065 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 1, 0 },
1066 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_MUST_BREAK, 1, 0 },
1067 { DWRITE_BREAK_CONDITION_MUST_BREAK, DWRITE_BREAK_CONDITION_MUST_BREAK, 1, 0 },
1068 { DWRITE_BREAK_CONDITION_MUST_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_CAN_BREAK, 0, 0 },
1073 /* Soft hyphen, visible word dividers */
1074 { {'A',0xad,'B',0x5be,'C',0xf0b,'D',0x1361,'E',0x17d8,'F',0x17da,'G',0},
1076 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1077 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 0, 1 },
1078 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1079 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 0, 0 },
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_CAN_BREAK, 0, 0 },
1091 { { 0 } }
1094 static void compare_breakpoints(const struct linebreaks_test *test, DWRITE_LINE_BREAKPOINT *actual)
1096 static const char *conditions[] = {"N","CB","NB","B"};
1097 const WCHAR *text = test->text;
1098 int cmp = memcmp(test->bp, actual, sizeof(*actual)*BREAKPOINT_COUNT);
1099 ok(!cmp, "%s: got wrong breakpoint data\n", wine_dbgstr_w(test->text));
1100 if (cmp) {
1101 int i = 0;
1102 while (*text) {
1103 ok(!memcmp(&test->bp[i], &actual[i], sizeof(*actual)),
1104 "%s: got [%s, %s] (%s, %s), expected [%s, %s] (%s, %s)\n",
1105 wine_dbgstr_wn(&test->text[i], 1),
1106 conditions[g_actual_bp[i].breakConditionBefore],
1107 conditions[g_actual_bp[i].breakConditionAfter],
1108 g_actual_bp[i].isWhitespace ? "WS" : "0",
1109 g_actual_bp[i].isSoftHyphen ? "SHY" : "0",
1110 conditions[test->bp[i].breakConditionBefore],
1111 conditions[test->bp[i].breakConditionAfter],
1112 test->bp[i].isWhitespace ? "WS" : "0",
1113 test->bp[i].isSoftHyphen ? "SHY" : "0");
1114 if (g_actual_bp[i].isSoftHyphen)
1115 ok(!g_actual_bp[i].isWhitespace, "%s: soft hyphen marked as a whitespace\n",
1116 wine_dbgstr_wn(&test->text[i], 1));
1117 text++;
1118 i++;
1123 static void test_AnalyzeLineBreakpoints(void)
1125 static const WCHAR emptyW[] = {0};
1126 const struct linebreaks_test *ptr = linebreaks_tests;
1127 IDWriteTextAnalyzer *analyzer;
1128 UINT32 i = 0;
1129 HRESULT hr;
1131 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
1132 ok(hr == S_OK, "got 0x%08x\n", hr);
1134 init_textsource(&analysissource, emptyW, DWRITE_READING_DIRECTION_LEFT_TO_RIGHT);
1135 hr = IDWriteTextAnalyzer_AnalyzeLineBreakpoints(analyzer, &analysissource.IDWriteTextAnalysisSource_iface, 0, 0,
1136 &analysissink);
1137 ok(hr == S_OK, "got 0x%08x\n", hr);
1139 while (*ptr->text)
1141 UINT32 len;
1143 init_textsource(&analysissource, ptr->text, DWRITE_READING_DIRECTION_LEFT_TO_RIGHT);
1145 len = lstrlenW(ptr->text);
1146 if (len > BREAKPOINT_COUNT) {
1147 ok(0, "test %u: increase BREAKPOINT_COUNT to at least %u\n", i, len);
1148 i++;
1149 ptr++;
1150 continue;
1153 memset(g_actual_bp, 0, sizeof(g_actual_bp));
1154 hr = IDWriteTextAnalyzer_AnalyzeLineBreakpoints(analyzer, &analysissource.IDWriteTextAnalysisSource_iface,
1155 0, len, &analysissink);
1156 ok(hr == S_OK, "got 0x%08x\n", hr);
1157 compare_breakpoints(ptr, g_actual_bp);
1159 i++;
1160 ptr++;
1163 IDWriteTextAnalyzer_Release(analyzer);
1166 static void test_GetScriptProperties(void)
1168 IDWriteTextAnalyzer1 *analyzer1;
1169 IDWriteTextAnalyzer *analyzer;
1170 DWRITE_SCRIPT_ANALYSIS sa;
1171 DWRITE_SCRIPT_PROPERTIES props;
1172 HRESULT hr;
1174 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
1175 ok(hr == S_OK, "got 0x%08x\n", hr);
1177 hr = IDWriteTextAnalyzer_QueryInterface(analyzer, &IID_IDWriteTextAnalyzer1, (void**)&analyzer1);
1178 IDWriteTextAnalyzer_Release(analyzer);
1179 if (hr != S_OK) {
1180 win_skip("GetScriptProperties() is not supported.\n");
1181 return;
1184 sa.script = 1000;
1185 hr = IDWriteTextAnalyzer1_GetScriptProperties(analyzer1, sa, &props);
1186 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1188 if (0) /* crashes on native */
1189 hr = IDWriteTextAnalyzer1_GetScriptProperties(analyzer1, sa, NULL);
1191 sa.script = 0;
1192 hr = IDWriteTextAnalyzer1_GetScriptProperties(analyzer1, sa, &props);
1193 ok(hr == S_OK, "got 0x%08x\n", hr);
1195 IDWriteTextAnalyzer1_Release(analyzer1);
1198 struct textcomplexity_test {
1199 const WCHAR text[5];
1200 UINT32 length;
1201 BOOL simple;
1202 UINT32 len_read;
1205 static const struct textcomplexity_test textcomplexity_tests[] = {
1206 { {0}, 1, FALSE, 1 },
1207 { {0}, 0, TRUE, 0 },
1208 { {0x610,0}, 0, TRUE, 0 },
1209 { {'A','B','C','D',0}, 3, TRUE, 3 },
1210 { {'A','B','C','D',0}, 5, TRUE, 4 },
1211 { {'A','B','C','D',0}, 10, TRUE, 4 },
1212 { {'A',0x610,'C','D',0}, 1, TRUE, 1 },
1213 { {'A',0x610,'C','D',0}, 2, FALSE, 2 },
1214 { {0x610,'A','C','D',0}, 1, FALSE, 1 },
1215 { {0x610,'A','C','D',0}, 2, FALSE, 1 },
1216 { {0x610,0x610,'C','D',0}, 2, FALSE, 2 },
1217 { {0xd800,'A','B',0}, 1, FALSE, 1 },
1218 { {0xd800,'A','B',0}, 2, FALSE, 1 },
1219 { {0xdc00,'A','B',0}, 2, FALSE, 1 },
1220 { {0x202a,'A',0x202c,0}, 3, FALSE, 1 },
1221 { {0x200e,'A',0}, 2, FALSE, 1 },
1222 { {0x200f,'A',0}, 2, FALSE, 1 },
1223 { {0x202d,'A',0}, 2, FALSE, 1 },
1224 { {0x202e,'A',0}, 2, FALSE, 1 },
1228 static void test_GetTextComplexity(void)
1230 static const WCHAR textW[] = {'A','B','C',0};
1231 IDWriteTextAnalyzer1 *analyzer1;
1232 IDWriteTextAnalyzer *analyzer;
1233 IDWriteFontFace *fontface;
1234 UINT16 indices[10];
1235 BOOL simple;
1236 HRESULT hr;
1237 UINT32 len;
1238 int i;
1240 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
1241 ok(hr == S_OK, "got 0x%08x\n", hr);
1243 hr = IDWriteTextAnalyzer_QueryInterface(analyzer, &IID_IDWriteTextAnalyzer1, (void**)&analyzer1);
1244 IDWriteTextAnalyzer_Release(analyzer);
1245 if (hr != S_OK) {
1246 win_skip("GetTextComplexity() is not supported.\n");
1247 return;
1250 if (0) { /* crashes on native */
1251 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer1, NULL, 0, NULL, NULL, NULL, NULL);
1252 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer1, NULL, 0, NULL, NULL, &len, NULL);
1253 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer1, textW, 3, NULL, NULL, NULL, NULL);
1254 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer1, textW, 3, NULL, NULL, &len, NULL);
1255 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer1, textW, 3, NULL, &simple, NULL, NULL);
1258 len = 1;
1259 simple = TRUE;
1260 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer1, NULL, 0, NULL, &simple, &len, NULL);
1261 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1262 ok(len == 0, "got %d\n", len);
1263 ok(simple == FALSE, "got %d\n", simple);
1265 len = 1;
1266 simple = TRUE;
1267 indices[0] = 1;
1268 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer1, textW, 3, NULL, &simple, &len, NULL);
1269 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1270 ok(len == 0, "got %d\n", len);
1271 ok(simple == FALSE, "got %d\n", simple);
1272 ok(indices[0] == 1, "got %d\n", indices[0]);
1274 fontface = create_fontface();
1276 for (i = 0; i < sizeof(textcomplexity_tests)/sizeof(struct textcomplexity_test); i++) {
1277 const struct textcomplexity_test *ptr = &textcomplexity_tests[i];
1278 len = 1;
1279 simple = !ptr->simple;
1280 indices[0] = 0;
1281 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer1, ptr->text, ptr->length, fontface, &simple, &len, indices);
1282 ok(hr == S_OK, "%d: got 0x%08x\n", i, hr);
1283 ok(len == ptr->len_read, "%d: read length: got %d, expected %d\n", i, len, ptr->len_read);
1284 ok(simple == ptr->simple, "%d: simple: got %d, expected %d\n", i, simple, ptr->simple);
1285 if (simple && ptr->length)
1286 ok(indices[0] > 0, "%d: got %d\n", i, indices[0]);
1287 else
1288 ok(indices[0] == 0, "%d: got %d\n", i, indices[0]);
1291 IDWriteFontFace_Release(fontface);
1292 IDWriteTextAnalyzer1_Release(analyzer1);
1295 static void test_numbersubstitution(void)
1297 static const WCHAR dummyW[] = {'d','u','m','m','y',0};
1298 IDWriteNumberSubstitution *substitution;
1299 HRESULT hr;
1301 /* locale is not specified, method does not require it */
1302 hr = IDWriteFactory_CreateNumberSubstitution(factory, DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE, NULL, FALSE, &substitution);
1303 ok(hr == S_OK, "got 0x%08x\n", hr);
1304 IDWriteNumberSubstitution_Release(substitution);
1306 /* invalid locale name, method does not require it */
1307 hr = IDWriteFactory_CreateNumberSubstitution(factory, DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE, dummyW, FALSE, &substitution);
1308 ok(hr == S_OK, "got 0x%08x\n", hr);
1309 IDWriteNumberSubstitution_Release(substitution);
1311 /* invalid method */
1312 hr = IDWriteFactory_CreateNumberSubstitution(factory, DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL+1, NULL, FALSE, &substitution);
1313 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1315 /* invalid method */
1316 hr = IDWriteFactory_CreateNumberSubstitution(factory, -1, NULL, FALSE, &substitution);
1317 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1319 /* invalid locale */
1320 hr = IDWriteFactory_CreateNumberSubstitution(factory, DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL, NULL, FALSE, &substitution);
1321 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1323 hr = IDWriteFactory_CreateNumberSubstitution(factory, DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL, dummyW, FALSE, &substitution);
1324 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1326 hr = IDWriteFactory_CreateNumberSubstitution(factory, DWRITE_NUMBER_SUBSTITUTION_METHOD_CONTEXTUAL, dummyW, FALSE, &substitution);
1327 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1329 hr = IDWriteFactory_CreateNumberSubstitution(factory, DWRITE_NUMBER_SUBSTITUTION_METHOD_NATIONAL, dummyW, FALSE, &substitution);
1330 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1332 /* invalid locale, but it's not needed for this method */
1333 hr = IDWriteFactory_CreateNumberSubstitution(factory, DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE, dummyW, FALSE, &substitution);
1334 ok(hr == S_OK, "got 0x%08x\n", hr);
1335 IDWriteNumberSubstitution_Release(substitution);
1338 static void get_fontface_glyphs(IDWriteFontFace *fontface, const WCHAR *str, UINT16 *glyphs)
1340 while (*str) {
1341 UINT32 codepoint = *str;
1342 HRESULT hr;
1344 hr = IDWriteFontFace_GetGlyphIndices(fontface, &codepoint, 1, glyphs++);
1345 ok(hr == S_OK, "got 0x%08x\n", hr);
1346 str++;
1350 static void get_fontface_advances(IDWriteFontFace *fontface, FLOAT emsize, const UINT16 *glyphs, FLOAT *advances, UINT32 count)
1352 DWRITE_FONT_METRICS fontmetrics;
1353 UINT32 i;
1355 IDWriteFontFace_GetMetrics(fontface, &fontmetrics);
1356 for (i = 0; i < count; i++) {
1357 DWRITE_GLYPH_METRICS metrics;
1358 HRESULT hr;
1360 hr = IDWriteFontFace_GetDesignGlyphMetrics(fontface, glyphs + i, 1, &metrics, FALSE);
1361 ok(hr == S_OK, "got 0x%08x\n", hr);
1363 advances[i] = (FLOAT)metrics.advanceWidth * emsize / (FLOAT)fontmetrics.designUnitsPerEm;
1367 static void test_GetGlyphs(void)
1369 static const WCHAR test1W[] = {'<','B',' ','C',0};
1370 static const WCHAR test2W[] = {'<','B','\t','C',0};
1371 static const WCHAR test3W[] = {0x202a,0x202c,0};
1372 DWRITE_SHAPING_GLYPH_PROPERTIES shapingprops[20];
1373 DWRITE_SHAPING_TEXT_PROPERTIES props[20];
1374 UINT32 maxglyphcount, actual_count;
1375 FLOAT advances[10], advances2[10];
1376 IDWriteTextAnalyzer *analyzer;
1377 IDWriteFontFace *fontface;
1378 DWRITE_SCRIPT_ANALYSIS sa;
1379 DWRITE_GLYPH_OFFSET offsets[10];
1380 UINT16 clustermap[10];
1381 UINT16 glyphs1[10];
1382 UINT16 glyphs2[10];
1383 HRESULT hr;
1385 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
1386 ok(hr == S_OK, "got 0x%08x\n", hr);
1388 fontface = create_fontface();
1390 maxglyphcount = 1;
1391 sa.script = 0;
1392 sa.shapes = DWRITE_SCRIPT_SHAPES_DEFAULT;
1393 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test1W, lstrlenW(test1W), fontface, FALSE, FALSE, &sa, NULL,
1394 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs1, shapingprops, &actual_count);
1395 ok(hr == E_NOT_SUFFICIENT_BUFFER, "got 0x%08x\n", hr);
1397 if (0) {
1398 /* NULL fontface - crashes on Windows */
1399 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test1W, lstrlenW(test1W), NULL, FALSE, FALSE, &sa, NULL,
1400 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs1, shapingprops, &actual_count);
1403 /* invalid script id */
1404 maxglyphcount = 10;
1405 actual_count = 0;
1406 sa.script = 999;
1407 sa.shapes = DWRITE_SCRIPT_SHAPES_DEFAULT;
1408 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test1W, lstrlenW(test1W), fontface, FALSE, FALSE, &sa, NULL,
1409 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs1, shapingprops, &actual_count);
1410 ok(hr == S_OK, "got 0x%08x\n", hr);
1411 ok(actual_count == 4, "got %d\n", actual_count);
1412 ok(sa.script == 999, "got %u\n", sa.script);
1414 /* no '\t' -> ' ' replacement */
1415 maxglyphcount = 10;
1416 actual_count = 0;
1417 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test1W, lstrlenW(test1W), fontface, FALSE, FALSE, &sa, NULL,
1418 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs1, shapingprops, &actual_count);
1419 ok(hr == S_OK, "got 0x%08x\n", hr);
1420 ok(actual_count == 4, "got %d\n", actual_count);
1422 actual_count = 0;
1423 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test2W, lstrlenW(test2W), fontface, FALSE, FALSE, &sa, NULL,
1424 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs2, shapingprops, &actual_count);
1425 ok(hr == S_OK, "got 0x%08x\n", hr);
1426 ok(actual_count == 4, "got %d\n", actual_count);
1427 ok(glyphs1[2] != glyphs2[2], "got %d\n", glyphs1[2]);
1429 /* check that mirroring works */
1430 maxglyphcount = 10;
1431 actual_count = 0;
1432 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test1W, lstrlenW(test1W), fontface, FALSE, FALSE, &sa, NULL,
1433 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs1, shapingprops, &actual_count);
1434 ok(hr == S_OK, "got 0x%08x\n", hr);
1435 ok(actual_count == 4, "got %d\n", actual_count);
1437 actual_count = 0;
1438 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test1W, lstrlenW(test1W), fontface, FALSE, TRUE, &sa, NULL,
1439 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs2, shapingprops, &actual_count);
1440 ok(hr == S_OK, "got 0x%08x\n", hr);
1441 ok(actual_count == 4, "got %d\n", actual_count);
1442 ok(glyphs1[0] != glyphs2[0], "got %d\n", glyphs1[0]);
1444 /* embedded control codes, with unknown script id 0 */
1445 get_fontface_glyphs(fontface, test3W, glyphs2);
1446 get_fontface_advances(fontface, 10.0, glyphs2, advances2, 2);
1448 actual_count = 0;
1449 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test3W, lstrlenW(test3W), fontface, FALSE, TRUE, &sa, NULL,
1450 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs1, shapingprops, &actual_count);
1451 ok(hr == S_OK, "got 0x%08x\n", hr);
1452 ok(actual_count == 2, "got %d\n", actual_count);
1453 ok(glyphs1[0] == glyphs2[0], "got %u, expected %u\n", glyphs1[0], glyphs2[0]);
1454 ok(glyphs1[1] == glyphs2[1], "got %u, expected %u\n", glyphs1[1], glyphs2[1]);
1455 ok(shapingprops[0].isClusterStart == 1, "got %d\n", shapingprops[0].isClusterStart);
1456 ok(shapingprops[0].isZeroWidthSpace == 0, "got %d\n", shapingprops[0].isZeroWidthSpace);
1457 ok(shapingprops[1].isClusterStart == 1, "got %d\n", shapingprops[1].isClusterStart);
1458 ok(shapingprops[1].isZeroWidthSpace == 0, "got %d\n", shapingprops[1].isZeroWidthSpace);
1459 ok(clustermap[0] == 0, "got %d\n", clustermap[0]);
1460 ok(clustermap[1] == 1, "got %d\n", clustermap[1]);
1462 memset(advances, 0, sizeof(advances));
1463 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, test3W, clustermap, props, lstrlenW(test3W),
1464 glyphs1, shapingprops, actual_count, fontface, 10.0, FALSE, FALSE, &sa, NULL, NULL,
1465 NULL, 0, advances, offsets);
1466 ok(hr == S_OK, "got 0x%08x\n", hr);
1467 ok(advances[0] == advances2[0], "got %.2f, expected %.2f\n", advances[0], advances2[0]);
1468 ok(advances[1] == advances2[1], "got %.2f, expected %.2f\n", advances[1], advances2[1]);
1470 /* embedded control codes with proper script */
1471 sa.script = 0;
1472 get_script_analysis(test3W, &sa);
1473 ok(sa.script != 0, "got %d\n", sa.script);
1474 actual_count = 0;
1475 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test3W, lstrlenW(test3W), fontface, FALSE, FALSE, &sa, NULL,
1476 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs1, shapingprops, &actual_count);
1477 ok(hr == S_OK, "got 0x%08x\n", hr);
1478 ok(actual_count == 2, "got %d\n", actual_count);
1479 ok(glyphs1[0] == glyphs2[0], "got %u, expected %u\n", glyphs1[0], glyphs2[0]);
1480 ok(glyphs1[1] == glyphs2[1], "got %u, expected %u\n", glyphs1[1], glyphs2[1]);
1481 ok(shapingprops[0].isClusterStart == 1, "got %d\n", shapingprops[0].isClusterStart);
1482 ok(shapingprops[0].isZeroWidthSpace == 0, "got %d\n", shapingprops[0].isZeroWidthSpace);
1483 ok(shapingprops[1].isClusterStart == 1, "got %d\n", shapingprops[1].isClusterStart);
1484 ok(shapingprops[1].isZeroWidthSpace == 0, "got %d\n", shapingprops[1].isZeroWidthSpace);
1485 ok(clustermap[0] == 0, "got %d\n", clustermap[0]);
1486 ok(clustermap[1] == 1, "got %d\n", clustermap[1]);
1488 memset(advances, 0, sizeof(advances));
1489 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, test3W, clustermap, props, lstrlenW(test3W),
1490 glyphs1, shapingprops, actual_count, fontface, 10.0, FALSE, FALSE, &sa, NULL, NULL,
1491 NULL, 0, advances, offsets);
1492 ok(hr == S_OK, "got 0x%08x\n", hr);
1493 ok(advances[0] == advances2[0], "got %.2f, expected %.2f\n", advances[0], advances2[0]);
1494 ok(advances[1] == advances2[1], "got %.2f, expected %.2f\n", advances[1], advances2[1]);
1496 /* DWRITE_SCRIPT_SHAPES_NO_VISUAL run */
1497 maxglyphcount = 10;
1498 actual_count = 0;
1499 sa.script = 0;
1500 sa.shapes = DWRITE_SCRIPT_SHAPES_NO_VISUAL;
1501 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test1W, lstrlenW(test1W), fontface, FALSE, FALSE, &sa, NULL,
1502 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs1, shapingprops, &actual_count);
1503 ok(hr == S_OK, "got 0x%08x\n", hr);
1504 ok(actual_count == 4, "got %d\n", actual_count);
1505 ok(sa.script == 0, "got %u\n", sa.script);
1506 ok(!shapingprops[0].isZeroWidthSpace, "got %d\n", shapingprops[0].isZeroWidthSpace);
1508 IDWriteTextAnalyzer_Release(analyzer);
1509 IDWriteFontFace_Release(fontface);
1512 static BOOL has_feature(const DWRITE_FONT_FEATURE_TAG *tags, UINT32 count, DWRITE_FONT_FEATURE_TAG feature)
1514 UINT32 i;
1516 for (i = 0; i < count; i++)
1517 if (tags[i] == feature) return TRUE;
1518 return FALSE;
1521 static void test_GetTypographicFeatures(void)
1523 static const WCHAR localeW[] = {'c','a','d','a','b','r','a',0};
1524 static const WCHAR arabicW[] = {0x064a,0x064f,0x0633,0};
1525 static const WCHAR abcW[] = {'a','b','c',0};
1526 DWRITE_FONT_FEATURE_TAG tags[20];
1527 IDWriteTextAnalyzer2 *analyzer2;
1528 IDWriteTextAnalyzer *analyzer;
1529 IDWriteFontFace *fontface;
1530 DWRITE_SCRIPT_ANALYSIS sa;
1531 UINT32 count;
1532 HRESULT hr;
1533 BOOL ret;
1535 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
1536 ok(hr == S_OK, "got 0x%08x\n", hr);
1538 hr = IDWriteTextAnalyzer_QueryInterface(analyzer, &IID_IDWriteTextAnalyzer2, (void**)&analyzer2);
1539 IDWriteTextAnalyzer_Release(analyzer);
1540 if (hr != S_OK) {
1541 win_skip("GetTypographicFeatures() is not supported.\n");
1542 return;
1545 fontface = create_fontface();
1547 get_script_analysis(abcW, &sa);
1548 count = 0;
1549 hr = IDWriteTextAnalyzer2_GetTypographicFeatures(analyzer2, fontface, sa, NULL, 0, &count, NULL);
1550 todo_wine {
1551 ok(hr == E_NOT_SUFFICIENT_BUFFER, "got 0x%08x\n", hr);
1552 ok(count > 0, "got %u\n", count);
1554 /* invalid locale name is ignored */
1555 get_script_analysis(abcW, &sa);
1556 count = 0;
1557 hr = IDWriteTextAnalyzer2_GetTypographicFeatures(analyzer2, fontface, sa, localeW, 0, &count, NULL);
1558 todo_wine {
1559 ok(hr == E_NOT_SUFFICIENT_BUFFER, "got 0x%08x\n", hr);
1560 ok(count > 0, "got %u\n", count);
1562 /* both GSUB and GPOS features are reported */
1563 get_script_analysis(arabicW, &sa);
1564 memset(tags, 0, sizeof(tags));
1565 count = 0;
1566 hr = IDWriteTextAnalyzer2_GetTypographicFeatures(analyzer2, fontface, sa, NULL, sizeof(tags)/sizeof(tags[0]), &count, tags);
1567 ok(hr == S_OK, "got 0x%08x\n", hr);
1568 todo_wine {
1569 ok(count > 0, "got %u\n", count);
1570 ret = has_feature(tags, count, DWRITE_FONT_FEATURE_TAG_CONTEXTUAL_ALTERNATES);
1571 ok(ret, "expected 'calt' feature\n");
1572 ret = has_feature(tags, count, DWRITE_FONT_FEATURE_TAG_MARK_TO_MARK_POSITIONING);
1573 ok(ret, "expected 'mkmk' feature\n");
1575 get_script_analysis(abcW, &sa);
1576 memset(tags, 0, sizeof(tags));
1577 count = 0;
1578 hr = IDWriteTextAnalyzer2_GetTypographicFeatures(analyzer2, fontface, sa, NULL, sizeof(tags)/sizeof(tags[0]), &count, tags);
1579 ok(hr == S_OK, "got 0x%08x\n", hr);
1580 todo_wine {
1581 ok(count > 0, "got %u\n", count);
1582 ret = has_feature(tags, count, DWRITE_FONT_FEATURE_TAG_GLYPH_COMPOSITION_DECOMPOSITION);
1583 ok(ret, "expected 'ccmp' feature\n");
1584 ret = has_feature(tags, count, DWRITE_FONT_FEATURE_TAG_MARK_TO_MARK_POSITIONING);
1585 ok(ret, "expected 'mkmk' feature\n");
1587 ret = has_feature(tags, count, DWRITE_FONT_FEATURE_TAG_CONTEXTUAL_ALTERNATES);
1588 ok(!ret, "unexpected 'calt' feature\n");
1590 IDWriteFontFace_Release(fontface);
1591 IDWriteTextAnalyzer2_Release(analyzer2);
1594 static void test_GetGlyphPlacements(void)
1596 DWRITE_SHAPING_GLYPH_PROPERTIES glyphprops[2];
1597 DWRITE_SHAPING_TEXT_PROPERTIES textprops[2];
1598 static const WCHAR aW[] = {'A','D',0};
1599 UINT16 clustermap[2], glyphs[2];
1600 DWRITE_GLYPH_OFFSET offsets[2];
1601 IDWriteTextAnalyzer *analyzer;
1602 IDWriteFontFace *fontface;
1603 DWRITE_SCRIPT_ANALYSIS sa;
1604 FLOAT advances[2];
1605 UINT32 count, len;
1606 WCHAR *path;
1607 HRESULT hr;
1609 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
1610 ok(hr == S_OK, "got 0x%08x\n", hr);
1612 path = create_testfontfile(test_fontfile);
1613 fontface = create_testfontface(path);
1615 get_script_analysis(aW, &sa);
1616 count = 0;
1617 len = lstrlenW(aW);
1618 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, aW, len, fontface, FALSE, FALSE, &sa, NULL,
1619 NULL, NULL, NULL, 0, len, clustermap, textprops, glyphs, glyphprops, &count);
1620 ok(hr == S_OK, "got 0x%08x\n", hr);
1621 ok(count == 2, "got %u\n", count);
1623 /* just return on zero glyphs */
1624 advances[0] = advances[1] = 1.0;
1625 offsets[0].advanceOffset = offsets[0].ascenderOffset = 2.0;
1626 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, clustermap, textprops,
1627 len, glyphs, glyphprops, 0, fontface, 0.0, FALSE, FALSE, &sa, NULL, NULL,
1628 NULL, 0, advances, offsets);
1629 ok(hr == S_OK, "got 0x%08x\n", hr);
1630 ok(advances[0] == 1.0, "got %.2f\n", advances[0]);
1631 ok(offsets[0].advanceOffset == 2.0 && offsets[0].ascenderOffset == 2.0, "got %.2f,%.2f\n",
1632 offsets[0].advanceOffset, offsets[0].ascenderOffset);
1634 /* advances/offsets are scaled with provided font emSize and designed eM box size */
1635 advances[0] = advances[1] = 1.0;
1636 memset(offsets, 0xcc, sizeof(offsets));
1637 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, clustermap, textprops,
1638 len, glyphs, glyphprops, len, fontface, 0.0, FALSE, FALSE, &sa, NULL, NULL,
1639 NULL, 0, advances, offsets);
1640 ok(hr == S_OK, "got 0x%08x\n", hr);
1641 ok(advances[0] == 0.0, "got %.2f\n", advances[0]);
1642 ok(offsets[0].advanceOffset == 0.0 && offsets[0].ascenderOffset == 0.0, "got %.2f,%.2f\n",
1643 offsets[0].advanceOffset, offsets[0].ascenderOffset);
1645 advances[0] = advances[1] = 1.0;
1646 memset(offsets, 0xcc, sizeof(offsets));
1647 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, clustermap, textprops,
1648 len, glyphs, glyphprops, len, fontface, 2048.0, FALSE, FALSE, &sa, NULL, NULL,
1649 NULL, 0, advances, offsets);
1650 ok(hr == S_OK, "got 0x%08x\n", hr);
1651 ok(advances[0] == 1000.0, "got %.2f\n", advances[0]);
1652 ok(offsets[0].advanceOffset == 0.0 && offsets[0].ascenderOffset == 0.0, "got %.2f,%.2f\n",
1653 offsets[0].advanceOffset, offsets[0].ascenderOffset);
1655 advances[0] = advances[1] = 1.0;
1656 memset(offsets, 0xcc, sizeof(offsets));
1657 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, clustermap, textprops,
1658 len, glyphs, glyphprops, len, fontface, 1024.0, FALSE, FALSE, &sa, NULL, NULL,
1659 NULL, 0, advances, offsets);
1660 ok(hr == S_OK, "got 0x%08x\n", hr);
1661 ok(advances[0] == 500.0, "got %.2f\n", advances[0]);
1662 ok(advances[1] == 500.0, "got %.2f\n", advances[1]);
1663 ok(offsets[0].advanceOffset == 0.0 && offsets[0].ascenderOffset == 0.0, "got %.2f,%.2f\n",
1664 offsets[0].advanceOffset, offsets[0].ascenderOffset);
1666 advances[0] = advances[1] = 1.0;
1667 memset(offsets, 0xcc, sizeof(offsets));
1668 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, clustermap, textprops,
1669 len, glyphs, glyphprops, len, fontface, 20.48, FALSE, FALSE, &sa, NULL, NULL,
1670 NULL, 0, advances, offsets);
1671 ok(hr == S_OK, "got 0x%08x\n", hr);
1672 ok(advances[0] == 10.0, "got %.2f\n", advances[0]);
1673 ok(advances[1] == 10.0, "got %.2f\n", advances[1]);
1674 ok(offsets[0].advanceOffset == 0.0 && offsets[0].ascenderOffset == 0.0, "got %.2f,%.2f\n",
1675 offsets[0].advanceOffset, offsets[0].ascenderOffset);
1677 /* without clustermap */
1678 advances[0] = advances[1] = 1.0;
1679 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, NULL, textprops,
1680 len, glyphs, glyphprops, len, fontface, 1024.0, FALSE, FALSE, &sa, NULL, NULL,
1681 NULL, 0, advances, offsets);
1682 ok(hr == S_OK, "got 0x%08x\n", hr);
1683 ok(advances[0] == 500.0, "got %.2f\n", advances[0]);
1684 ok(advances[1] == 500.0, "got %.2f\n", advances[1]);
1686 /* it's happy to use negative size too */
1687 advances[0] = advances[1] = 1.0;
1688 memset(offsets, 0xcc, sizeof(offsets));
1689 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, clustermap, textprops,
1690 len, glyphs, glyphprops, len, fontface, -10.24, FALSE, FALSE, &sa, NULL, NULL,
1691 NULL, 0, advances, offsets);
1692 ok(hr == S_OK, "got 0x%08x\n", hr);
1693 ok(advances[0] == -5.0, "got %.2f\n", advances[0]);
1694 ok(offsets[0].advanceOffset == 0.0 && offsets[0].ascenderOffset == 0.0, "got %.2f,%.2f\n",
1695 offsets[0].advanceOffset, offsets[0].ascenderOffset);
1697 /* DWRITE_SCRIPT_SHAPES_NO_VISUAL has no effect on placement */
1698 sa.shapes = DWRITE_SCRIPT_SHAPES_NO_VISUAL;
1699 advances[0] = advances[1] = 1.0f;
1700 memset(offsets, 0xcc, sizeof(offsets));
1701 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, clustermap, textprops,
1702 len, glyphs, glyphprops, len, fontface, 2048.0f, FALSE, FALSE, &sa, NULL, NULL,
1703 NULL, 0, advances, offsets);
1704 ok(hr == S_OK, "got 0x%08x\n", hr);
1705 ok(advances[0] == 1000.0f, "got %.2f\n", advances[0]);
1706 ok(advances[1] == 1000.0f, "got %.2f\n", advances[1]);
1707 ok(offsets[0].advanceOffset == 0.0f && offsets[0].ascenderOffset == 0.0f, "got %.2f,%.2f\n",
1708 offsets[0].advanceOffset, offsets[0].ascenderOffset);
1710 /* isZeroWidthSpace */
1711 sa.shapes = DWRITE_SCRIPT_SHAPES_DEFAULT;
1712 advances[0] = advances[1] = 1.0f;
1713 memset(offsets, 0xcc, sizeof(offsets));
1714 glyphprops[0].isZeroWidthSpace = 1;
1715 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, clustermap, textprops,
1716 len, glyphs, glyphprops, len, fontface, 2048.0f, FALSE, FALSE, &sa, NULL, NULL,
1717 NULL, 0, advances, offsets);
1718 ok(hr == S_OK, "got 0x%08x\n", hr);
1719 ok(advances[0] == 0.0f, "got %.2f\n", advances[0]);
1720 ok(advances[1] == 1000.0f, "got %.2f\n", advances[1]);
1721 ok(offsets[0].advanceOffset == 0.0f && offsets[0].ascenderOffset == 0.0f, "got %.2f,%.2f\n",
1722 offsets[0].advanceOffset, offsets[0].ascenderOffset);
1724 IDWriteTextAnalyzer_Release(analyzer);
1725 IDWriteFontFace_Release(fontface);
1726 DELETE_FONTFILE(path);
1729 struct spacing_test {
1730 FLOAT leading;
1731 FLOAT trailing;
1732 FLOAT min_advance;
1733 FLOAT advances[3];
1734 FLOAT offsets[3];
1735 FLOAT modified_advances[3];
1736 FLOAT modified_offsets[3];
1737 BOOL single_cluster;
1738 DWRITE_SHAPING_GLYPH_PROPERTIES props[3];
1741 static const struct spacing_test spacing_tests[] = {
1742 { 0.0, 0.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 } }, /* 0 */
1743 { 0.0, 0.0, 2.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 } },
1744 { 1.0, 0.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 11.0, 12.0 }, { 3.0, 4.0 } },
1745 { 1.0, 1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 12.0, 13.0 }, { 3.0, 4.0 } },
1746 { 1.0, -1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 3.0, 4.0 } },
1747 { 0.0, -10.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 0.0, 1.0 }, { 2.0, 3.0 } }, /* 5 */
1748 { -5.0, -4.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 5.0, 5.0 }, { -1.0, -0.5 } },
1749 { -5.0, -5.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 5.0, 5.0 }, { -0.5, 0.0 } },
1750 { 2.0, 0.0, 5.0, { 1.0, 2.0 }, { 2.0, 3.0 }, { 7.0, 7.0 }, { 6.0, 6.5 } },
1751 { 2.0, 1.0, 5.0, { 1.0, 2.0 }, { 2.0, 3.0 }, { 8.0, 8.0 }, { 6.0, 6.5 } },
1752 { 2.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 7.0, 7.0 }, { 4.0, 5.0 } }, /* 10 */
1753 { 1.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 6.0, 6.0 }, { 3.0, 4.0 } },
1754 { -10.0, 1.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 6.0, 6.0 }, { -3.0, -3.0 } },
1755 { 0.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 5.0, 5.0 }, { 2.0, 3.0 } },
1756 { 1.0, -10.0, -5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 0.0, 0.0 }, { 2.0, 3.0 } },
1757 { -10.0, 1.0, 5.0, { 8.0, 11.0 }, { 2.0, 3.0 }, { 6.0, 6.0 }, { -1.0, -3.0 } }, /* 15 */
1758 /* cluster of more than 1 glyph */
1759 { 0.0, 0.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 }, TRUE },
1760 { 1.0, 0.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.5 }, { 11.0, 11.0 }, { 3.0, 3.5 }, TRUE },
1761 { 1.0, 1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 11.0, 12.0 }, { 3.0, 3.0 }, TRUE },
1762 { 1.0, -1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 11.0, 10.0 }, { 3.0, 3.0 }, TRUE },
1763 { 0.0, -10.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 1.0 }, { 2.0, 3.0 }, TRUE }, /* 20 */
1764 { 0.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 1.0 }, { 2.0, 3.0 }, TRUE },
1765 { 1.0, -10.0, -5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 0.0, 0.0 }, { 2.0, 3.0 }, TRUE },
1766 { -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 },
1767 { -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 },
1768 { -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 */
1769 { -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 },
1770 { 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 },
1771 { -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 },
1772 /* isZeroWidthSpace set */
1773 { 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 } } },
1774 { 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 */
1775 { 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 } } },
1776 { 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 } } },
1777 { -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 } } },
1778 { -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 }} },
1779 { 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 */
1780 { 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 } } },
1781 { 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 } } },
1782 { 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 } } },
1783 { -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 } } },
1784 { 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 */
1785 { 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 } } },
1786 { -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 } } },
1787 /* isDiacritic */
1788 { 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 } } },
1789 { 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 } } },
1790 { 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 */
1791 { 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 } } },
1792 { -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 } } },
1793 { -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 }} },
1794 { 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 } } },
1795 { 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 */
1796 { 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 } } },
1797 { 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 } } },
1798 { -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 } } },
1799 { 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 } } },
1800 { 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 */
1801 { -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 } } },
1804 static void test_ApplyCharacterSpacing(void)
1806 DWRITE_SHAPING_GLYPH_PROPERTIES props[3];
1807 IDWriteTextAnalyzer1 *analyzer1;
1808 IDWriteTextAnalyzer *analyzer;
1809 UINT16 clustermap[2];
1810 HRESULT hr;
1811 int i;
1813 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
1814 ok(hr == S_OK, "got 0x%08x\n", hr);
1816 hr = IDWriteTextAnalyzer_QueryInterface(analyzer, &IID_IDWriteTextAnalyzer1, (void**)&analyzer1);
1817 IDWriteTextAnalyzer_Release(analyzer);
1818 if (hr != S_OK) {
1819 win_skip("ApplyCharacterSpacing() is not supported.\n");
1820 return;
1823 for (i = 0; i < sizeof(spacing_tests)/sizeof(spacing_tests[0]); i++) {
1824 const struct spacing_test *ptr = spacing_tests + i;
1825 DWRITE_GLYPH_OFFSET offsets[3];
1826 UINT32 glyph_count;
1827 FLOAT advances[3];
1829 offsets[0].advanceOffset = ptr->offsets[0];
1830 offsets[1].advanceOffset = ptr->offsets[1];
1831 offsets[2].advanceOffset = ptr->offsets[2];
1832 /* Ascender offsets are never touched as spacing applies in reading direction only,
1833 we'll only test them to see if they are not changed */
1834 offsets[0].ascenderOffset = 23.0;
1835 offsets[1].ascenderOffset = 32.0;
1836 offsets[2].ascenderOffset = 31.0;
1838 glyph_count = ptr->advances[2] > 0.0 ? 3 : 2;
1839 if (ptr->single_cluster) {
1840 clustermap[0] = 0;
1841 clustermap[1] = 0;
1843 else {
1844 /* trivial case with one glyph per cluster */
1845 clustermap[0] = 0;
1846 clustermap[1] = 1;
1849 advances[0] = advances[1] = 123.45;
1850 memcpy(props, ptr->props, sizeof(props));
1852 hr = IDWriteTextAnalyzer1_ApplyCharacterSpacing(analyzer1,
1853 ptr->leading,
1854 ptr->trailing,
1855 ptr->min_advance,
1856 sizeof(clustermap)/sizeof(clustermap[0]),
1857 glyph_count,
1858 clustermap,
1859 ptr->advances,
1860 offsets,
1861 props,
1862 advances,
1863 offsets);
1864 ok(hr == (ptr->min_advance < 0.0f ? E_INVALIDARG : S_OK), "%d: got 0x%08x\n", i, hr);
1866 if (hr == S_OK) {
1867 ok(ptr->modified_advances[0] == advances[0], "%d: got advance[0] %.2f, expected %.2f\n", i, advances[0], ptr->modified_advances[0]);
1868 ok(ptr->modified_advances[1] == advances[1], "%d: got advance[1] %.2f, expected %.2f\n", i, advances[1], ptr->modified_advances[1]);
1869 if (glyph_count > 2)
1870 ok(ptr->modified_advances[2] == advances[2], "%d: got advance[2] %.2f, expected %.2f\n", i, advances[2], ptr->modified_advances[2]);
1872 ok(ptr->modified_offsets[0] == offsets[0].advanceOffset, "%d: got offset[0] %.2f, expected %.2f\n", i,
1873 offsets[0].advanceOffset, ptr->modified_offsets[0]);
1874 ok(ptr->modified_offsets[1] == offsets[1].advanceOffset, "%d: got offset[1] %.2f, expected %.2f\n", i,
1875 offsets[1].advanceOffset, ptr->modified_offsets[1]);
1876 if (glyph_count > 2)
1877 ok(ptr->modified_offsets[2] == offsets[2].advanceOffset, "%d: got offset[2] %.2f, expected %.2f\n", i,
1878 offsets[2].advanceOffset, ptr->modified_offsets[2]);
1880 ok(offsets[0].ascenderOffset == 23.0, "%d: unexpected ascenderOffset %.2f\n", i, offsets[0].ascenderOffset);
1881 ok(offsets[1].ascenderOffset == 32.0, "%d: unexpected ascenderOffset %.2f\n", i, offsets[1].ascenderOffset);
1882 ok(offsets[2].ascenderOffset == 31.0, "%d: unexpected ascenderOffset %.2f\n", i, offsets[2].ascenderOffset);
1884 else {
1885 ok(ptr->modified_advances[0] == advances[0], "%d: got advance[0] %.2f, expected %.2f\n", i, advances[0], ptr->modified_advances[0]);
1886 ok(ptr->modified_advances[1] == advances[1], "%d: got advance[1] %.2f, expected %.2f\n", i, advances[1], ptr->modified_advances[1]);
1887 ok(ptr->offsets[0] == offsets[0].advanceOffset, "%d: got offset[0] %.2f, expected %.2f\n", i,
1888 offsets[0].advanceOffset, ptr->modified_offsets[0]);
1889 ok(ptr->offsets[1] == offsets[1].advanceOffset, "%d: got offset[1] %.2f, expected %.2f\n", i,
1890 offsets[1].advanceOffset, ptr->modified_offsets[1]);
1891 ok(offsets[0].ascenderOffset == 23.0, "%d: unexpected ascenderOffset %.2f\n", i, offsets[0].ascenderOffset);
1892 ok(offsets[1].ascenderOffset == 32.0, "%d: unexpected ascenderOffset %.2f\n", i, offsets[1].ascenderOffset);
1895 /* same, with argument aliasing */
1896 memcpy(advances, ptr->advances, glyph_count * sizeof(*advances));
1897 offsets[0].advanceOffset = ptr->offsets[0];
1898 offsets[1].advanceOffset = ptr->offsets[1];
1899 offsets[2].advanceOffset = ptr->offsets[2];
1900 /* Ascender offsets are never touched as spacing applies in reading direction only,
1901 we'll only test them to see if they are not changed */
1902 offsets[0].ascenderOffset = 23.0f;
1903 offsets[1].ascenderOffset = 32.0f;
1904 offsets[2].ascenderOffset = 31.0f;
1906 hr = IDWriteTextAnalyzer1_ApplyCharacterSpacing(analyzer1,
1907 ptr->leading,
1908 ptr->trailing,
1909 ptr->min_advance,
1910 sizeof(clustermap)/sizeof(clustermap[0]),
1911 glyph_count,
1912 clustermap,
1913 advances,
1914 offsets,
1915 props,
1916 advances,
1917 offsets);
1918 ok(hr == (ptr->min_advance < 0.0f ? E_INVALIDARG : S_OK), "%d: got 0x%08x\n", i, hr);
1920 if (hr == S_OK) {
1921 ok(ptr->modified_advances[0] == advances[0], "%d: got advance[0] %.2f, expected %.2f\n", i, advances[0], ptr->modified_advances[0]);
1922 ok(ptr->modified_advances[1] == advances[1], "%d: got advance[1] %.2f, expected %.2f\n", i, advances[1], ptr->modified_advances[1]);
1923 if (glyph_count > 2)
1924 ok(ptr->modified_advances[2] == advances[2], "%d: got advance[2] %.2f, expected %.2f\n", i, advances[2], ptr->modified_advances[2]);
1926 ok(ptr->modified_offsets[0] == offsets[0].advanceOffset, "%d: got offset[0] %.2f, expected %.2f\n", i,
1927 offsets[0].advanceOffset, ptr->modified_offsets[0]);
1928 ok(ptr->modified_offsets[1] == offsets[1].advanceOffset, "%d: got offset[1] %.2f, expected %.2f\n", i,
1929 offsets[1].advanceOffset, ptr->modified_offsets[1]);
1930 if (glyph_count > 2)
1931 ok(ptr->modified_offsets[2] == offsets[2].advanceOffset, "%d: got offset[2] %.2f, expected %.2f\n", i,
1932 offsets[2].advanceOffset, ptr->modified_offsets[2]);
1934 ok(offsets[0].ascenderOffset == 23.0f, "%d: unexpected ascenderOffset %.2f\n", i, offsets[0].ascenderOffset);
1935 ok(offsets[1].ascenderOffset == 32.0f, "%d: unexpected ascenderOffset %.2f\n", i, offsets[1].ascenderOffset);
1936 ok(offsets[2].ascenderOffset == 31.0f, "%d: unexpected ascenderOffset %.2f\n", i, offsets[2].ascenderOffset);
1938 else {
1939 /* with aliased advances original values are retained */
1940 ok(ptr->advances[0] == advances[0], "%d: got advance[0] %.2f, expected %.2f\n", i, advances[0], ptr->advances[0]);
1941 ok(ptr->advances[1] == advances[1], "%d: got advance[1] %.2f, expected %.2f\n", i, advances[1], ptr->advances[1]);
1942 ok(ptr->offsets[0] == offsets[0].advanceOffset, "%d: got offset[0] %.2f, expected %.2f\n", i,
1943 offsets[0].advanceOffset, ptr->modified_offsets[0]);
1944 ok(ptr->offsets[1] == offsets[1].advanceOffset, "%d: got offset[1] %.2f, expected %.2f\n", i,
1945 offsets[1].advanceOffset, ptr->modified_offsets[1]);
1946 ok(offsets[0].ascenderOffset == 23.0f, "%d: unexpected ascenderOffset %.2f\n", i, offsets[0].ascenderOffset);
1947 ok(offsets[1].ascenderOffset == 32.0f, "%d: unexpected ascenderOffset %.2f\n", i, offsets[1].ascenderOffset);
1951 IDWriteTextAnalyzer1_Release(analyzer1);
1954 struct orientation_transf_test {
1955 DWRITE_GLYPH_ORIENTATION_ANGLE angle;
1956 BOOL is_sideways;
1957 DWRITE_MATRIX m;
1960 static const struct orientation_transf_test ot_tests[] = {
1961 { DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES, FALSE, { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 } },
1962 { DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES, FALSE, { 0.0, 1.0, -1.0, 0.0, 0.0, 0.0 } },
1963 { DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES, FALSE, { -1.0, 0.0, 0.0, -1.0, 0.0, 0.0 } },
1964 { DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES, FALSE, { 0.0, -1.0, 1.0, 0.0, 0.0, 0.0 } },
1965 { DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES, TRUE, { 0.0, 1.0, -1.0, 0.0, 0.0, 0.0 } },
1966 { DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES, TRUE, { -1.0, 0.0, 0.0, -1.0, 0.0, 0.0 } },
1967 { DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES, TRUE, { 0.0, -1.0, 1.0, 0.0, 0.0, 0.0 } },
1968 { DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES, TRUE, { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 } }
1971 static inline const char *dbgstr_matrix(const DWRITE_MATRIX *m)
1973 static char buff[64];
1974 sprintf(buff, "{%.2f, %.2f, %.2f, %.2f, %.2f, %.2f}", m->m11, m->m12,
1975 m->m21, m->m22, m->dx, m->dy);
1976 return buff;
1979 static void test_GetGlyphOrientationTransform(void)
1981 IDWriteTextAnalyzer2 *analyzer2;
1982 IDWriteTextAnalyzer1 *analyzer1;
1983 IDWriteTextAnalyzer *analyzer;
1984 FLOAT originx, originy;
1985 DWRITE_MATRIX m;
1986 HRESULT hr;
1987 int i;
1989 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
1990 ok(hr == S_OK, "got 0x%08x\n", hr);
1992 hr = IDWriteTextAnalyzer_QueryInterface(analyzer, &IID_IDWriteTextAnalyzer1, (void**)&analyzer1);
1993 IDWriteTextAnalyzer_Release(analyzer);
1994 if (hr != S_OK) {
1995 win_skip("GetGlyphOrientationTransform() is not supported.\n");
1996 return;
1999 /* invalid angle value */
2000 memset(&m, 0xcc, sizeof(m));
2001 hr = IDWriteTextAnalyzer1_GetGlyphOrientationTransform(analyzer1,
2002 DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES + 1, FALSE, &m);
2003 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
2004 ok(m.m11 == 0.0, "got %.2f\n", m.m11);
2006 for (i = 0; i < sizeof(ot_tests)/sizeof(ot_tests[0]); i++) {
2007 memset(&m, 0, sizeof(m));
2008 hr = IDWriteTextAnalyzer1_GetGlyphOrientationTransform(analyzer1, ot_tests[i].angle,
2009 ot_tests[i].is_sideways, &m);
2010 ok(hr == S_OK, "got 0x%08x\n", hr);
2011 ok(!memcmp(&ot_tests[i].m, &m, sizeof(m)), "%d: wrong matrix %s\n", i, dbgstr_matrix(&m));
2014 hr = IDWriteTextAnalyzer1_QueryInterface(analyzer1, &IID_IDWriteTextAnalyzer2, (void**)&analyzer2);
2015 IDWriteTextAnalyzer1_Release(analyzer1);
2016 if (hr != S_OK) {
2017 win_skip("IDWriteTextAnalyzer2::GetGlyphOrientationTransform() is not supported.\n");
2018 return;
2021 /* invalid angle value */
2022 memset(&m, 0xcc, sizeof(m));
2023 hr = IDWriteTextAnalyzer2_GetGlyphOrientationTransform(analyzer2,
2024 DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES + 1, FALSE, 0.0, 0.0, &m);
2025 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
2026 ok(m.m11 == 0.0, "got %.2f\n", m.m11);
2028 originx = 50.0;
2029 originy = 60.0;
2030 for (i = 0; i < sizeof(ot_tests)/sizeof(ot_tests[0]); i++) {
2031 DWRITE_GLYPH_ORIENTATION_ANGLE angle = DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES;
2032 DWRITE_MATRIX m_exp;
2034 memset(&m, 0, sizeof(m));
2036 /* zero offset gives same result as a call from IDWriteTextAnalyzer1 */
2037 hr = IDWriteTextAnalyzer2_GetGlyphOrientationTransform(analyzer2, ot_tests[i].angle,
2038 ot_tests[i].is_sideways, 0.0, 0.0, &m);
2039 ok(hr == S_OK, "got 0x%08x\n", hr);
2040 ok(!memcmp(&ot_tests[i].m, &m, sizeof(m)), "%d: wrong matrix %s\n", i, dbgstr_matrix(&m));
2042 m_exp = ot_tests[i].m;
2043 hr = IDWriteTextAnalyzer2_GetGlyphOrientationTransform(analyzer2, ot_tests[i].angle,
2044 ot_tests[i].is_sideways, originx, originy, &m);
2045 ok(hr == S_OK, "got 0x%08x\n", hr);
2047 /* 90 degrees more for sideways */
2048 if (ot_tests[i].is_sideways) {
2049 switch (ot_tests[i].angle)
2051 case DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES:
2052 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES;
2053 break;
2054 case DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES:
2055 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES;
2056 break;
2057 case DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES:
2058 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES;
2059 break;
2060 case DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES:
2061 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES;
2062 break;
2063 default:
2067 else
2068 angle = ot_tests[i].angle;
2070 /* set expected offsets */
2071 switch (angle)
2073 case DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES:
2074 break;
2075 case DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES:
2076 m_exp.dx = originx + originy;
2077 m_exp.dy = originy - originx;
2078 break;
2079 case DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES:
2080 m_exp.dx = originx + originx;
2081 m_exp.dy = originy + originy;
2082 break;
2083 case DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES:
2084 m_exp.dx = originx - originy;
2085 m_exp.dy = originy + originx;
2086 break;
2087 default:
2091 ok(!memcmp(&m_exp, &m, sizeof(m)), "%d: wrong matrix %s\n", i, dbgstr_matrix(&m));
2094 IDWriteTextAnalyzer2_Release(analyzer2);
2097 static void test_GetBaseline(void)
2099 DWRITE_SCRIPT_ANALYSIS sa = { 0 };
2100 IDWriteTextAnalyzer1 *analyzer1;
2101 IDWriteTextAnalyzer *analyzer;
2102 IDWriteFontFace *fontface;
2103 INT32 baseline;
2104 BOOL exists;
2105 HRESULT hr;
2107 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
2108 ok(hr == S_OK, "got 0x%08x\n", hr);
2110 hr = IDWriteTextAnalyzer_QueryInterface(analyzer, &IID_IDWriteTextAnalyzer1, (void**)&analyzer1);
2111 IDWriteTextAnalyzer_Release(analyzer);
2112 if (hr != S_OK) {
2113 win_skip("GetBaseline() is not supported.\n");
2114 return;
2117 fontface = create_fontface();
2119 /* Tahoma doesn't have BASE table, it doesn't work even with simulation enabled */
2120 exists = TRUE;
2121 baseline = 456;
2122 hr = IDWriteTextAnalyzer1_GetBaseline(analyzer1,
2123 fontface,
2124 DWRITE_BASELINE_DEFAULT,
2125 FALSE,
2126 TRUE,
2128 NULL,
2129 &baseline,
2130 &exists);
2131 todo_wine {
2132 ok(hr == S_OK, "got 0x%08x\n", hr);
2133 ok(baseline == 0, "got %d\n", baseline);
2134 ok(exists == FALSE, "got %d\n", exists);
2136 exists = TRUE;
2137 baseline = 456;
2138 hr = IDWriteTextAnalyzer1_GetBaseline(analyzer1,
2139 fontface,
2140 DWRITE_BASELINE_ROMAN,
2141 FALSE,
2142 TRUE,
2144 NULL,
2145 &baseline,
2146 &exists);
2147 todo_wine {
2148 ok(hr == S_OK, "got 0x%08x\n", hr);
2149 ok(baseline == 0, "got %d\n", baseline);
2150 ok(exists == FALSE, "got %d\n", exists);
2152 IDWriteFontFace_Release(fontface);
2153 IDWriteTextAnalyzer1_Release(analyzer1);
2156 static inline BOOL float_eq(FLOAT left, FLOAT right)
2158 int x = *(int *)&left;
2159 int y = *(int *)&right;
2161 if (x < 0)
2162 x = INT_MIN - x;
2163 if (y < 0)
2164 y = INT_MIN - y;
2166 return abs(x - y) <= 8;
2169 static void test_GetGdiCompatibleGlyphPlacements(void)
2171 static const WCHAR strW[] = {'A',0};
2172 DWRITE_SHAPING_GLYPH_PROPERTIES glyphprops[1];
2173 DWRITE_SHAPING_TEXT_PROPERTIES textprops[1];
2174 DWRITE_SCRIPT_ANALYSIS sa = { 0 };
2175 IDWriteTextAnalyzer *analyzer;
2176 IDWriteFontFace *fontface;
2177 UINT16 clustermap[1];
2178 HRESULT hr;
2179 UINT32 count;
2180 UINT16 glyphs[1];
2181 FLOAT advance;
2182 DWRITE_GLYPH_OFFSET offsets[1];
2183 DWRITE_FONT_METRICS fontmetrics;
2184 FLOAT emsize;
2186 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
2187 ok(hr == S_OK, "got 0x%08x\n", hr);
2189 fontface = create_fontface();
2191 IDWriteFontFace_GetMetrics(fontface, &fontmetrics);
2193 count = 0;
2194 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, strW, 1, fontface,
2195 FALSE, FALSE, &sa, NULL, NULL, NULL, NULL, 0, 1, clustermap,
2196 textprops, glyphs, glyphprops, &count);
2197 ok(hr == S_OK, "got 0x%08x\n", hr);
2198 ok(count == 1, "got %u\n", count);
2200 for (emsize = 12.0; emsize <= 20.0; emsize += 1.0) {
2201 FLOAT compatadvance, expected, ppdip;
2202 DWRITE_GLYPH_METRICS metrics;
2204 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, strW, clustermap,
2205 textprops, 1, glyphs, glyphprops, count, fontface, emsize, FALSE, FALSE,
2206 &sa, NULL, NULL, NULL, 0, &advance, offsets);
2207 ok(hr == S_OK, "got 0x%08x\n", hr);
2208 ok(advance > 0.0, "got %f\n", advance);
2210 /* 1 ppdip, no transform */
2211 ppdip = 1.0;
2212 hr = IDWriteFontFace_GetGdiCompatibleGlyphMetrics(fontface, emsize, ppdip, NULL, FALSE,
2213 glyphs, 1, &metrics, FALSE);
2214 ok(hr == S_OK, "got 0x%08x\n", hr);
2216 expected = floorf(metrics.advanceWidth * emsize * ppdip / fontmetrics.designUnitsPerEm + 0.5f) / ppdip;
2217 hr = IDWriteTextAnalyzer_GetGdiCompatibleGlyphPlacements(analyzer, strW,
2218 clustermap, textprops, 1, glyphs, glyphprops, count, fontface, emsize,
2219 ppdip, NULL, FALSE, FALSE, FALSE, &sa, NULL, NULL, NULL, 0, &compatadvance, offsets);
2220 ok(hr == S_OK, "got 0x%08x\n", hr);
2221 ok(compatadvance == expected, "%.0f: got advance %f, expected %f, natural %f\n", emsize,
2222 compatadvance, expected, advance);
2224 /* 1.2 ppdip, no transform */
2225 ppdip = 1.2;
2226 hr = IDWriteFontFace_GetGdiCompatibleGlyphMetrics(fontface, emsize, ppdip, NULL, FALSE,
2227 glyphs, 1, &metrics, FALSE);
2228 ok(hr == S_OK, "got 0x%08x\n", hr);
2230 expected = floorf(metrics.advanceWidth * emsize * ppdip / fontmetrics.designUnitsPerEm + 0.5f) / ppdip;
2231 hr = IDWriteTextAnalyzer_GetGdiCompatibleGlyphPlacements(analyzer, strW,
2232 clustermap, textprops, 1, glyphs, glyphprops, count, fontface, emsize,
2233 ppdip, NULL, FALSE, FALSE, FALSE, &sa, NULL, NULL, NULL, 0, &compatadvance, offsets);
2234 ok(hr == S_OK, "got 0x%08x\n", hr);
2235 ok(float_eq(compatadvance, expected), "%.0f: got advance %f, expected %f, natural %f\n", emsize,
2236 compatadvance, expected, advance);
2239 IDWriteFontFace_Release(fontface);
2240 IDWriteTextAnalyzer_Release(analyzer);
2243 struct bidi_test
2245 const WCHAR text[BIDI_LEVELS_COUNT];
2246 DWRITE_READING_DIRECTION direction;
2247 UINT8 explicit[BIDI_LEVELS_COUNT];
2248 UINT8 resolved[BIDI_LEVELS_COUNT];
2249 BOOL todo;
2252 static const struct bidi_test bidi_tests[] = {
2254 { 0x645, 0x6cc, 0x200c, 0x6a9, 0x646, 0x645, 0 },
2255 DWRITE_READING_DIRECTION_RIGHT_TO_LEFT,
2256 { 1, 1, 1, 1, 1, 1 },
2257 { 1, 1, 1, 1, 1, 1 }
2260 { 0x645, 0x6cc, 0x200c, 0x6a9, 0x646, 0x645, 0 },
2261 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2262 { 0, 0, 0, 0, 0, 0 },
2263 { 1, 1, 1, 1, 1, 1 }
2266 { 0x200c, 0x645, 0x6cc, 0x6a9, 0x646, 0x645, 0 },
2267 DWRITE_READING_DIRECTION_RIGHT_TO_LEFT,
2268 { 1, 1, 1, 1, 1, 1 },
2269 { 1, 1, 1, 1, 1, 1 }
2272 { 0x200c, 0x645, 0x6cc, 0x6a9, 0x646, 0x645, 0 },
2273 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2274 { 0, 0, 0, 0, 0, 0 },
2275 { 0, 1, 1, 1, 1, 1 }
2278 { 'A', 0x200c, 'B', 0 },
2279 DWRITE_READING_DIRECTION_RIGHT_TO_LEFT,
2280 { 1, 1, 1 },
2281 { 2, 2, 2 }
2284 { 'A', 0x200c, 'B', 0 },
2285 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2286 { 0, 0, 0 },
2287 { 0, 0, 0 }
2290 { LRE, PDF, 'a', 'b', 0 },
2291 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2292 { 2, 2, 0, 0 },
2293 { 0, 0, 0, 0 },
2296 { 'a', LRE, PDF, 'b', 0 },
2297 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2298 { 0, 2, 2, 0 },
2299 { 0, 0, 0, 0 },
2302 { RLE, PDF, 'a', 'b', 0 },
2303 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2304 { 1, 1, 0, 0 },
2305 { 0, 0, 0, 0 },
2308 { 'a', RLE, PDF, 'b', 0 },
2309 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2310 { 0, 1, 1, 0 },
2311 { 0, 0, 0, 0 },
2314 { 'a', RLE, PDF, 'b', 0 },
2315 DWRITE_READING_DIRECTION_RIGHT_TO_LEFT,
2316 { 1, 3, 3, 1 },
2317 { 2, 2, 2, 2 },
2320 { LRE, PDF, 'a', 'b', 0 },
2321 DWRITE_READING_DIRECTION_RIGHT_TO_LEFT,
2322 { 2, 2, 1, 1 },
2323 { 1, 1, 2, 2 },
2326 { PDF, 'a', 'b', 0 },
2327 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2328 { 0, 0, 0, 0 },
2329 { 0, 0, 0, 0 }
2332 { LRE, 'a', 'b', PDF, 0 },
2333 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2334 { 2, 2, 2, 2 },
2335 { 0, 2, 2, 2 },
2336 TRUE
2339 { LRI, 'a', 'b', PDI, 0 },
2340 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2341 { 0, 0, 0, 0 },
2342 { 0, 0, 0, 0 },
2343 TRUE
2346 { RLI, 'a', 'b', PDI, 0 },
2347 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2348 { 0, 0, 0, 0 },
2349 { 0, 0, 0, 0 },
2350 TRUE
2353 { 0 }
2357 static void compare_bidi_levels(unsigned int seq, const struct bidi_test *test, UINT32 len, UINT8 *explicit, UINT8 *resolved)
2359 unsigned int i, failcount = 0;
2360 BOOL match;
2362 match = !memcmp(explicit, test->explicit, len);
2363 if (!match) {
2364 if (test->todo) {
2365 failcount++;
2366 todo_wine
2367 ok(0, "test %u: %s wrong explicit levels:\n", seq, wine_dbgstr_w(test->text));
2369 else
2370 ok(0, "test %u: %s wrong explicit levels:\n", seq, wine_dbgstr_w(test->text));
2372 for (i = 0; i < len; i++) {
2373 if (test->explicit[i] != explicit[i]) {
2374 if (test->todo) {
2375 failcount++;
2376 todo_wine
2377 ok(0, "\tat %u, explicit level %u, expected %u\n", i, explicit[i], test->explicit[i]);
2379 else
2380 ok(0, "\tat %u, explicit level %u, expected %u\n", i, explicit[i], test->explicit[i]);
2385 match = !memcmp(resolved, test->resolved, len);
2386 if (!match) {
2387 if (test->todo) {
2388 failcount++;
2389 todo_wine
2390 ok(0, "test %u: %s wrong resolved levels:\n", seq, wine_dbgstr_w(test->text));
2392 else
2393 ok(0, "test %u: %s wrong resolved levels:\n", seq, wine_dbgstr_w(test->text));
2395 for (i = 0; i < len; i++) {
2396 if (test->resolved[i] != resolved[i]) {
2397 if (test->todo) {
2398 failcount++;
2399 todo_wine
2400 ok(0, "\tat %u, resolved level %u, expected %u\n", i, resolved[i], test->resolved[i]);
2402 else
2403 ok(0, "\tat %u, resolved level %u, expected %u\n", i, resolved[i], test->resolved[i]);
2408 todo_wine_if(test->todo && failcount == 0)
2409 ok(1, "test %u: marked as \"todo_wine\" but succeeds\n", seq);
2412 static void test_AnalyzeBidi(void)
2414 const struct bidi_test *ptr = bidi_tests;
2415 IDWriteTextAnalyzer *analyzer;
2416 UINT32 i = 0;
2417 HRESULT hr;
2419 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
2420 ok(hr == S_OK, "got 0x%08x\n", hr);
2422 while (*ptr->text)
2424 UINT32 len;
2426 init_textsource(&analysissource, ptr->text, ptr->direction);
2428 len = lstrlenW(ptr->text);
2429 if (len > BIDI_LEVELS_COUNT) {
2430 ok(0, "test %u: increase BIDI_LEVELS_COUNT to at least %u\n", i, len);
2431 i++;
2432 ptr++;
2433 continue;
2436 memset(g_explicit_levels, 0, sizeof(g_explicit_levels));
2437 memset(g_resolved_levels, 0, sizeof(g_resolved_levels));
2438 hr = IDWriteTextAnalyzer_AnalyzeBidi(analyzer, &analysissource.IDWriteTextAnalysisSource_iface, 0,
2439 len, &analysissink);
2440 ok(hr == S_OK, "%u: got 0x%08x\n", i, hr);
2441 compare_bidi_levels(i, ptr, len, g_explicit_levels, g_resolved_levels);
2443 i++;
2444 ptr++;
2447 IDWriteTextAnalyzer_Release(analyzer);
2450 START_TEST(analyzer)
2452 HRESULT hr;
2454 hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_ISOLATED, &IID_IDWriteFactory, (IUnknown**)&factory);
2455 ok(hr == S_OK, "got 0x%08x\n", hr);
2456 if (hr != S_OK)
2458 win_skip("failed to create factory\n");
2459 return;
2462 init_call_sequences(sequences, NUM_CALL_SEQUENCES);
2463 init_call_sequences(expected_seq, 1);
2465 test_AnalyzeScript();
2466 test_AnalyzeLineBreakpoints();
2467 test_AnalyzeBidi();
2468 test_GetScriptProperties();
2469 test_GetTextComplexity();
2470 test_GetGlyphs();
2471 test_numbersubstitution();
2472 test_GetTypographicFeatures();
2473 test_GetGlyphPlacements();
2474 test_ApplyCharacterSpacing();
2475 test_GetGlyphOrientationTransform();
2476 test_GetBaseline();
2477 test_GetGdiCompatibleGlyphPlacements();
2479 IDWriteFactory_Release(factory);