msvcrt: Use EnumSystemLocalesEx instead of directly accessing kernel32 resources.
[wine.git] / dlls / dwrite / tests / analyzer.c
bloba712e6776243fb1d2f20568c78d363889247e0d5
1 /*
2 * Text analyzing tests
4 * Copyright 2012-2020 Nikolay Sivov for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #define COBJMACROS
23 #include <assert.h>
24 #include <stdio.h>
25 #include <limits.h>
26 #include <math.h>
28 #include "initguid.h"
29 #include "windows.h"
30 #include "winternl.h"
31 #include "dwrite_3.h"
33 #include "wine/test.h"
35 static IDWriteFactory *factory;
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 #define MS_GDEF_TAG DWRITE_MAKE_OPENTYPE_TAG('G','D','E','F')
49 #ifdef WORDS_BIGENDIAN
50 #define GET_BE_WORD(x) (x)
51 #define GET_BE_DWORD(x) (x)
52 #define GET_LE_WORD(x) RtlUshortByteSwap(x)
53 #define GET_LE_DWORD(x) RtlUlongByteSwap(x)
54 #else
55 #define GET_BE_WORD(x) RtlUshortByteSwap(x)
56 #define GET_BE_DWORD(x) RtlUlongByteSwap(x)
57 #define GET_LE_WORD(x) (x)
58 #define GET_LE_DWORD(x) (x)
59 #endif
61 struct ot_gdef_classdef_format1
63 WORD format;
64 WORD start_glyph;
65 WORD glyph_count;
66 WORD classes[1];
69 struct ot_gdef_class_range
71 WORD start_glyph;
72 WORD end_glyph;
73 WORD glyph_class;
76 struct ot_gdef_classdef_format2
78 WORD format;
79 WORD range_count;
80 struct ot_gdef_class_range ranges[1];
83 enum analysis_kind {
84 ScriptAnalysis,
85 LastKind
88 static const char *get_analysis_kind_name(enum analysis_kind kind)
90 switch (kind)
92 case ScriptAnalysis:
93 return "ScriptAnalysis";
94 default:
95 return "unknown";
99 struct script_analysis {
100 UINT32 pos;
101 UINT32 len;
102 DWRITE_SCRIPT_SHAPES shapes;
105 struct call_entry {
106 enum analysis_kind kind;
107 struct script_analysis sa;
110 struct testcontext {
111 enum analysis_kind kind;
112 BOOL todo;
113 int *failcount;
114 const char *file;
115 int line;
118 struct call_sequence
120 int count;
121 int size;
122 struct call_entry *sequence;
125 #define NUM_CALL_SEQUENCES 1
126 #define ANALYZER_ID 0
127 static struct call_sequence *sequences[NUM_CALL_SEQUENCES];
128 static struct call_sequence *expected_seq[1];
130 static void add_call(struct call_sequence **seq, int sequence_index, const struct call_entry *call)
132 struct call_sequence *call_seq = seq[sequence_index];
134 if (!call_seq->sequence)
136 call_seq->size = 10;
137 call_seq->sequence = malloc(call_seq->size * sizeof(*call_seq->sequence));
140 if (call_seq->count == call_seq->size)
142 call_seq->size *= 2;
143 call_seq->sequence = realloc(call_seq->sequence, call_seq->size * sizeof(*call_seq->sequence));
146 assert(call_seq->sequence);
148 call_seq->sequence[call_seq->count++] = *call;
151 static inline void flush_sequence(struct call_sequence **seg, int sequence_index)
153 struct call_sequence *call_seq = seg[sequence_index];
155 free(call_seq->sequence);
156 call_seq->sequence = NULL;
157 call_seq->count = call_seq->size = 0;
160 static void init_call_sequences(struct call_sequence **seq, int n)
162 int i;
164 for (i = 0; i < n; i++)
165 seq[i] = calloc(1, sizeof(*seq[i]));
168 static void test_uint(UINT32 actual, UINT32 expected, const char *name, const struct testcontext *ctxt)
170 if (expected != actual && ctxt->todo)
172 (*ctxt->failcount)++;
173 ok_(ctxt->file, ctxt->line) (0, "%s: \"%s\" expecting %u, got %u\n", get_analysis_kind_name(ctxt->kind), name, expected, actual);
175 else
176 ok_(ctxt->file, ctxt->line) (expected == actual, "%s: \"%s\" expecting %u, got %u\n", get_analysis_kind_name(ctxt->kind), name,
177 expected, actual);
180 static void ok_sequence_(struct call_sequence **seq, int sequence_index,
181 const struct call_entry *expected, const char *context, BOOL todo,
182 const char *file, int line)
184 struct call_sequence *call_seq = seq[sequence_index];
185 static const struct call_entry end_of_sequence = { LastKind };
186 const struct call_entry *actual, *sequence;
187 int failcount = 0;
188 struct testcontext ctxt;
190 add_call(seq, sequence_index, &end_of_sequence);
192 sequence = call_seq->sequence;
193 actual = sequence;
195 ctxt.failcount = &failcount;
196 ctxt.todo = todo;
197 ctxt.file = file;
198 ctxt.line = line;
200 while (expected->kind != LastKind && actual->kind != LastKind)
202 if (expected->kind == actual->kind)
204 ctxt.kind = expected->kind;
206 switch (actual->kind)
208 case ScriptAnalysis:
209 test_uint(actual->sa.pos, expected->sa.pos, "position", &ctxt);
210 test_uint(actual->sa.len, expected->sa.len, "length", &ctxt);
211 test_uint(actual->sa.shapes, expected->sa.shapes, "shapes", &ctxt);
212 break;
213 default:
214 ok(0, "%s: callback not handled, %s\n", context, get_analysis_kind_name(actual->kind));
216 expected++;
217 actual++;
219 else if (todo)
221 failcount++;
222 todo_wine
224 ok_(file, line) (0, "%s: call %s was expected, but got call %s instead\n",
225 context, get_analysis_kind_name(expected->kind), get_analysis_kind_name(actual->kind));
228 flush_sequence(seq, sequence_index);
229 return;
231 else
233 ok_(file, line) (0, "%s: call %s was expected, but got call %s instead\n",
234 context, get_analysis_kind_name(expected->kind), get_analysis_kind_name(actual->kind));
235 expected++;
236 actual++;
240 if (todo)
242 todo_wine
244 if (expected->kind != LastKind || actual->kind != LastKind)
246 failcount++;
247 ok_(file, line) (0, "%s: the call sequence is not complete: expected %s - actual %s\n",
248 context, get_analysis_kind_name(expected->kind), get_analysis_kind_name(actual->kind));
252 else if (expected->kind != LastKind || actual->kind != LastKind)
254 ok_(file, line) (0, "%s: the call sequence is not complete: expected %s - actual %s\n",
255 context, get_analysis_kind_name(expected->kind), get_analysis_kind_name(actual->kind));
258 if (todo && !failcount) /* succeeded yet marked todo */
260 todo_wine
262 ok_(file, line)(1, "%s: marked \"todo_wine\" but succeeds\n", context);
266 flush_sequence(seq, sequence_index);
269 #define ok_sequence(seq, index, exp, contx, todo) \
270 ok_sequence_(seq, index, (exp), (contx), (todo), __FILE__, __LINE__)
272 static HRESULT WINAPI analysissink_QueryInterface(IDWriteTextAnalysisSink *iface, REFIID riid, void **obj)
274 if (IsEqualIID(riid, &IID_IDWriteTextAnalysisSink) || IsEqualIID(riid, &IID_IUnknown))
276 *obj = iface;
277 return S_OK;
280 *obj = NULL;
281 return E_NOINTERFACE;
284 static ULONG WINAPI analysissink_AddRef(IDWriteTextAnalysisSink *iface)
286 return 2;
289 static ULONG WINAPI analysissink_Release(IDWriteTextAnalysisSink *iface)
291 return 1;
294 static HRESULT WINAPI analysissink_SetScriptAnalysis(IDWriteTextAnalysisSink *iface,
295 UINT32 position, UINT32 length, DWRITE_SCRIPT_ANALYSIS const* sa)
297 struct call_entry entry;
298 entry.kind = ScriptAnalysis;
299 entry.sa.pos = position;
300 entry.sa.len = length;
301 entry.sa.shapes = sa->shapes;
302 add_call(sequences, ANALYZER_ID, &entry);
303 return S_OK;
306 static DWRITE_SCRIPT_ANALYSIS g_sa;
307 static HRESULT WINAPI analysissink_SetScriptAnalysis2(IDWriteTextAnalysisSink *iface,
308 UINT32 position, UINT32 length, DWRITE_SCRIPT_ANALYSIS const* sa)
310 g_sa = *sa;
311 return S_OK;
314 #define BREAKPOINT_COUNT 20
315 static DWRITE_LINE_BREAKPOINT g_actual_bp[BREAKPOINT_COUNT];
317 static HRESULT WINAPI analysissink_SetLineBreakpoints(IDWriteTextAnalysisSink *iface,
318 UINT32 position,
319 UINT32 length,
320 DWRITE_LINE_BREAKPOINT const* breakpoints)
322 if (position + length > BREAKPOINT_COUNT) {
323 ok(0, "SetLineBreakpoints: reported pos=%u, len=%u overflows expected length %d\n", position, length, BREAKPOINT_COUNT);
324 return E_FAIL;
326 memcpy(&g_actual_bp[position], breakpoints, length*sizeof(DWRITE_LINE_BREAKPOINT));
327 return S_OK;
330 #define BIDI_LEVELS_COUNT 10
331 static UINT8 g_explicit_levels[BIDI_LEVELS_COUNT];
332 static UINT8 g_resolved_levels[BIDI_LEVELS_COUNT];
333 static HRESULT WINAPI analysissink_SetBidiLevel(IDWriteTextAnalysisSink *iface,
334 UINT32 position,
335 UINT32 length,
336 UINT8 explicitLevel,
337 UINT8 resolvedLevel)
339 if (position + length > BIDI_LEVELS_COUNT) {
340 ok(0, "SetBidiLevel: reported pos=%u, len=%u overflows expected length %d\n", position, length, BIDI_LEVELS_COUNT);
341 return E_FAIL;
343 memset(g_explicit_levels + position, explicitLevel, length);
344 memset(g_resolved_levels + position, resolvedLevel, length);
345 return S_OK;
348 static HRESULT WINAPI analysissink_SetNumberSubstitution(IDWriteTextAnalysisSink *iface,
349 UINT32 position,
350 UINT32 length,
351 IDWriteNumberSubstitution* substitution)
353 ok(0, "unexpected\n");
354 return E_NOTIMPL;
357 static IDWriteTextAnalysisSinkVtbl analysissinkvtbl = {
358 analysissink_QueryInterface,
359 analysissink_AddRef,
360 analysissink_Release,
361 analysissink_SetScriptAnalysis,
362 analysissink_SetLineBreakpoints,
363 analysissink_SetBidiLevel,
364 analysissink_SetNumberSubstitution
367 static IDWriteTextAnalysisSinkVtbl analysissinkvtbl2 = {
368 analysissink_QueryInterface,
369 analysissink_AddRef,
370 analysissink_Release,
371 analysissink_SetScriptAnalysis2,
372 analysissink_SetLineBreakpoints,
373 analysissink_SetBidiLevel,
374 analysissink_SetNumberSubstitution
377 static IDWriteTextAnalysisSink analysissink = { &analysissinkvtbl };
378 static IDWriteTextAnalysisSink analysissink2 = { &analysissinkvtbl2 };
380 static HRESULT WINAPI analysissource_QueryInterface(IDWriteTextAnalysisSource *iface,
381 REFIID riid, void **obj)
383 ok(0, "QueryInterface not expected\n");
384 return E_NOTIMPL;
387 static ULONG WINAPI analysissource_AddRef(IDWriteTextAnalysisSource *iface)
389 ok(0, "AddRef not expected\n");
390 return 2;
393 static ULONG WINAPI analysissource_Release(IDWriteTextAnalysisSource *iface)
395 ok(0, "Release not expected\n");
396 return 1;
399 struct testanalysissource
401 IDWriteTextAnalysisSource IDWriteTextAnalysisSource_iface;
402 const WCHAR *text;
403 UINT32 text_length;
404 DWRITE_READING_DIRECTION direction;
407 static void init_textsource(struct testanalysissource *source, const WCHAR *text,
408 DWRITE_READING_DIRECTION direction)
410 source->text = text;
411 source->text_length = lstrlenW(text);
412 source->direction = direction;
415 static inline struct testanalysissource *impl_from_IDWriteTextAnalysisSource(IDWriteTextAnalysisSource *iface)
417 return CONTAINING_RECORD(iface, struct testanalysissource, IDWriteTextAnalysisSource_iface);
420 static HRESULT WINAPI analysissource_GetTextAtPosition(IDWriteTextAnalysisSource *iface,
421 UINT32 position, WCHAR const** text, UINT32* text_len)
423 struct testanalysissource *source = impl_from_IDWriteTextAnalysisSource(iface);
425 if (position >= source->text_length)
427 *text = NULL;
428 *text_len = 0;
430 else
432 *text = source->text + position;
433 *text_len = source->text_length - position;
436 return S_OK;
439 static HRESULT WINAPI analysissource_GetTextBeforePosition(IDWriteTextAnalysisSource *iface,
440 UINT32 position, WCHAR const** text, UINT32* text_len)
442 ok(0, "unexpected\n");
443 return E_NOTIMPL;
446 static DWRITE_READING_DIRECTION WINAPI analysissource_GetParagraphReadingDirection(
447 IDWriteTextAnalysisSource *iface)
449 struct testanalysissource *source = impl_from_IDWriteTextAnalysisSource(iface);
450 return source->direction;
453 static HRESULT WINAPI analysissource_GetLocaleName(IDWriteTextAnalysisSource *iface,
454 UINT32 position, UINT32* text_len, WCHAR const** locale)
456 *locale = NULL;
457 *text_len = 0;
458 return S_OK;
461 static HRESULT WINAPI analysissource_GetNumberSubstitution(IDWriteTextAnalysisSource *iface,
462 UINT32 position, UINT32* text_len, IDWriteNumberSubstitution **substitution)
464 ok(0, "unexpected\n");
465 return E_NOTIMPL;
468 static IDWriteTextAnalysisSourceVtbl analysissourcevtbl = {
469 analysissource_QueryInterface,
470 analysissource_AddRef,
471 analysissource_Release,
472 analysissource_GetTextAtPosition,
473 analysissource_GetTextBeforePosition,
474 analysissource_GetParagraphReadingDirection,
475 analysissource_GetLocaleName,
476 analysissource_GetNumberSubstitution
479 static struct testanalysissource analysissource = { { &analysissourcevtbl } };
481 static IDWriteFontFace *create_fontface(void)
483 IDWriteGdiInterop *interop;
484 IDWriteFontFace *fontface;
485 IDWriteFont *font;
486 LOGFONTW logfont;
487 HRESULT hr;
489 hr = IDWriteFactory_GetGdiInterop(factory, &interop);
490 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
492 memset(&logfont, 0, sizeof(logfont));
493 logfont.lfHeight = 12;
494 logfont.lfWidth = 12;
495 logfont.lfWeight = FW_NORMAL;
496 logfont.lfItalic = 1;
497 lstrcpyW(logfont.lfFaceName, L"Tahoma");
499 hr = IDWriteGdiInterop_CreateFontFromLOGFONT(interop, &logfont, &font);
500 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
502 hr = IDWriteFont_CreateFontFace(font, &fontface);
503 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
505 IDWriteFont_Release(font);
506 IDWriteGdiInterop_Release(interop);
508 return fontface;
511 static WCHAR *create_testfontfile(const WCHAR *filename)
513 static WCHAR pathW[MAX_PATH];
514 DWORD written;
515 HANDLE file;
516 HRSRC res;
517 void *ptr;
519 GetTempPathW(ARRAY_SIZE(pathW), pathW);
520 lstrcatW(pathW, filename);
522 file = CreateFileW(pathW, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0);
523 ok(file != INVALID_HANDLE_VALUE, "file creation failed, at %s, error %ld\n", wine_dbgstr_w(pathW),
524 GetLastError());
526 res = FindResourceA(GetModuleHandleA(NULL), (LPCSTR)MAKEINTRESOURCE(1), (LPCSTR)RT_RCDATA);
527 ok(res != 0, "couldn't find resource\n");
528 ptr = LockResource(LoadResource(GetModuleHandleA(NULL), res));
529 WriteFile(file, ptr, SizeofResource(GetModuleHandleA(NULL), res), &written, NULL);
530 ok(written == SizeofResource(GetModuleHandleA(NULL), res), "couldn't write resource\n");
531 CloseHandle(file);
533 return pathW;
536 #define DELETE_FONTFILE(filename) _delete_testfontfile(filename, __LINE__)
537 static void _delete_testfontfile(const WCHAR *filename, int line)
539 BOOL ret = DeleteFileW(filename);
540 ok_(__FILE__,line)(ret, "failed to delete file %s, error %ld\n", wine_dbgstr_w(filename), GetLastError());
543 static IDWriteFontFace *create_testfontface(const WCHAR *filename)
545 IDWriteFontFace *face;
546 IDWriteFontFile *file;
547 HRESULT hr;
549 hr = IDWriteFactory_CreateFontFileReference(factory, filename, NULL, &file);
550 ok(hr == S_OK, "Unexpected hr %#lx.\n",hr);
552 hr = IDWriteFactory_CreateFontFace(factory, DWRITE_FONT_FACE_TYPE_TRUETYPE, 1, &file, 0,
553 DWRITE_FONT_SIMULATIONS_NONE, &face);
554 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
555 IDWriteFontFile_Release(file);
557 return face;
560 struct sa_test {
561 const WCHAR string[50];
562 int item_count;
563 struct script_analysis sa[10];
566 static struct sa_test sa_tests[] = {
568 /* just 1 char string */
569 {'t',0}, 1,
570 { { 0, 1, DWRITE_SCRIPT_SHAPES_DEFAULT }}
573 {'t','e','s','t',0}, 1,
574 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT }}
577 {' ',' ',' ',' ','!','$','[','^','{','~',0}, 1,
578 { { 0, 10, DWRITE_SCRIPT_SHAPES_DEFAULT }}
581 {' ',' ',' ','1','2',' ',0}, 1,
582 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
585 /* digits only */
586 {'1','2',0}, 1,
587 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }}
590 /* Arabic */
591 {0x064a,0x064f,0x0633,0x0627,0x0648,0x0650,0x064a,0x0661,0}, 1,
592 { { 0, 8, DWRITE_SCRIPT_SHAPES_DEFAULT }}
595 /* Arabic */
596 {0x0627,0x0644,0x0635,0x0651,0x0650,0x062d,0x0629,0x064f,' ',0x062a,0x064e,
597 0x0627,0x062c,0x064c,' ',0x0639,0x064e,0x0644,0x0649,' ',
598 0x0631,0x064f,0x0624,0x0648,0x0633,0x0650,' ',0x0627,0x0644,
599 0x0623,0x0635,0x0650,0x062d,0x0651,0x064e,0x0627,0x0621,0x0650,0x06f0,0x06f5,0}, 1,
600 { { 0, 40, DWRITE_SCRIPT_SHAPES_DEFAULT }}
603 /* Arabic, Latin */
604 {'1','2','3','-','5','2',0x064a,0x064f,0x0633,0x0627,0x0648,0x0650,0x064a,'7','1','.',0}, 1,
605 { { 0, 16, DWRITE_SCRIPT_SHAPES_DEFAULT }}
608 /* Arabic, English */
609 {'A','B','C','-','D','E','F',' ',0x0621,0x0623,0x0624,0}, 2,
610 { { 0, 8, DWRITE_SCRIPT_SHAPES_DEFAULT },
611 { 8, 3, DWRITE_SCRIPT_SHAPES_DEFAULT },
615 /* leading space, Arabic, English */
616 {' ',0x0621,0x0623,0x0624,'A','B','C','-','D','E','F',0}, 2,
617 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT },
618 { 4, 7, DWRITE_SCRIPT_SHAPES_DEFAULT },
622 /* English, Arabic, trailing space */
623 {'A','B','C','-','D','E','F',0x0621,0x0623,0x0624,' ',0}, 2,
624 { { 0, 7, DWRITE_SCRIPT_SHAPES_DEFAULT },
625 { 7, 4, DWRITE_SCRIPT_SHAPES_DEFAULT },
629 /* C1 Controls, Latin-1 Supplement */
630 {0x80,0x90,0x9f,0xa0,0xc0,0xb8,0xbf,0xc0,0xff,0}, 2,
631 { { 0, 3, DWRITE_SCRIPT_SHAPES_NO_VISUAL },
632 { 3, 6, DWRITE_SCRIPT_SHAPES_DEFAULT },
636 /* Latin Extended-A */
637 {0x100,0x120,0x130,0x140,0x150,0x160,0x170,0x17f,0}, 1,
638 { { 0, 8, DWRITE_SCRIPT_SHAPES_DEFAULT }}
641 /* Latin Extended-B */
642 {0x180,0x190,0x1bf,0x1c0,0x1c3,0x1c4,0x1cc,0x1dc,0x1ff,0x217,0x21b,0x24f,0}, 1,
643 { { 0, 12, DWRITE_SCRIPT_SHAPES_DEFAULT }}
646 /* IPA Extensions */
647 {0x250,0x260,0x270,0x290,0x2af,0}, 1,
648 { { 0, 5, DWRITE_SCRIPT_SHAPES_DEFAULT }}
651 /* Spacing Modifier Letters */
652 {0x2b0,0x2ba,0x2d7,0x2dd,0x2ef,0x2ff,0}, 1,
653 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
656 /* Combining Diacritical Marks */
657 {0x300,0x320,0x340,0x345,0x350,0x36f,0}, 1,
658 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
661 /* Greek and Coptic */
662 {0x370,0x388,0x3d8,0x3e1,0x3e2,0x3fa,0x3ff,0}, 3,
663 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT },
664 { 4, 1, DWRITE_SCRIPT_SHAPES_DEFAULT },
665 { 5, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }
669 /* Cyrillic and Cyrillic Supplement */
670 {0x400,0x40f,0x410,0x44f,0x450,0x45f,0x460,0x481,0x48a,0x4f0,0x4fa,0x4ff,0x500,0x510,0x520,0}, 1,
671 { { 0, 15, DWRITE_SCRIPT_SHAPES_DEFAULT }}
674 /* Armenian */
675 {0x531,0x540,0x559,0x55f,0x570,0x589,0x58a,0}, 1,
676 { { 0, 7, DWRITE_SCRIPT_SHAPES_DEFAULT }}
679 /* Hebrew */
680 {0x5e9,0x5dc,0x5d5,0x5dd,0}, 1,
681 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT }}
684 /* Latin, Hebrew, Latin */
685 {'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,
686 { { 0, 9, DWRITE_SCRIPT_SHAPES_DEFAULT },
687 { 9, 10, DWRITE_SCRIPT_SHAPES_DEFAULT },
688 { 19, 10, DWRITE_SCRIPT_SHAPES_DEFAULT }}
691 /* Syriac */
692 {0x710,0x712,0x712,0x714,'.',0}, 1,
693 { { 0, 5, DWRITE_SCRIPT_SHAPES_DEFAULT }}
696 /* Arabic Supplement */
697 {0x750,0x760,0x76d,'.',0}, 1,
698 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT }}
701 /* Thaana */
702 {0x780,0x78e,0x798,0x7a6,0x7b0,'.',0}, 1,
703 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
706 /* N'Ko */
707 {0x7c0,0x7ca,0x7e8,0x7eb,0x7f6,'.',0}, 1,
708 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
711 /* Thaana */
712 {0x780,0x798,0x7a5,0x7a6,0x7b0,'.',0}, 1,
713 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
716 /* Devanagari */
717 {0x926,0x947,0x935,0x928,0x93e,0x917,0x930,0x940,'.',0}, 1,
718 { { 0, 9, DWRITE_SCRIPT_SHAPES_DEFAULT }}
721 /* Bengali */
722 {0x9ac,0x9be,0x982,0x9b2,0x9be,'.',0}, 1,
723 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
726 /* Gurmukhi */
727 {0xa17,0xa41,0xa30,0xa2e,0xa41,0xa16,0xa40,'.',0}, 1,
728 { { 0, 8, DWRITE_SCRIPT_SHAPES_DEFAULT }}
731 /* Gujarati */
732 {0xa97,0xac1,0xa9c,0xab0,0xabe,0xaa4,0xac0,'.',0}, 1,
733 { { 0, 8, DWRITE_SCRIPT_SHAPES_DEFAULT }}
736 /* Oriya */
737 {0xb13,0xb21,0xb3c,0xb3f,0xb06,'.',0}, 1,
738 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
741 /* Tamil */
742 {0xba4,0xbae,0xbbf,0xbb4,0xbcd,'.',0}, 1,
743 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
746 /* Telugu */
747 {0xc24,0xc46,0xc32,0xc41,0xc17,0xc41,'.',0}, 1,
748 { { 0, 7, DWRITE_SCRIPT_SHAPES_DEFAULT }}
751 /* Kannada */
752 {0xc95,0xca8,0xccd,0xca8,0xca1,'.',0}, 1,
753 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
756 /* Malayalam */
757 {0xd2e,0xd32,0xd2f,0xd3e,0xd33,0xd02,'.',0}, 1,
758 { { 0, 7, DWRITE_SCRIPT_SHAPES_DEFAULT }}
761 /* Sinhala */
762 {0xd82,0xd85,0xd9a,0xdcf,'.',0}, 1,
763 { { 0, 5, DWRITE_SCRIPT_SHAPES_DEFAULT }}
766 /* Thai */
767 {0x0e04,0x0e27,0x0e32,0x0e21,0x0e1e,0x0e22,0x0e32,0x0e22,0x0e32,0x0e21,
768 0x0e2d,0x0e22,0x0e39,0x0e48,0x0e17,0x0e35,0x0e48,0x0e44,0x0e2b,0x0e19,
769 0x0e04,0x0e27,0x0e32,0x0e21,0x0e2a, 0x0e33,0x0e40,0x0e23,0x0e47,0x0e08,
770 0x0e2d,0x0e22,0x0e39,0x0e48,0x0e17,0x0e35,0x0e48,0x0e19,0x0e31,0x0e48,0x0e19,'.',0}, 1,
771 { { 0, 42, DWRITE_SCRIPT_SHAPES_DEFAULT }}
774 /* Lao */
775 {0xead,0xeb1,0xe81,0xeaa,0xead,0xe99,0xea5,0xeb2,0xea7,'.',0}, 1,
776 { { 0, 10, DWRITE_SCRIPT_SHAPES_DEFAULT }}
779 /* Tibetan */
780 {0xf04,0xf05,0xf0e,0x020,0xf51,0xf7c,0xf53,0xf0b,0xf5a,0xf53,0xf0b,
781 0xf51,0xf44,0xf0b,0xf54,0xf7c,0xf0d,'.',0}, 1,
782 { { 0, 18, DWRITE_SCRIPT_SHAPES_DEFAULT }}
785 /* Myanmar */
786 {0x1019,0x103c,0x1014,0x103a,0x1019,0x102c,0x1021,0x1000,0x1039,0x1001,0x101b,0x102c,'.',0}, 1,
787 { { 0, 13, DWRITE_SCRIPT_SHAPES_DEFAULT }}
790 /* Georgian */
791 {0x10a0,0x10d0,0x10da,0x10f1,0x10fb,0x2d00,'.',0}, 1,
792 { { 0, 7, DWRITE_SCRIPT_SHAPES_DEFAULT }}
795 /* Hangul */
796 {0x1100,0x1110,0x1160,0x1170,0x11a8,'.',0}, 1,
797 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
800 /* Ethiopic */
801 {0x130d,0x12d5,0x12dd,0}, 1,
802 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
805 /* Cherokee */
806 {0x13e3,0x13b3,0x13a9,0x0020,0x13a6,0x13ec,0x13c2,0x13af,0x13cd,0x13d7,0}, 1,
807 { { 0, 10, DWRITE_SCRIPT_SHAPES_DEFAULT }}
810 /* Canadian */
811 {0x1403,0x14c4,0x1483,0x144e,0x1450,0x1466,0}, 1,
812 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
815 /* Ogham */
816 {0x169b,0x1691,0x168c,0x1690,0x168b,0x169c,0}, 1,
817 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
820 /* Runic */
821 {0x16a0,0x16a1,0x16a2,0x16a3,0x16a4,0x16a5,0}, 1,
822 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
825 /* Khmer */
826 {0x1781,0x17c1,0x1798,0x179a,0x1797,0x17b6,0x179f,0x17b6,0x19e0,0}, 1,
827 { { 0, 9, DWRITE_SCRIPT_SHAPES_DEFAULT }}
830 /* Mongolian */
831 {0x182e,0x1823,0x1829,0x182d,0x1823,0x182f,0x0020,0x182a,0x1822,0x1834,0x1822,0x182d,0x180c,0}, 1,
832 { { 0, 13, DWRITE_SCRIPT_SHAPES_DEFAULT }}
835 /* Limbu */
836 {0x1900,0x1910,0x1920,0x1930,0}, 1,
837 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT }}
840 /* Tai Le */
841 {0x1956,0x196d,0x1970,0x1956,0x196c,0x1973,0x1951,0x1968,0x1952,0x1970,0}, 1,
842 { { 0, 10, DWRITE_SCRIPT_SHAPES_DEFAULT }}
845 /* New Tai Lue */
846 {0x1992,0x19c4,0}, 1,
847 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }}
850 /* Buginese */
851 {0x1a00,0x1a10,0}, 1,
852 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }}
855 /* Tai Tham */
856 {0x1a20,0x1a40,0x1a50,0}, 1,
857 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
860 /* Balinese */
861 {0x1b00,0x1b05,0x1b20,0}, 1,
862 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
865 /* Sundanese */
866 {0x1b80,0x1b85,0x1ba0,0}, 1,
867 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
870 /* Batak */
871 {0x1bc0,0x1be5,0x1bfc,0}, 1,
872 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
875 /* Lepcha */
876 {0x1c00,0x1c20,0x1c40,0}, 1,
877 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
880 /* Ol Chiki */
881 {0x1c50,0x1c5a,0x1c77,0}, 1,
882 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
885 /* Sundanese Supplement */
886 {0x1cc0,0x1cc5,0x1cc7,0}, 1,
887 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
890 /* Phonetic Extensions */
891 {0x1d00,0x1d40,0x1d70,0}, 1,
892 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
895 /* Combining diacritical marks */
896 {0x1dc0,0x300,0x1ddf,0}, 1,
897 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
900 /* Latin Extended Additional, Extended-C */
901 {0x1e00,0x1d00,0x2c60,0}, 1,
902 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
905 /* Greek Extended */
906 {0x3f0,0x1f00,0}, 1,
907 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }}
910 /* General Punctuation */
911 {0x1dc0,0x2000,0}, 1,
912 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }}
915 /* Superscripts and Subscripts */
916 {0x2070,0x2086,0x2000,0}, 1,
917 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
920 /* Currency, Combining Diacritical Marks for Symbols. Letterlike Symbols.. */
921 {0x20a0,0x20b8,0x2000,0x20d0,0x2100,0x2150,0x2190,0x2200,0x2300,0x2400,0x2440,0x2460,0x2500,0x2580,0x25a0,0x2600,
922 0x2700,0x27c0,0x27f0,0x2900,0x2980,0x2a00,0x2b00,0}, 1,
923 { { 0, 23, DWRITE_SCRIPT_SHAPES_DEFAULT }}
926 /* Braille */
927 {0x2800,0x2070,0x2000,0}, 1,
928 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
931 /* Glagolitic */
932 {0x2c00,0x2c12,0}, 1,
933 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }}
936 /* Coptic */
937 {0x2c80,0x3e2,0x1f00,0}, 2,
938 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT },
939 { 2, 1, DWRITE_SCRIPT_SHAPES_DEFAULT } }
942 /* Tifinagh */
943 {0x2d30,0x2d4a,0}, 1,
944 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }}
947 /* LRE/PDF */
948 {LRE,PDF,'a','b','c','\r',0}, 3,
949 { { 0, 2, DWRITE_SCRIPT_SHAPES_NO_VISUAL },
950 { 2, 3, DWRITE_SCRIPT_SHAPES_DEFAULT },
951 { 5, 1, DWRITE_SCRIPT_SHAPES_NO_VISUAL } }
954 /* LRE/PDF and other visual and non-visual codes from Common script range */
955 {LRE,PDF,'r','!',0x200b,'\r',0}, 3,
956 { { 0, 2, DWRITE_SCRIPT_SHAPES_NO_VISUAL },
957 { 2, 2, DWRITE_SCRIPT_SHAPES_DEFAULT },
958 { 4, 2, DWRITE_SCRIPT_SHAPES_NO_VISUAL } }
961 /* Inherited on its own */
962 {0x300,0x300,0}, 1,
963 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT } }
966 /* Inherited followed by Latin */
967 {0x300,0x300,'a',0}, 1,
968 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT } }
971 /* Inherited mixed with Arabic and Latin */
972 {0x300,'+',0x627,0x300,'a',0}, 2,
973 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT },
974 { 4, 1, DWRITE_SCRIPT_SHAPES_DEFAULT } }
977 {'a',0x300,'+',0x627,0x300,')','a',0}, 3,
978 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT },
979 { 3, 3, DWRITE_SCRIPT_SHAPES_DEFAULT },
980 { 6, 1, DWRITE_SCRIPT_SHAPES_DEFAULT } }
982 /* Paired punctuation */
984 {0x627,'(','a',')','a',0}, 2,
985 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT },
986 { 2, 3, DWRITE_SCRIPT_SHAPES_DEFAULT } }
989 {0x627,'[','a',']',0x627,0}, 3,
990 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT },
991 { 2, 2, DWRITE_SCRIPT_SHAPES_DEFAULT },
992 { 4, 1, DWRITE_SCRIPT_SHAPES_DEFAULT } }
994 /* Combining marks */
996 /* dotted circle - Common, followed by accent - Inherited */
997 {0x25cc,0x300,0}, 1,
998 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT } }
1001 /* combining mark with explicit script value */
1002 {0x25cc,0x300,0x5c4,0}, 1,
1003 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT } }
1006 /* inherited merges with following explicit script */
1007 {0x25cc,0x300,'a',0}, 1,
1008 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT } }
1010 /* keep this as end test data marker */
1011 { {0} }
1014 static void init_expected_sa(struct call_sequence **seq, const struct sa_test *test)
1016 static const struct call_entry end_of_sequence = { LastKind };
1017 int i;
1019 flush_sequence(seq, ANALYZER_ID);
1021 /* add expected calls */
1022 for (i = 0; i < test->item_count; i++)
1024 struct call_entry call;
1026 call.kind = ScriptAnalysis;
1027 call.sa.pos = test->sa[i].pos;
1028 call.sa.len = test->sa[i].len;
1029 call.sa.shapes = test->sa[i].shapes;
1030 add_call(seq, 0, &call);
1033 /* and stop marker */
1034 add_call(seq, 0, &end_of_sequence);
1037 static void get_script_analysis(const WCHAR *str, DWRITE_SCRIPT_ANALYSIS *sa)
1039 IDWriteTextAnalyzer *analyzer;
1040 HRESULT hr;
1042 init_textsource(&analysissource, str, DWRITE_READING_DIRECTION_LEFT_TO_RIGHT);
1043 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
1044 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1046 hr = IDWriteTextAnalyzer_AnalyzeScript(analyzer, &analysissource.IDWriteTextAnalysisSource_iface, 0,
1047 lstrlenW(analysissource.text), &analysissink2);
1048 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1050 *sa = g_sa;
1053 static void test_AnalyzeScript(void)
1055 const struct sa_test *ptr = sa_tests;
1056 IDWriteTextAnalyzer *analyzer;
1057 HRESULT hr;
1059 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
1060 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1062 while (*ptr->string)
1064 init_textsource(&analysissource, ptr->string, DWRITE_READING_DIRECTION_LEFT_TO_RIGHT);
1066 init_expected_sa(expected_seq, ptr);
1067 hr = IDWriteTextAnalyzer_AnalyzeScript(analyzer, &analysissource.IDWriteTextAnalysisSource_iface, 0,
1068 lstrlenW(ptr->string), &analysissink);
1069 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1070 ok_sequence(sequences, ANALYZER_ID, expected_seq[0]->sequence, wine_dbgstr_w(ptr->string), FALSE);
1071 ptr++;
1074 IDWriteTextAnalyzer_Release(analyzer);
1077 struct linebreaks_test {
1078 const WCHAR text[BREAKPOINT_COUNT+1];
1079 DWRITE_LINE_BREAKPOINT bp[BREAKPOINT_COUNT];
1082 static const struct linebreaks_test linebreaks_tests[] =
1084 { {'A','-','B',' ','C',0x58a,'D',0x2010,'E',0x2012,'F',0x2013,'\t',0xc,0xb,0x2028,0x2029,0x200b,0},
1086 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1087 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 0, 0 },
1088 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1089 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 1, 0 },
1090 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1091 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 0, 0 },
1092 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1093 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 0, 0 },
1094 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1095 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 0, 0 },
1096 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1097 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1098 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 1, 0 },
1099 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_MUST_BREAK, 1, 0 },
1100 { DWRITE_BREAK_CONDITION_MUST_BREAK, DWRITE_BREAK_CONDITION_MUST_BREAK, 1, 0 },
1101 { DWRITE_BREAK_CONDITION_MUST_BREAK, DWRITE_BREAK_CONDITION_MUST_BREAK, 1, 0 },
1102 { DWRITE_BREAK_CONDITION_MUST_BREAK, DWRITE_BREAK_CONDITION_MUST_BREAK, 1, 0 },
1103 { DWRITE_BREAK_CONDITION_MUST_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 0, 0 },
1106 /* Soft hyphen, visible word dividers */
1107 { {'A',0xad,'B',0x5be,'C',0xf0b,'D',0x1361,'E',0x17d8,'F',0x17da,'G',0},
1109 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1110 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 0, 1 },
1111 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1112 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 0, 0 },
1113 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1114 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 0, 0 },
1115 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1116 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 0, 0 },
1117 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1118 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 0, 0 },
1119 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1120 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 0, 0 },
1121 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 0, 0 },
1124 /* LB30 changes in Unicode 13 regarding East Asian parentheses */
1125 { {0x5f35,'G',0x300c,0},
1127 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK },
1128 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK },
1129 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK },
1132 { { 0 } }
1135 static void compare_breakpoints(const struct linebreaks_test *test, DWRITE_LINE_BREAKPOINT *actual)
1137 static const char *conditions[] = {"N","CB","NB","B"};
1138 const WCHAR *text = test->text;
1139 int cmp = memcmp(test->bp, actual, sizeof(*actual)*BREAKPOINT_COUNT);
1140 ok(!cmp, "%s: got wrong breakpoint data\n", wine_dbgstr_w(test->text));
1141 if (cmp) {
1142 int i = 0;
1143 while (*text) {
1144 ok(!memcmp(&test->bp[i], &actual[i], sizeof(*actual)),
1145 "%s: got [%s, %s] (%s, %s), expected [%s, %s] (%s, %s)\n",
1146 wine_dbgstr_wn(&test->text[i], 1),
1147 conditions[g_actual_bp[i].breakConditionBefore],
1148 conditions[g_actual_bp[i].breakConditionAfter],
1149 g_actual_bp[i].isWhitespace ? "WS" : "0",
1150 g_actual_bp[i].isSoftHyphen ? "SHY" : "0",
1151 conditions[test->bp[i].breakConditionBefore],
1152 conditions[test->bp[i].breakConditionAfter],
1153 test->bp[i].isWhitespace ? "WS" : "0",
1154 test->bp[i].isSoftHyphen ? "SHY" : "0");
1155 if (g_actual_bp[i].isSoftHyphen)
1156 ok(!g_actual_bp[i].isWhitespace, "%s: soft hyphen marked as a whitespace\n",
1157 wine_dbgstr_wn(&test->text[i], 1));
1158 text++;
1159 i++;
1164 static void test_AnalyzeLineBreakpoints(void)
1166 const struct linebreaks_test *ptr = linebreaks_tests;
1167 IDWriteTextAnalyzer *analyzer;
1168 UINT32 i = 0;
1169 HRESULT hr;
1171 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
1172 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1174 init_textsource(&analysissource, L"", DWRITE_READING_DIRECTION_LEFT_TO_RIGHT);
1175 hr = IDWriteTextAnalyzer_AnalyzeLineBreakpoints(analyzer, &analysissource.IDWriteTextAnalysisSource_iface, 0, 0,
1176 &analysissink);
1177 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1179 while (*ptr->text)
1181 UINT32 len;
1183 init_textsource(&analysissource, ptr->text, DWRITE_READING_DIRECTION_LEFT_TO_RIGHT);
1185 len = lstrlenW(ptr->text);
1186 if (len > BREAKPOINT_COUNT) {
1187 ok(0, "test %u: increase BREAKPOINT_COUNT to at least %u\n", i, len);
1188 i++;
1189 ptr++;
1190 continue;
1193 memset(g_actual_bp, 0, sizeof(g_actual_bp));
1194 hr = IDWriteTextAnalyzer_AnalyzeLineBreakpoints(analyzer, &analysissource.IDWriteTextAnalysisSource_iface,
1195 0, len, &analysissink);
1196 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1197 compare_breakpoints(ptr, g_actual_bp);
1199 i++;
1200 ptr++;
1203 IDWriteTextAnalyzer_Release(analyzer);
1206 static void test_GetScriptProperties(void)
1208 IDWriteTextAnalyzer1 *analyzer1;
1209 IDWriteTextAnalyzer *analyzer;
1210 DWRITE_SCRIPT_ANALYSIS sa;
1211 DWRITE_SCRIPT_PROPERTIES props;
1212 HRESULT hr;
1214 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
1215 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1217 hr = IDWriteTextAnalyzer_QueryInterface(analyzer, &IID_IDWriteTextAnalyzer1, (void**)&analyzer1);
1218 IDWriteTextAnalyzer_Release(analyzer);
1219 if (hr != S_OK) {
1220 win_skip("GetScriptProperties() is not supported.\n");
1221 return;
1224 sa.script = 1000;
1225 hr = IDWriteTextAnalyzer1_GetScriptProperties(analyzer1, sa, &props);
1226 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
1228 if (0) /* crashes on native */
1229 hr = IDWriteTextAnalyzer1_GetScriptProperties(analyzer1, sa, NULL);
1231 sa.script = 0;
1232 hr = IDWriteTextAnalyzer1_GetScriptProperties(analyzer1, sa, &props);
1233 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1235 IDWriteTextAnalyzer1_Release(analyzer1);
1238 struct textcomplexity_test {
1239 const WCHAR text[5];
1240 UINT32 length;
1241 BOOL simple;
1242 UINT32 len_read;
1245 static const struct textcomplexity_test textcomplexity_tests[] = {
1246 { {0}, 1, FALSE, 1 },
1247 { {0}, 0, TRUE, 0 },
1248 { {0x610,0}, 0, TRUE, 0 },
1249 { {'A','B','C','D',0}, 3, TRUE, 3 },
1250 { {'A','B','C','D',0}, 5, TRUE, 4 },
1251 { {'A','B','C','D',0}, 10, TRUE, 4 },
1252 { {'A',0x610,'C','D',0}, 1, TRUE, 1 },
1253 { {'A',0x610,'C','D',0}, 2, FALSE, 2 },
1254 { {0x610,'A','C','D',0}, 1, FALSE, 1 },
1255 { {0x610,'A','C','D',0}, 2, FALSE, 1 },
1256 { {0x610,0x610,'C','D',0}, 2, FALSE, 2 },
1257 { {0xd800,'A','B',0}, 1, FALSE, 1 },
1258 { {0xd800,'A','B',0}, 2, FALSE, 1 },
1259 { {0xdc00,'A','B',0}, 2, FALSE, 1 },
1260 { {0x202a,'A',0x202c,0}, 3, FALSE, 1 },
1261 { {0x200e,'A',0}, 2, FALSE, 1 },
1262 { {0x200f,'A',0}, 2, FALSE, 1 },
1263 { {0x202d,'A',0}, 2, FALSE, 1 },
1264 { {0x202e,'A',0}, 2, FALSE, 1 },
1268 static void test_GetTextComplexity(void)
1270 IDWriteTextAnalyzer1 *analyzer1;
1271 IDWriteTextAnalyzer *analyzer;
1272 IDWriteFontFace *fontface;
1273 UINT16 indices[10];
1274 BOOL simple;
1275 HRESULT hr;
1276 UINT32 len;
1277 int i;
1279 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
1280 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1282 hr = IDWriteTextAnalyzer_QueryInterface(analyzer, &IID_IDWriteTextAnalyzer1, (void**)&analyzer1);
1283 IDWriteTextAnalyzer_Release(analyzer);
1284 if (hr != S_OK) {
1285 win_skip("GetTextComplexity() is not supported.\n");
1286 return;
1289 if (0) { /* crashes on native */
1290 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer1, NULL, 0, NULL, NULL, NULL, NULL);
1291 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer1, NULL, 0, NULL, NULL, &len, NULL);
1292 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer1, L"ABC", 3, NULL, NULL, NULL, NULL);
1293 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer1, L"ABC", 3, NULL, NULL, &len, NULL);
1294 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer1, L"ABC", 3, NULL, &simple, NULL, NULL);
1297 len = 1;
1298 simple = TRUE;
1299 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer1, NULL, 0, NULL, &simple, &len, NULL);
1300 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
1301 ok(len == 0, "got %d\n", len);
1302 ok(simple == FALSE, "got %d\n", simple);
1304 len = 1;
1305 simple = TRUE;
1306 indices[0] = 1;
1307 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer1, L"ABC", 3, NULL, &simple, &len, NULL);
1308 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
1309 ok(len == 0, "got %d\n", len);
1310 ok(simple == FALSE, "got %d\n", simple);
1311 ok(indices[0] == 1, "got %d\n", indices[0]);
1313 fontface = create_fontface();
1315 for (i = 0; i < ARRAY_SIZE(textcomplexity_tests); i++) {
1316 const struct textcomplexity_test *ptr = &textcomplexity_tests[i];
1317 len = 1;
1318 simple = !ptr->simple;
1319 indices[0] = 0;
1320 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer1, ptr->text, ptr->length, fontface, &simple, &len, indices);
1321 ok(hr == S_OK, "%d: Unexpected hr %#lx.\n", i, hr);
1322 ok(len == ptr->len_read, "%d: read length: got %d, expected %d\n", i, len, ptr->len_read);
1323 ok(simple == ptr->simple, "%d: simple: got %d, expected %d\n", i, simple, ptr->simple);
1324 if (simple && ptr->length)
1325 ok(indices[0] > 0, "%d: got %d\n", i, indices[0]);
1326 else
1327 ok(indices[0] == 0, "%d: got %d\n", i, indices[0]);
1330 IDWriteFontFace_Release(fontface);
1331 IDWriteTextAnalyzer1_Release(analyzer1);
1334 static void test_numbersubstitution(void)
1336 IDWriteNumberSubstitution *substitution;
1337 HRESULT hr;
1339 /* locale is not specified, method does not require it */
1340 hr = IDWriteFactory_CreateNumberSubstitution(factory, DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE, NULL, FALSE, &substitution);
1341 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1342 IDWriteNumberSubstitution_Release(substitution);
1344 /* invalid locale name, method does not require it */
1345 hr = IDWriteFactory_CreateNumberSubstitution(factory, DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE, L"dummy",
1346 FALSE, &substitution);
1347 ok(hr == S_OK, "Failed to create number substitution, hr %#lx.\n", hr);
1348 IDWriteNumberSubstitution_Release(substitution);
1350 /* invalid method */
1351 hr = IDWriteFactory_CreateNumberSubstitution(factory, DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL+1, NULL, FALSE, &substitution);
1352 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
1354 /* invalid method */
1355 hr = IDWriteFactory_CreateNumberSubstitution(factory, -1, NULL, FALSE, &substitution);
1356 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
1358 /* invalid locale */
1359 hr = IDWriteFactory_CreateNumberSubstitution(factory, DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL, NULL, FALSE, &substitution);
1360 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
1362 hr = IDWriteFactory_CreateNumberSubstitution(factory, DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL, L"dummy",
1363 FALSE, &substitution);
1364 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
1366 hr = IDWriteFactory_CreateNumberSubstitution(factory, DWRITE_NUMBER_SUBSTITUTION_METHOD_CONTEXTUAL, L"dummy",
1367 FALSE, &substitution);
1368 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
1370 hr = IDWriteFactory_CreateNumberSubstitution(factory, DWRITE_NUMBER_SUBSTITUTION_METHOD_NATIONAL, L"dummy",
1371 FALSE, &substitution);
1372 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
1374 /* invalid locale, but it's not needed for this method */
1375 hr = IDWriteFactory_CreateNumberSubstitution(factory, DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE, L"dummy", FALSE,
1376 &substitution);
1377 ok(hr == S_OK, "Failed to create number substitution, hr %#lx.\n", hr);
1378 IDWriteNumberSubstitution_Release(substitution);
1381 static void get_fontface_glyphs(IDWriteFontFace *fontface, const WCHAR *str, UINT16 *glyphs)
1383 while (*str) {
1384 UINT32 codepoint = *str;
1385 HRESULT hr;
1387 hr = IDWriteFontFace_GetGlyphIndices(fontface, &codepoint, 1, glyphs++);
1388 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1389 str++;
1393 static void get_fontface_advances(IDWriteFontFace *fontface, FLOAT emsize, const UINT16 *glyphs, FLOAT *advances, UINT32 count)
1395 DWRITE_FONT_METRICS fontmetrics;
1396 UINT32 i;
1398 IDWriteFontFace_GetMetrics(fontface, &fontmetrics);
1399 for (i = 0; i < count; i++) {
1400 DWRITE_GLYPH_METRICS metrics;
1401 HRESULT hr;
1403 hr = IDWriteFontFace_GetDesignGlyphMetrics(fontface, glyphs + i, 1, &metrics, FALSE);
1404 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1406 advances[i] = (FLOAT)metrics.advanceWidth * emsize / (FLOAT)fontmetrics.designUnitsPerEm;
1410 enum ot_gdef_class
1412 GDEF_CLASS_UNCLASSIFIED = 0,
1413 GDEF_CLASS_BASE = 1,
1414 GDEF_CLASS_LIGATURE = 2,
1415 GDEF_CLASS_MARK = 3,
1416 GDEF_CLASS_COMPONENT = 4,
1417 GDEF_CLASS_MAX = GDEF_CLASS_COMPONENT,
1420 struct dwrite_fonttable
1422 BYTE *data;
1423 void *context;
1424 unsigned int size;
1427 static const void *table_read_ensure(const struct dwrite_fonttable *table, unsigned int offset, unsigned int size)
1429 if (size > table->size || offset > table->size - size)
1430 return NULL;
1432 return table->data + offset;
1435 static WORD table_read_be_word(const struct dwrite_fonttable *table, unsigned int offset)
1437 const WORD *ptr = table_read_ensure(table, offset, sizeof(*ptr));
1438 return ptr ? GET_BE_WORD(*ptr) : 0;
1441 static int gdef_class_compare_format2(const void *g, const void *r)
1443 const struct ot_gdef_class_range *range = r;
1444 UINT16 glyph = *(UINT16 *)g;
1446 if (glyph < GET_BE_WORD(range->start_glyph))
1447 return -1;
1448 else if (glyph > GET_BE_WORD(range->end_glyph))
1449 return 1;
1450 else
1451 return 0;
1454 static unsigned int get_glyph_class(const struct dwrite_fonttable *table, UINT16 glyph)
1456 unsigned int glyph_class = GDEF_CLASS_UNCLASSIFIED, offset;
1457 WORD format, count;
1459 offset = table_read_be_word(table, 4);
1461 format = table_read_be_word(table, offset);
1463 if (format == 1)
1465 const struct ot_gdef_classdef_format1 *format1;
1467 count = table_read_be_word(table, offset + FIELD_OFFSET(struct ot_gdef_classdef_format1, glyph_count));
1468 format1 = table_read_ensure(table, offset, FIELD_OFFSET(struct ot_gdef_classdef_format1, classes[count]));
1469 if (format1)
1471 WORD start_glyph = GET_BE_WORD(format1->start_glyph);
1472 if (glyph >= start_glyph && (glyph - start_glyph) < count)
1474 glyph_class = GET_BE_WORD(format1->classes[glyph - start_glyph]);
1475 if (glyph_class > GDEF_CLASS_MAX)
1476 glyph_class = GDEF_CLASS_UNCLASSIFIED;
1480 else if (format == 2)
1482 const struct ot_gdef_classdef_format2 *format2;
1484 count = table_read_be_word(table, offset + FIELD_OFFSET(struct ot_gdef_classdef_format2, range_count));
1485 format2 = table_read_ensure(table, offset, FIELD_OFFSET(struct ot_gdef_classdef_format2, ranges[count]));
1486 if (format2)
1488 const struct ot_gdef_class_range *range = bsearch(&glyph, format2->ranges, count,
1489 sizeof(struct ot_gdef_class_range), gdef_class_compare_format2);
1490 glyph_class = range && glyph <= GET_BE_WORD(range->end_glyph) ?
1491 GET_BE_WORD(range->glyph_class) : GDEF_CLASS_UNCLASSIFIED;
1492 if (glyph_class > GDEF_CLASS_MAX)
1493 glyph_class = GDEF_CLASS_UNCLASSIFIED;
1497 return glyph_class;
1500 static void get_enus_string(IDWriteLocalizedStrings *strings, WCHAR *buff, unsigned int size)
1502 BOOL exists = FALSE;
1503 unsigned int index;
1504 HRESULT hr;
1506 hr = IDWriteLocalizedStrings_FindLocaleName(strings, L"en-us", &index, &exists);
1507 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1509 /* Not all fonts have an en-us name! */
1510 if (!exists)
1511 index = 0;
1513 hr = IDWriteLocalizedStrings_GetString(strings, index, buff, size);
1514 ok(hr == S_OK, "Failed to get name string, hr %#lx.\n", hr);
1517 static void test_glyph_props(IDWriteTextAnalyzer *analyzer, const WCHAR *family, const WCHAR *face,
1518 IDWriteFontFace *fontface)
1520 unsigned int i, ch, count, offset;
1521 struct dwrite_fonttable gdef;
1522 DWRITE_UNICODE_RANGE *ranges;
1523 IDWriteFontFace1 *fontface1;
1524 BOOL exists = FALSE;
1525 HRESULT hr;
1527 hr = IDWriteFontFace_TryGetFontTable(fontface, MS_GDEF_TAG, (const void **)&gdef.data, &gdef.size,
1528 &gdef.context, &exists);
1529 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1531 if (!exists)
1532 return;
1534 offset = table_read_be_word(&gdef, 4);
1535 if (!offset)
1537 IDWriteFontFace_ReleaseFontTable(fontface, gdef.context);
1538 return;
1541 if (FAILED(IDWriteFontFace_QueryInterface(fontface, &IID_IDWriteFontFace1, (void **)&fontface1)))
1543 IDWriteFontFace_ReleaseFontTable(fontface, gdef.context);
1544 return;
1547 hr = IDWriteFontFace1_GetUnicodeRanges(fontface1, 0, NULL, &count);
1548 ok(hr == E_NOT_SUFFICIENT_BUFFER, "Unexpected hr %#lx.\n", hr);
1550 ranges = malloc(count * sizeof(*ranges));
1552 hr = IDWriteFontFace1_GetUnicodeRanges(fontface1, count, ranges, &count);
1553 ok(hr == S_OK, "Failed to get ranges, hr %#lx.\n", hr);
1555 for (i = 0; i < count; ++i)
1557 if (ranges[i].first > 0xffff)
1558 break;
1560 for (ch = ranges[i].first; ch <= ranges[i].last; ch++)
1562 DWRITE_SHAPING_TEXT_PROPERTIES text_props[10];
1563 DWRITE_SHAPING_GLYPH_PROPERTIES glyph_props[10];
1564 UINT16 glyphs[10], clustermap[10], glyph;
1565 unsigned int actual_count, glyph_class;
1566 DWRITE_SCRIPT_ANALYSIS sa;
1567 WCHAR text[1];
1569 hr = IDWriteFontFace1_GetGlyphIndices(fontface1, &ch, 1, &glyph);
1570 ok(hr == S_OK, "Failed to get glyph index, hr %#lx.\n", hr);
1572 if (!glyph)
1573 continue;
1575 sa.script = 999;
1576 sa.shapes = DWRITE_SCRIPT_SHAPES_DEFAULT;
1577 text[0] = (WCHAR)ch;
1578 memset(glyph_props, 0, sizeof(glyph_props));
1579 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, text, 1, fontface, FALSE, FALSE, &sa, NULL,
1580 NULL, NULL, NULL, 0, ARRAY_SIZE(glyphs), clustermap, text_props, glyphs, glyph_props, &actual_count);
1581 ok(hr == S_OK, "Failed to shape, hr %#lx.\n", hr);
1582 if (actual_count > 1)
1583 continue;
1585 glyph_class = get_glyph_class(&gdef, glyphs[0]);
1587 switch (glyph_class)
1589 case GDEF_CLASS_MARK:
1590 ok(glyph_props[0].isDiacritic && glyph_props[0].isZeroWidthSpace,
1591 "%#x -> %u: unexpected glyph properties %u/%u. Class %u. Font %s - %s.\n",
1592 text[0], glyphs[0], glyph_props[0].isDiacritic, glyph_props[0].isZeroWidthSpace,
1593 glyph_class, wine_dbgstr_w(family), wine_dbgstr_w(face));
1594 break;
1595 default:
1596 break;
1599 if (glyph_props[0].isDiacritic)
1600 ok(glyph_props[0].isZeroWidthSpace,
1601 "%#x -> %u: unexpected glyph properties %u/%u. Class %u. Font %s - %s.\n", text[0], glyphs[0],
1602 glyph_props[0].isDiacritic, glyph_props[0].isZeroWidthSpace, glyph_class,
1603 wine_dbgstr_w(family), wine_dbgstr_w(face));
1607 free(ranges);
1609 IDWriteFontFace_ReleaseFontTable(fontface, gdef.context);
1610 IDWriteFontFace1_Release(fontface1);
1613 static void test_GetGlyphs(void)
1615 static const WCHAR test1W[] = {'<','B',' ','C',0};
1616 static const WCHAR test2W[] = {'<','B','\t','C',0};
1617 static const WCHAR test3W[] = {0x202a,0x202c,0};
1618 DWRITE_SHAPING_GLYPH_PROPERTIES shapingprops[20];
1619 DWRITE_SHAPING_TEXT_PROPERTIES props[20];
1620 UINT32 maxglyphcount, actual_count;
1621 FLOAT advances[10], advances2[10];
1622 IDWriteFontCollection *syscoll;
1623 IDWriteTextAnalyzer *analyzer;
1624 IDWriteFontFace *fontface;
1625 DWRITE_SCRIPT_ANALYSIS sa;
1626 DWRITE_GLYPH_OFFSET offsets[10];
1627 UINT16 clustermap[10];
1628 UINT16 glyphs1[10];
1629 UINT16 glyphs2[10];
1630 unsigned int i, j;
1631 HRESULT hr;
1633 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
1634 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1636 fontface = create_fontface();
1638 maxglyphcount = 1;
1639 sa.script = 0;
1640 sa.shapes = DWRITE_SCRIPT_SHAPES_DEFAULT;
1641 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test1W, lstrlenW(test1W), fontface, FALSE, FALSE, &sa, NULL,
1642 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs1, shapingprops, &actual_count);
1643 ok(hr == E_NOT_SUFFICIENT_BUFFER, "Unexpected hr %#lx.\n", hr);
1645 if (0) {
1646 /* NULL fontface - crashes on Windows */
1647 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test1W, lstrlenW(test1W), NULL, FALSE, FALSE, &sa, NULL,
1648 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs1, shapingprops, &actual_count);
1651 /* invalid script id */
1652 maxglyphcount = 10;
1653 actual_count = 0;
1654 sa.script = 999;
1655 sa.shapes = DWRITE_SCRIPT_SHAPES_DEFAULT;
1656 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test1W, lstrlenW(test1W), fontface, FALSE, FALSE, &sa, NULL,
1657 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs1, shapingprops, &actual_count);
1658 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1659 ok(actual_count == 4, "got %d\n", actual_count);
1660 ok(sa.script == 999, "got %u\n", sa.script);
1662 /* no '\t' -> ' ' replacement */
1663 maxglyphcount = 10;
1664 actual_count = 0;
1665 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test1W, lstrlenW(test1W), fontface, FALSE, FALSE, &sa, NULL,
1666 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs1, shapingprops, &actual_count);
1667 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1668 ok(actual_count == 4, "got %d\n", actual_count);
1670 actual_count = 0;
1671 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test2W, lstrlenW(test2W), fontface, FALSE, FALSE, &sa, NULL,
1672 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs2, shapingprops, &actual_count);
1673 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1674 ok(actual_count == 4, "got %d\n", actual_count);
1675 ok(glyphs1[2] != glyphs2[2], "got %d\n", glyphs1[2]);
1677 /* check that mirroring works */
1678 maxglyphcount = 10;
1679 actual_count = 0;
1680 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test1W, lstrlenW(test1W), fontface, FALSE, FALSE, &sa, NULL,
1681 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs1, shapingprops, &actual_count);
1682 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1683 ok(actual_count == 4, "got %d\n", actual_count);
1685 actual_count = 0;
1686 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test1W, lstrlenW(test1W), fontface, FALSE, TRUE, &sa, NULL,
1687 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs2, shapingprops, &actual_count);
1688 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1689 ok(actual_count == 4, "got %d\n", actual_count);
1690 ok(glyphs1[0] != glyphs2[0], "got %d\n", glyphs1[0]);
1692 /* embedded control codes, with unknown script id 0 */
1693 get_fontface_glyphs(fontface, test3W, glyphs2);
1694 get_fontface_advances(fontface, 10.0, glyphs2, advances2, 2);
1696 actual_count = 0;
1697 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test3W, lstrlenW(test3W), fontface, FALSE, TRUE, &sa, NULL,
1698 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs1, shapingprops, &actual_count);
1699 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1700 ok(actual_count == 2, "got %d\n", actual_count);
1701 ok(glyphs1[0] == glyphs2[0], "got %u, expected %u\n", glyphs1[0], glyphs2[0]);
1702 ok(glyphs1[1] == glyphs2[1], "got %u, expected %u\n", glyphs1[1], glyphs2[1]);
1703 ok(shapingprops[0].isClusterStart == 1, "got %d\n", shapingprops[0].isClusterStart);
1704 ok(shapingprops[0].isZeroWidthSpace == 0, "got %d\n", shapingprops[0].isZeroWidthSpace);
1705 ok(shapingprops[1].isClusterStart == 1, "got %d\n", shapingprops[1].isClusterStart);
1706 ok(shapingprops[1].isZeroWidthSpace == 0, "got %d\n", shapingprops[1].isZeroWidthSpace);
1707 ok(clustermap[0] == 0, "got %d\n", clustermap[0]);
1708 ok(clustermap[1] == 1, "got %d\n", clustermap[1]);
1710 memset(advances, 0, sizeof(advances));
1711 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, test3W, clustermap, props, lstrlenW(test3W),
1712 glyphs1, shapingprops, actual_count, fontface, 10.0, FALSE, FALSE, &sa, NULL, NULL,
1713 NULL, 0, advances, offsets);
1714 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1715 ok(advances[0] == advances2[0], "got %.2f, expected %.2f\n", advances[0], advances2[0]);
1716 ok(advances[1] == advances2[1], "got %.2f, expected %.2f\n", advances[1], advances2[1]);
1718 /* embedded control codes with proper script */
1719 sa.script = 0;
1720 get_script_analysis(test3W, &sa);
1721 ok(sa.script != 0, "got %d\n", sa.script);
1722 actual_count = 0;
1723 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test3W, lstrlenW(test3W), fontface, FALSE, FALSE, &sa, NULL,
1724 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs1, shapingprops, &actual_count);
1725 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1726 ok(actual_count == 2, "got %d\n", actual_count);
1727 ok(glyphs1[0] == glyphs2[0], "got %u, expected %u\n", glyphs1[0], glyphs2[0]);
1728 ok(glyphs1[1] == glyphs2[1], "got %u, expected %u\n", glyphs1[1], glyphs2[1]);
1729 ok(shapingprops[0].isClusterStart == 1, "got %d\n", shapingprops[0].isClusterStart);
1730 ok(shapingprops[0].isZeroWidthSpace == 0, "got %d\n", shapingprops[0].isZeroWidthSpace);
1731 ok(shapingprops[1].isClusterStart == 1, "got %d\n", shapingprops[1].isClusterStart);
1732 ok(shapingprops[1].isZeroWidthSpace == 0, "got %d\n", shapingprops[1].isZeroWidthSpace);
1733 ok(clustermap[0] == 0, "got %d\n", clustermap[0]);
1734 ok(clustermap[1] == 1, "got %d\n", clustermap[1]);
1736 memset(advances, 0, sizeof(advances));
1737 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, test3W, clustermap, props, lstrlenW(test3W),
1738 glyphs1, shapingprops, actual_count, fontface, 10.0, FALSE, FALSE, &sa, NULL, NULL,
1739 NULL, 0, advances, offsets);
1740 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1741 ok(advances[0] == advances2[0], "got %.2f, expected %.2f\n", advances[0], advances2[0]);
1742 ok(advances[1] == advances2[1], "got %.2f, expected %.2f\n", advances[1], advances2[1]);
1744 /* DWRITE_SCRIPT_SHAPES_NO_VISUAL run */
1745 maxglyphcount = 10;
1746 actual_count = 0;
1747 sa.script = 0;
1748 sa.shapes = DWRITE_SCRIPT_SHAPES_NO_VISUAL;
1749 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test1W, lstrlenW(test1W), fontface, FALSE, FALSE, &sa, NULL,
1750 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs1, shapingprops, &actual_count);
1751 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1752 ok(actual_count == 4, "got %d\n", actual_count);
1753 ok(sa.script == 0, "got %u\n", sa.script);
1754 ok(!shapingprops[0].isZeroWidthSpace, "got %d\n", shapingprops[0].isZeroWidthSpace);
1756 IDWriteFontFace_Release(fontface);
1758 /* Test setting glyph properties from GDEF. */
1759 hr = IDWriteFactory_GetSystemFontCollection(factory, &syscoll, FALSE);
1760 ok(hr == S_OK, "Failed to get system collection, hr %#lx.\n", hr);
1762 for (i = 0; i < IDWriteFontCollection_GetFontFamilyCount(syscoll); ++i)
1764 IDWriteLocalizedStrings *names;
1765 IDWriteFontFamily *family;
1766 WCHAR familyW[256];
1768 hr = IDWriteFontCollection_GetFontFamily(syscoll, i, &family);
1769 ok(hr == S_OK, "Failed to get font family, hr %#lx.\n", hr);
1771 hr = IDWriteFontFamily_GetFamilyNames(family, &names);
1772 ok(hr == S_OK, "Failed to get family names, hr %#lx.\n", hr);
1773 get_enus_string(names, familyW, ARRAY_SIZE(familyW));
1774 IDWriteLocalizedStrings_Release(names);
1776 for (j = 0; j < IDWriteFontFamily_GetFontCount(family); ++j)
1778 IDWriteFont *font;
1779 WCHAR faceW[256];
1781 hr = IDWriteFontFamily_GetFont(family, j, &font);
1782 ok(hr == S_OK, "Failed to get font instance, hr %#lx.\n", hr);
1784 hr = IDWriteFont_CreateFontFace(font, &fontface);
1785 ok(hr == S_OK, "Failed to create fontface, hr %#lx.\n", hr);
1787 hr = IDWriteFont_GetFaceNames(font, &names);
1788 ok(hr == S_OK, "Failed to get face names, hr %#lx.\n", hr);
1789 get_enus_string(names, faceW, ARRAY_SIZE(faceW));
1790 IDWriteLocalizedStrings_Release(names);
1792 test_glyph_props(analyzer, familyW, faceW, fontface);
1794 IDWriteFontFace_Release(fontface);
1795 IDWriteFont_Release(font);
1798 IDWriteFontFamily_Release(family);
1801 IDWriteFontCollection_Release(syscoll);
1803 IDWriteTextAnalyzer_Release(analyzer);
1806 static void test_GetTypographicFeatures(void)
1808 static const WCHAR arabicW[] = {0x064a,0x064f,0x0633,0};
1809 DWRITE_FONT_FEATURE_TAG tags[20];
1810 IDWriteTextAnalyzer2 *analyzer2;
1811 IDWriteTextAnalyzer *analyzer;
1812 IDWriteFontFace *fontface;
1813 DWRITE_SCRIPT_ANALYSIS sa;
1814 UINT32 count;
1815 HRESULT hr;
1817 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
1818 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1820 hr = IDWriteTextAnalyzer_QueryInterface(analyzer, &IID_IDWriteTextAnalyzer2, (void**)&analyzer2);
1821 IDWriteTextAnalyzer_Release(analyzer);
1822 if (hr != S_OK) {
1823 win_skip("GetTypographicFeatures() is not supported.\n");
1824 return;
1827 fontface = create_fontface();
1829 get_script_analysis(L"abc", &sa);
1830 count = 0;
1831 hr = IDWriteTextAnalyzer2_GetTypographicFeatures(analyzer2, fontface, sa, NULL, 0, &count, NULL);
1832 ok(hr == E_NOT_SUFFICIENT_BUFFER, "Unexpected hr %#lx.\n", hr);
1833 ok(!!count, "Unexpected count %u.\n", count);
1835 /* invalid locale name is ignored */
1836 get_script_analysis(L"abc", &sa);
1837 count = 0;
1838 hr = IDWriteTextAnalyzer2_GetTypographicFeatures(analyzer2, fontface, sa, L"cadabra", 0, &count, NULL);
1839 ok(hr == E_NOT_SUFFICIENT_BUFFER, "Unexpected hr %#lx.\n", hr);
1840 ok(!!count, "Unexpected count %u.\n", count);
1842 /* Make some calls for different scripts. */
1844 get_script_analysis(arabicW, &sa);
1845 memset(tags, 0, sizeof(tags));
1846 count = 0;
1847 hr = IDWriteTextAnalyzer2_GetTypographicFeatures(analyzer2, fontface, sa, NULL, ARRAY_SIZE(tags), &count, tags);
1848 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1849 ok(!!count, "Unexpected count %u.\n", count);
1851 get_script_analysis(L"abc", &sa);
1852 memset(tags, 0, sizeof(tags));
1853 count = 0;
1854 hr = IDWriteTextAnalyzer2_GetTypographicFeatures(analyzer2, fontface, sa, NULL, ARRAY_SIZE(tags), &count, tags);
1855 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1856 ok(!!count, "Unexpected count %u.\n", count);
1858 IDWriteFontFace_Release(fontface);
1859 IDWriteTextAnalyzer2_Release(analyzer2);
1862 static void test_GetGlyphPlacements(void)
1864 DWRITE_SHAPING_GLYPH_PROPERTIES glyphprops[2];
1865 DWRITE_SHAPING_TEXT_PROPERTIES textprops[2];
1866 static const WCHAR aW[] = {'A','D',0};
1867 UINT16 clustermap[2], glyphs[2];
1868 DWRITE_GLYPH_OFFSET offsets[2];
1869 IDWriteTextAnalyzer *analyzer;
1870 IDWriteFontFace *fontface;
1871 DWRITE_SCRIPT_ANALYSIS sa;
1872 FLOAT advances[2];
1873 UINT32 count, len;
1874 WCHAR *path;
1875 HRESULT hr;
1877 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
1878 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1880 path = create_testfontfile(L"wine_test_font.ttf");
1881 fontface = create_testfontface(path);
1883 get_script_analysis(aW, &sa);
1884 count = 0;
1885 len = lstrlenW(aW);
1886 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, aW, len, fontface, FALSE, FALSE, &sa, NULL,
1887 NULL, NULL, NULL, 0, len, clustermap, textprops, glyphs, glyphprops, &count);
1888 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1889 ok(count == 2, "got %u\n", count);
1891 /* just return on zero glyphs */
1892 advances[0] = advances[1] = 1.0;
1893 offsets[0].advanceOffset = offsets[0].ascenderOffset = 2.0;
1894 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, clustermap, textprops,
1895 len, glyphs, glyphprops, 0, fontface, 0.0, FALSE, FALSE, &sa, NULL, NULL,
1896 NULL, 0, advances, offsets);
1897 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1898 ok(advances[0] == 1.0, "got %.2f\n", advances[0]);
1899 ok(offsets[0].advanceOffset == 2.0 && offsets[0].ascenderOffset == 2.0, "got %.2f,%.2f\n",
1900 offsets[0].advanceOffset, offsets[0].ascenderOffset);
1902 /* advances/offsets are scaled with provided font emSize and designed eM box size */
1903 advances[0] = advances[1] = 1.0;
1904 memset(offsets, 0xcc, sizeof(offsets));
1905 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, clustermap, textprops,
1906 len, glyphs, glyphprops, len, fontface, 0.0, FALSE, FALSE, &sa, NULL, NULL,
1907 NULL, 0, advances, offsets);
1908 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1909 ok(advances[0] == 0.0, "got %.2f\n", advances[0]);
1910 ok(offsets[0].advanceOffset == 0.0 && offsets[0].ascenderOffset == 0.0, "got %.2f,%.2f\n",
1911 offsets[0].advanceOffset, offsets[0].ascenderOffset);
1913 advances[0] = advances[1] = 1.0;
1914 memset(offsets, 0xcc, sizeof(offsets));
1915 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, clustermap, textprops,
1916 len, glyphs, glyphprops, len, fontface, 2048.0, FALSE, FALSE, &sa, NULL, NULL,
1917 NULL, 0, advances, offsets);
1918 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1919 ok(advances[0] == 1000.0, "got %.2f\n", advances[0]);
1920 ok(offsets[0].advanceOffset == 0.0 && offsets[0].ascenderOffset == 0.0, "got %.2f,%.2f\n",
1921 offsets[0].advanceOffset, offsets[0].ascenderOffset);
1923 advances[0] = advances[1] = 1.0;
1924 memset(offsets, 0xcc, sizeof(offsets));
1925 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, clustermap, textprops,
1926 len, glyphs, glyphprops, len, fontface, 1024.0, FALSE, FALSE, &sa, NULL, NULL,
1927 NULL, 0, advances, offsets);
1928 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1929 ok(advances[0] == 500.0, "got %.2f\n", advances[0]);
1930 ok(advances[1] == 500.0, "got %.2f\n", advances[1]);
1931 ok(offsets[0].advanceOffset == 0.0 && offsets[0].ascenderOffset == 0.0, "got %.2f,%.2f\n",
1932 offsets[0].advanceOffset, offsets[0].ascenderOffset);
1934 advances[0] = advances[1] = 1.0;
1935 memset(offsets, 0xcc, sizeof(offsets));
1936 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, clustermap, textprops,
1937 len, glyphs, glyphprops, len, fontface, 20.48, FALSE, FALSE, &sa, NULL, NULL,
1938 NULL, 0, advances, offsets);
1939 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1940 ok(advances[0] == 10.0, "got %.2f\n", advances[0]);
1941 ok(advances[1] == 10.0, "got %.2f\n", advances[1]);
1942 ok(offsets[0].advanceOffset == 0.0 && offsets[0].ascenderOffset == 0.0, "got %.2f,%.2f\n",
1943 offsets[0].advanceOffset, offsets[0].ascenderOffset);
1945 /* without clustermap */
1946 advances[0] = advances[1] = 1.0;
1947 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, NULL, textprops,
1948 len, glyphs, glyphprops, len, fontface, 1024.0, FALSE, FALSE, &sa, NULL, NULL,
1949 NULL, 0, advances, offsets);
1950 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1951 ok(advances[0] == 500.0, "got %.2f\n", advances[0]);
1952 ok(advances[1] == 500.0, "got %.2f\n", advances[1]);
1954 /* it's happy to use negative size too */
1955 advances[0] = advances[1] = 1.0;
1956 memset(offsets, 0xcc, sizeof(offsets));
1957 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, clustermap, textprops,
1958 len, glyphs, glyphprops, len, fontface, -10.24, FALSE, FALSE, &sa, NULL, NULL,
1959 NULL, 0, advances, offsets);
1960 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1961 ok(advances[0] == -5.0, "got %.2f\n", advances[0]);
1962 ok(offsets[0].advanceOffset == 0.0 && offsets[0].ascenderOffset == 0.0, "got %.2f,%.2f\n",
1963 offsets[0].advanceOffset, offsets[0].ascenderOffset);
1965 /* DWRITE_SCRIPT_SHAPES_NO_VISUAL has no effect on placement */
1966 sa.shapes = DWRITE_SCRIPT_SHAPES_NO_VISUAL;
1967 advances[0] = advances[1] = 1.0f;
1968 memset(offsets, 0xcc, sizeof(offsets));
1969 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, clustermap, textprops,
1970 len, glyphs, glyphprops, len, fontface, 2048.0f, FALSE, FALSE, &sa, NULL, NULL,
1971 NULL, 0, advances, offsets);
1972 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1973 ok(advances[0] == 1000.0f, "got %.2f\n", advances[0]);
1974 ok(advances[1] == 1000.0f, "got %.2f\n", advances[1]);
1975 ok(offsets[0].advanceOffset == 0.0f && offsets[0].ascenderOffset == 0.0f, "got %.2f,%.2f\n",
1976 offsets[0].advanceOffset, offsets[0].ascenderOffset);
1978 /* isZeroWidthSpace */
1979 sa.shapes = DWRITE_SCRIPT_SHAPES_DEFAULT;
1980 advances[0] = advances[1] = 1.0f;
1981 memset(offsets, 0xcc, sizeof(offsets));
1982 glyphprops[0].isZeroWidthSpace = 1;
1983 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, clustermap, textprops,
1984 len, glyphs, glyphprops, len, fontface, 2048.0f, FALSE, FALSE, &sa, NULL, NULL,
1985 NULL, 0, advances, offsets);
1986 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1987 ok(advances[0] == 0.0f, "got %.2f\n", advances[0]);
1988 ok(advances[1] == 1000.0f, "got %.2f\n", advances[1]);
1989 ok(offsets[0].advanceOffset == 0.0f && offsets[0].ascenderOffset == 0.0f, "got %.2f,%.2f\n",
1990 offsets[0].advanceOffset, offsets[0].ascenderOffset);
1992 IDWriteTextAnalyzer_Release(analyzer);
1993 IDWriteFontFace_Release(fontface);
1994 DELETE_FONTFILE(path);
1997 struct spacing_test {
1998 FLOAT leading;
1999 FLOAT trailing;
2000 FLOAT min_advance;
2001 FLOAT advances[3];
2002 FLOAT offsets[3];
2003 FLOAT modified_advances[3];
2004 FLOAT modified_offsets[3];
2005 BOOL single_cluster;
2006 DWRITE_SHAPING_GLYPH_PROPERTIES props[3];
2009 static const struct spacing_test spacing_tests[] =
2011 /* Default spacing glyph properties. */
2012 #define P_S { 0 }
2013 /* isZeroWidthSpace */
2014 #define P_Z { 0, 0, 0, 1, 0 }
2015 /* isDiacritic */
2016 #define P_D { 0, 0, 1, 0, 0 }
2017 /* isDiacritic + isZeroWidthSpace, that's how diacritics are shaped. */
2018 #define P_D_Z { 0, 0, 1, 1, 0 }
2020 { 0.0, 0.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 } }, /* 0 */
2021 { 0.0, 0.0, 2.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 } },
2022 { 1.0, 0.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 11.0, 12.0 }, { 3.0, 4.0 } },
2023 { 1.0, 1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 12.0, 13.0 }, { 3.0, 4.0 } },
2024 { 1.0, -1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 3.0, 4.0 } },
2025 { 0.0, -10.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 0.0, 1.0 }, { 2.0, 3.0 } }, /* 5 */
2026 { -5.0, -4.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 5.0, 5.0 }, { -1.0, -0.5 } },
2027 { -5.0, -5.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 5.0, 5.0 }, { -0.5, 0.0 } },
2028 { 2.0, 0.0, 5.0, { 1.0, 2.0 }, { 2.0, 3.0 }, { 7.0, 7.0 }, { 6.0, 6.5 } },
2029 { 2.0, 1.0, 5.0, { 1.0, 2.0 }, { 2.0, 3.0 }, { 8.0, 8.0 }, { 6.0, 6.5 } },
2030 { 2.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 7.0, 7.0 }, { 4.0, 5.0 } }, /* 10 */
2031 { 1.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 6.0, 6.0 }, { 3.0, 4.0 } },
2032 { -10.0, 1.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 6.0, 6.0 }, { -3.0, -3.0 } },
2033 { 0.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 5.0, 5.0 }, { 2.0, 3.0 } },
2034 { 1.0, -10.0, -5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 0.0, 0.0 }, { 2.0, 3.0 } },
2035 { -10.0, 1.0, 5.0, { 8.0, 11.0 }, { 2.0, 3.0 }, { 6.0, 6.0 }, { -1.0, -3.0 } }, /* 15 */
2036 /* cluster of more than 1 glyph */
2037 { 0.0f, 0.0f, 0.0f, { 10.0f, 11.0f }, { 2.0f, 3.0f }, { 10.0f, 11.0f }, { 2.0f, 3.0f }, TRUE },
2038 { 1.0f, 0.0f, 0.0f, { 10.0f, 11.0f }, { 2.0f, 3.5f }, { 11.0f, 11.0f }, { 3.0f, 3.5f }, TRUE },
2039 { 1.0f, 1.0f, 0.0f, { 10.0f, 11.0f }, { 2.0f, 3.0f }, { 11.0f, 12.0f }, { 3.0f, 3.0f }, TRUE },
2040 { 1.0, -1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 11.0, 10.0 }, { 3.0, 3.0 }, TRUE },
2041 { 0.0, -10.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 1.0 }, { 2.0, 3.0 }, TRUE }, /* 20 */
2042 { 0.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 1.0 }, { 2.0, 3.0 }, TRUE },
2043 { 1.0, -10.0, -5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 0.0, 0.0 }, { 2.0, 3.0 }, TRUE },
2044 { -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 },
2045 { -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 },
2046 { -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 */
2047 { -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 },
2048 { 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 },
2049 { -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 },
2050 /* isZeroWidthSpace set */
2051 { 1.0, 0.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 12.0 }, { 2.0, 4.0 }, FALSE, { P_Z, P_S } },
2052 { 1.0, 1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 13.0 }, { 2.0, 4.0 }, FALSE, { P_Z, P_S } }, /* 30 */
2053 { 1.0, -1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 3.0, 3.0 }, FALSE, { P_S, P_Z } },
2054 { 0.0, -10.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 1.0 }, { 2.0, 3.0 }, FALSE, { P_Z, P_S } },
2055 { -5.0, -4.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 5.0, 11.0 }, { -1.0, 3.0 }, FALSE, { P_S, P_Z } },
2056 { -5.0, -5.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 }, FALSE, { P_Z, P_Z } },
2057 { 2.0, 0.0, 5.0, { 1.0, 2.0 }, { 2.0, 3.0 }, { 7.0, 2.0 }, { 6.0, 3.0 }, FALSE, { P_S, P_Z } }, /* 35 */
2058 { 2.0, 1.0, 5.0, { 1.0, 2.0 }, { 2.0, 3.0 }, { 8.0, 2.0 }, { 6.0, 3.0 }, FALSE, { P_S, P_Z } },
2059 { 2.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 }, FALSE, { P_Z, P_Z } },
2060 { 1.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 6.0, 11.0 }, { 3.0, 3.0 }, FALSE, { P_S, P_Z } },
2061 { -10.0, 1.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 }, FALSE, { P_Z, P_Z } },
2062 { 0.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 5.0, 11.0 }, { 2.0, 3.0 }, FALSE, { P_S, P_Z } }, /* 40 */
2063 { 1.0, -10.0, -5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 0.0, 0.0 }, { 2.0, 3.0 }, FALSE, { P_Z, P_S } },
2064 { -10.0, 1.0, 5.0, { 8.0, 11.0 }, { 2.0, 3.0 }, { 6.0, 11.0 }, { -1.0, 3.0 }, FALSE, { P_S, P_Z } },
2065 /* isDiacritic */
2066 { 1.0, 0.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 11.0, 12.0 }, { 3.0, 4.0 }, FALSE, { P_D, P_S } },
2067 { 1.0, 1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 12.0, 13.0 }, { 3.0, 4.0 }, FALSE, { P_D, P_S } },
2068 { 1.0, -1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 3.0, 4.0 }, FALSE, { P_S, P_D } }, /* 45 */
2069 { 0.0, -10.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 0.0, 1.0 }, { 2.0, 3.0 }, FALSE, { P_D, P_S } },
2070 { -5.0, -4.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 5.0, 5.0 }, { -1.0, -0.5 }, FALSE, { P_S, P_D } },
2071 { -5.0, -5.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 5.0, 5.0 }, { -0.5, 0.0 }, FALSE, { P_D, P_D } },
2072 { 2.0, 0.0, 5.0, { 1.0, 2.0 }, { 2.0, 3.0 }, { 7.0, 7.0 }, { 6.0, 6.5 }, FALSE, { P_S, P_D } },
2073 { 2.0, 1.0, 5.0, { 1.0, 2.0 }, { 2.0, 3.0 }, { 8.0, 8.0 }, { 6.0, 6.5 }, FALSE, { P_S, P_D } }, /* 50 */
2074 { 2.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 7.0, 7.0 }, { 4.0, 5.0 }, FALSE, { P_D, P_D } },
2075 { 1.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 6.0, 6.0 }, { 3.0, 4.0 }, FALSE, { P_S, P_D } },
2076 { -10.0, 1.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 6.0, 6.0 }, { -3.0, -3.0 }, FALSE, { P_D, P_D } },
2077 { 0.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 5.0, 5.0 }, { 2.0, 3.0 }, FALSE, { P_S, P_D } },
2078 { 1.0, -10.0, -5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 0.0, 0.0 }, { 2.0, 3.0 }, FALSE, { P_D, P_S } }, /* 55 */
2079 { -10.0, 1.0, 5.0, { 8.0, 11.0 }, { 2.0, 3.0 }, { 6.0, 6.0 }, { -1.0, -3.0 }, FALSE, { P_S, P_D } },
2080 /* isZeroWidthSpace in a cluster */
2081 { 1.0, 0.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 12.0 }, { 3.0, 4.0 }, TRUE, { P_Z, P_S } },
2082 { 1.0, 1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 13.0 }, { 3.0, 4.0 }, TRUE, { P_Z, P_S } },
2083 { 1.0, -1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 3.0, 4.0 }, TRUE, { P_S, P_Z } },
2084 { 0.0, -10.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 1.0 }, { 2.0, 3.0 }, TRUE, { P_Z, P_S } }, /* 60 */
2085 { -5.0, -4.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 1.0, 11.0 }, { -3.0, 7.0 }, TRUE, { P_S, P_Z } },
2086 { -5.0, -5.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 }, TRUE, { P_Z, P_Z } },
2087 { 0.0, 0.0, 5.0, { 1.0, 2.0 }, { 2.0, 3.0 }, { 3.0, 2.0 }, { 3.0, 2.0 }, TRUE, { P_S, P_Z } },
2088 { 0.0, 0.0, 5.0, { 1.0, 2.0, 1.0 }, { 2.0, 3.0, 5.0 }, { 1.5, 2.0, 1.5 }, { 2.5, 3.0, 5.0 }, TRUE, { P_S, P_Z, P_S } },
2089 { 0.0, 0.0, 5.0, { 1.0, 2.0, 1.0 }, { 2.0, 3.0, 5.0 }, { 1.5, 2.5, 1.0 }, { 2.5, 3.0, 4.5 }, TRUE, { P_S, P_S, P_Z } },
2090 { 0.0, 0.0, 5.0, { 1.0, 2.0, 1.0 }, { 2.0, 3.0, 5.0 }, { 2.0, 2.0, 1.0 }, { 2.5, 2.5, 4.5 }, TRUE, { P_S, P_Z, P_Z } },
2091 { 0.0, 0.0, 5.0, { 1.0, 2.0, 1.0 }, { 2.0, 3.0, 5.0 }, { 1.0, 2.0, 1.0 }, { 2.0, 3.0, 5.0 }, TRUE, { P_Z, P_Z, P_Z } },
2092 { 2.0, 1.0, 1.0, { 1.0, 2.0, 3.0 }, { 2.0, 3.0, 4.0 }, { 3.0, 2.0, 4.0 }, { 4.0, 3.0, 4.0 }, TRUE, { P_S, P_Z, P_S } },
2093 { 0.0, 0.0, 5.0, { 1.0, 2.0 }, { 2.0, 3.0 }, { 3.0, 2.0 }, { 3.0, 2.0 }, TRUE, { P_S, P_Z } },
2094 { 0.0, 0.0, 5.0, { 1.0, 2.0, 6.0 }, { 2.0, 3.0, 4.0 }, { 1.0, 2.0, 6.0 }, { 2.0, 3.0, 4.0 }, TRUE, { P_S, P_Z, P_S } },
2095 { 2.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 }, TRUE, { P_Z, P_Z } }, /* 65 */
2096 { 1.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 1.0, 11.0 }, { 3.0, 13.0 }, TRUE, { P_S, P_Z } },
2097 { -10.0, 1.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 }, TRUE, { P_Z, P_Z } },
2098 { 0.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 0.0, 11.0 }, { 2.0, 13.0 }, TRUE, { P_S, P_Z } },
2099 { 1.0, -10.0, -5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 0.0, 0.0 }, { 2.0, 3.0 }, TRUE, { P_Z, P_S } },
2100 { -10.0, 1.0, 5.0, { 8.0, 11.0 }, { 2.0, 3.0 }, { -1.0, 11.0 }, { -8.0, 2.0 }, TRUE, { P_S, P_Z } }, /* 70 */
2101 /* isDiacritic in a cluster */
2102 { 1.0, 0.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 11.0, 11.0 }, { 3.0, 3.0 }, TRUE, { P_D, P_S } },
2103 { 1.0, 1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 11.0, 12.0 }, { 3.0, 3.0 }, TRUE, { P_D, P_S } },
2104 { 1.0, -1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 11.0, 10.0 }, { 3.0, 3.0 }, TRUE, { P_S, P_D } },
2105 { 0.0, -10.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 1.0 }, { 2.0, 3.0 }, TRUE, { P_D, P_S } },
2106 { -5.0, -4.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 5.0, 7.0 }, { -3.0, 3.0 }, TRUE, { P_S, P_D } }, /* 75 */
2107 { -5.0, -5.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 5.0, 6.0 }, { -3.0, 3.0 }, TRUE, { P_D, P_D } },
2108 { 2.0, 0.0, 5.0, { 1.0, 2.0 }, { 2.0, 3.0 }, { 4.0, 3.0 }, { 5.0, 3.0 }, TRUE, { P_S, P_D } },
2109 { 2.0, 1.0, 5.0, { 1.0, 2.0 }, { 2.0, 3.0 }, { 4.0, 4.0 }, { 5.0, 3.0 }, TRUE, { P_S, P_D } },
2110 { 2.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 12.0, 1.0 }, { 4.0, 3.0 }, TRUE, { P_D, P_D } },
2111 { 1.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 11.0, 1.0 }, { 3.0, 3.0 }, TRUE, { P_S, P_D } }, /* 80 */
2112 { -10.0, 1.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 0.0, 12.0 }, { -8.0, 3.0 }, TRUE, { P_D, P_D } },
2113 { 0.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 1.0 }, { 2.0, 3.0 }, TRUE, { P_S, P_D } },
2114 { 1.0, -10.0, -5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 0.0, 0.0 }, { 2.0, 3.0 }, TRUE, { P_D, P_S } },
2115 { -10.0, 1.0, 5.0, { 8.0, 11.0 }, { 2.0, 3.0 }, { -2.0, 12.0 }, { -8.0, 3.0 }, TRUE, { P_S, P_D } },
2116 /* isZeroWidthSpace + isDiacritic */
2117 { 1.0, 0.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 12.0 }, { 2.0, 4.0 }, FALSE, { P_D_Z, P_S } }, /* 85 */
2118 { 1.0, 1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 13.0 }, { 2.0, 4.0 }, FALSE, { P_D_Z, P_S } },
2119 { 1.0, -1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 3.0, 3.0 }, FALSE, { P_S, P_D_Z } },
2120 { 0.0, -10.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 1.0 }, { 2.0, 3.0 }, FALSE, { P_D_Z, P_S } },
2121 { -5.0, -4.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 5.0, 11.0 }, { -1.0, 3.0 }, FALSE, { P_S, P_D_Z } },
2122 { -5.0, -5.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 }, FALSE, { P_D_Z, P_D_Z } }, /* 90 */
2123 { 2.0, 0.0, 5.0, { 1.0, 2.0 }, { 2.0, 3.0 }, { 7.0, 2.0 }, { 6.0, 3.0 }, FALSE, { P_S, P_D_Z } },
2124 { 2.0, 1.0, 5.0, { 1.0, 2.0 }, { 2.0, 3.0 }, { 8.0, 2.0 }, { 6.0, 3.0 }, FALSE, { P_S, P_D_Z } },
2125 { 2.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 }, FALSE, { P_D_Z, P_D_Z } },
2126 { 1.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 6.0, 11.0 }, { 3.0, 3.0 }, FALSE, { P_S, P_D_Z } },
2127 { -10.0, 1.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 }, FALSE, { P_D_Z, P_D_Z } }, /* 95 */
2128 { 0.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 5.0, 11.0 }, { 2.0, 3.0 }, FALSE, { P_S, P_D_Z } },
2129 { 1.0, -10.0, -5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 0.0, 0.0 }, { 2.0, 3.0 }, FALSE, { P_D_Z, P_S } },
2130 { -10.0, 1.0, 5.0, { 8.0, 11.0 }, { 2.0, 3.0 }, { 6.0, 11.0 }, { -1.0, 3.0 }, FALSE, { P_S, P_D_Z } },
2131 /* isZeroWidthSpace + isDiacritic in a cluster */
2132 { 1.0, 0.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 12.0 }, { 3.0, 4.0 }, TRUE, { P_D_Z, P_S } },
2133 { 1.0, 1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 13.0 }, { 3.0, 4.0 }, TRUE, { P_D_Z, P_S } }, /* 100 */
2134 { 1.0, -1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 3.0, 4.0 }, TRUE, { P_S, P_D_Z } },
2135 { 0.0, -10.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 1.0 }, { 2.0, 3.0 }, TRUE, { P_D_Z, P_S } },
2136 { -5.0, -4.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 1.0, 11.0 }, { -3.0, 7.0 }, TRUE, { P_S, P_D_Z } },
2137 { -5.0, -5.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 }, TRUE, { P_D_Z, P_D_Z } },
2138 { 2.0, 0.0, 5.0, { 1.0, 2.0 }, { 2.0, 3.0 }, { 5.0, 2.0 }, { 5.0, 2.0 }, TRUE, { P_S, P_D_Z } }, /* 105 */
2139 { 2.0, 1.0, 5.0, { 1.0, 2.0 }, { 2.0, 3.0 }, { 6.0, 2.0 }, { 5.0, 1.0 }, TRUE, { P_S, P_D_Z } },
2140 { 2.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 }, TRUE, { P_D_Z, P_D_Z } },
2141 { 1.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 1.0, 11.0 }, { 3.0, 13.0 }, TRUE, { P_S, P_D_Z } },
2142 { -10.0, 1.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 }, TRUE, { P_D_Z, P_D_Z } },
2143 { 0.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 0.0, 11.0 }, { 2.0, 13.0 }, TRUE, { P_S, P_D_Z } }, /* 110 */
2144 { 1.0, -10.0, -5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 0.0, 0.0 }, { 2.0, 3.0 }, TRUE, { P_D_Z, P_S } },
2145 { -10.0, 1.0, 5.0, { 8.0, 11.0 }, { 2.0, 3.0 }, { -1.0, 11.0 }, { -8.0, 2.0 }, TRUE, { P_S, P_D_Z } },
2147 #undef P_S
2148 #undef P_D
2149 #undef P_Z
2150 #undef P_D_Z
2153 static void test_ApplyCharacterSpacing(void)
2155 DWRITE_SHAPING_GLYPH_PROPERTIES props[3];
2156 IDWriteTextAnalyzer1 *analyzer1;
2157 IDWriteTextAnalyzer *analyzer;
2158 UINT16 clustermap[2];
2159 HRESULT hr;
2160 int i;
2162 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
2163 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2165 hr = IDWriteTextAnalyzer_QueryInterface(analyzer, &IID_IDWriteTextAnalyzer1, (void**)&analyzer1);
2166 IDWriteTextAnalyzer_Release(analyzer);
2167 if (hr != S_OK) {
2168 win_skip("ApplyCharacterSpacing() is not supported.\n");
2169 return;
2172 for (i = 0; i < ARRAY_SIZE(spacing_tests); ++i)
2174 const struct spacing_test *ptr = spacing_tests + i;
2175 DWRITE_GLYPH_OFFSET offsets[3];
2176 UINT32 glyph_count;
2177 FLOAT advances[3];
2179 offsets[0].advanceOffset = ptr->offsets[0];
2180 offsets[1].advanceOffset = ptr->offsets[1];
2181 offsets[2].advanceOffset = ptr->offsets[2];
2182 /* Ascender offsets are never touched as spacing applies in reading direction only,
2183 we'll only test them to see if they are not changed */
2184 offsets[0].ascenderOffset = 23.0;
2185 offsets[1].ascenderOffset = 32.0;
2186 offsets[2].ascenderOffset = 31.0;
2188 advances[0] = advances[1] = 123.45f;
2189 memcpy(props, ptr->props, sizeof(props));
2190 glyph_count = ptr->advances[2] > 0.0 ? 3 : 2;
2191 if (ptr->single_cluster)
2193 clustermap[0] = 0;
2194 clustermap[1] = 0;
2195 props[0].isClusterStart = 1;
2197 else
2199 /* trivial case with one glyph per cluster */
2200 clustermap[0] = 0;
2201 clustermap[1] = 1;
2202 props[0].isClusterStart = props[1].isClusterStart = 1;
2205 winetest_push_context("Test %u", i);
2207 hr = IDWriteTextAnalyzer1_ApplyCharacterSpacing(analyzer1,
2208 ptr->leading,
2209 ptr->trailing,
2210 ptr->min_advance,
2211 ARRAY_SIZE(clustermap),
2212 glyph_count,
2213 clustermap,
2214 ptr->advances,
2215 offsets,
2216 props,
2217 advances,
2218 offsets);
2219 ok(hr == (ptr->min_advance < 0.0f ? E_INVALIDARG : S_OK), "Unexpected hr %#lx.\n", hr);
2221 if (hr == S_OK) {
2222 ok(ptr->modified_advances[0] == advances[0], "Got advance[0] %.2f, expected %.2f.\n", advances[0], ptr->modified_advances[0]);
2223 ok(ptr->modified_advances[1] == advances[1], "Got advance[1] %.2f, expected %.2f.\n", advances[1], ptr->modified_advances[1]);
2224 if (glyph_count > 2)
2225 ok(ptr->modified_advances[2] == advances[2], "Got advance[2] %.2f, expected %.2f.\n", advances[2], ptr->modified_advances[2]);
2227 ok(ptr->modified_offsets[0] == offsets[0].advanceOffset, "Got offset[0] %.2f, expected %.2f.\n",
2228 offsets[0].advanceOffset, ptr->modified_offsets[0]);
2229 ok(ptr->modified_offsets[1] == offsets[1].advanceOffset, "Got offset[1] %.2f, expected %.2f.\n",
2230 offsets[1].advanceOffset, ptr->modified_offsets[1]);
2231 if (glyph_count > 2)
2232 ok(ptr->modified_offsets[2] == offsets[2].advanceOffset, "Got offset[2] %.2f, expected %.2f.\n",
2233 offsets[2].advanceOffset, ptr->modified_offsets[2]);
2235 ok(offsets[0].ascenderOffset == 23.0, "Unexpected ascenderOffset %.2f.\n", offsets[0].ascenderOffset);
2236 ok(offsets[1].ascenderOffset == 32.0, "Unexpected ascenderOffset %.2f.\n", offsets[1].ascenderOffset);
2237 ok(offsets[2].ascenderOffset == 31.0, "Unexpected ascenderOffset %.2f.\n", offsets[2].ascenderOffset);
2239 else {
2240 ok(ptr->modified_advances[0] == advances[0], "Got advance[0] %.2f, expected %.2f.\n", advances[0], ptr->modified_advances[0]);
2241 ok(ptr->modified_advances[1] == advances[1], "Got advance[1] %.2f, expected %.2f.\n", advances[1], ptr->modified_advances[1]);
2242 ok(ptr->offsets[0] == offsets[0].advanceOffset, "Got offset[0] %.2f, expected %.2f.\n",
2243 offsets[0].advanceOffset, ptr->modified_offsets[0]);
2244 ok(ptr->offsets[1] == offsets[1].advanceOffset, "Got offset[1] %.2f, expected %.2f.\n",
2245 offsets[1].advanceOffset, ptr->modified_offsets[1]);
2246 ok(offsets[0].ascenderOffset == 23.0, "Unexpected ascenderOffset %.2f.\n", offsets[0].ascenderOffset);
2247 ok(offsets[1].ascenderOffset == 32.0, "Unexpected ascenderOffset %.2f.\n", offsets[1].ascenderOffset);
2250 /* same, with argument aliasing */
2251 memcpy(advances, ptr->advances, glyph_count * sizeof(*advances));
2252 offsets[0].advanceOffset = ptr->offsets[0];
2253 offsets[1].advanceOffset = ptr->offsets[1];
2254 offsets[2].advanceOffset = ptr->offsets[2];
2255 /* Ascender offsets are never touched as spacing applies in reading direction only,
2256 we'll only test them to see if they are not changed */
2257 offsets[0].ascenderOffset = 23.0f;
2258 offsets[1].ascenderOffset = 32.0f;
2259 offsets[2].ascenderOffset = 31.0f;
2261 hr = IDWriteTextAnalyzer1_ApplyCharacterSpacing(analyzer1,
2262 ptr->leading,
2263 ptr->trailing,
2264 ptr->min_advance,
2265 ARRAY_SIZE(clustermap),
2266 glyph_count,
2267 clustermap,
2268 advances,
2269 offsets,
2270 props,
2271 advances,
2272 offsets);
2273 ok(hr == (ptr->min_advance < 0.0f ? E_INVALIDARG : S_OK), "Unexpected hr %#lx.\n", hr);
2275 if (hr == S_OK)
2277 ok(ptr->modified_advances[0] == advances[0], "Got advance[0] %.2f, expected %.2f.\n", advances[0], ptr->modified_advances[0]);
2278 ok(ptr->modified_advances[1] == advances[1], "Got advance[1] %.2f, expected %.2f.\n", advances[1], ptr->modified_advances[1]);
2279 if (glyph_count > 2)
2280 ok(ptr->modified_advances[2] == advances[2], "Got advance[2] %.2f, expected %.2f.\n", advances[2], ptr->modified_advances[2]);
2282 ok(ptr->modified_offsets[0] == offsets[0].advanceOffset, "Got offset[0] %.2f, expected %.2f.\n",
2283 offsets[0].advanceOffset, ptr->modified_offsets[0]);
2284 ok(ptr->modified_offsets[1] == offsets[1].advanceOffset, "Got offset[1] %.2f, expected %.2f.\n",
2285 offsets[1].advanceOffset, ptr->modified_offsets[1]);
2286 if (glyph_count > 2)
2287 ok(ptr->modified_offsets[2] == offsets[2].advanceOffset, "Got offset[2] %.2f, expected %.2f.\n",
2288 offsets[2].advanceOffset, ptr->modified_offsets[2]);
2290 ok(offsets[0].ascenderOffset == 23.0f, "Unexpected ascenderOffset %.2f.\n", offsets[0].ascenderOffset);
2291 ok(offsets[1].ascenderOffset == 32.0f, "Unexpected ascenderOffset %.2f.\n", offsets[1].ascenderOffset);
2292 ok(offsets[2].ascenderOffset == 31.0f, "Unexpected ascenderOffset %.2f.\n", offsets[2].ascenderOffset);
2294 else
2296 /* with aliased advances original values are retained */
2297 ok(ptr->advances[0] == advances[0], "Got advance[0] %.2f, expected %.2f.\n", advances[0], ptr->advances[0]);
2298 ok(ptr->advances[1] == advances[1], "Got advance[1] %.2f, expected %.2f.\n", advances[1], ptr->advances[1]);
2299 ok(ptr->offsets[0] == offsets[0].advanceOffset, "Got offset[0] %.2f, expected %.2f.\n",
2300 offsets[0].advanceOffset, ptr->modified_offsets[0]);
2301 ok(ptr->offsets[1] == offsets[1].advanceOffset, "Got offset[1] %.2f, expected %.2f.\n",
2302 offsets[1].advanceOffset, ptr->modified_offsets[1]);
2303 ok(offsets[0].ascenderOffset == 23.0f, "Unexpected ascenderOffset %.2f.\n", offsets[0].ascenderOffset);
2304 ok(offsets[1].ascenderOffset == 32.0f, "Unexpected ascenderOffset %.2f.\n", offsets[1].ascenderOffset);
2307 winetest_pop_context();
2310 IDWriteTextAnalyzer1_Release(analyzer1);
2313 struct orientation_transf_test {
2314 DWRITE_GLYPH_ORIENTATION_ANGLE angle;
2315 BOOL is_sideways;
2316 DWRITE_MATRIX m;
2319 static const struct orientation_transf_test ot_tests[] = {
2320 { DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES, FALSE, { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 } },
2321 { DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES, FALSE, { 0.0, 1.0, -1.0, 0.0, 0.0, 0.0 } },
2322 { DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES, FALSE, { -1.0, 0.0, 0.0, -1.0, 0.0, 0.0 } },
2323 { DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES, FALSE, { 0.0, -1.0, 1.0, 0.0, 0.0, 0.0 } },
2324 { DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES, TRUE, { 0.0, 1.0, -1.0, 0.0, 0.0, 0.0 } },
2325 { DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES, TRUE, { -1.0, 0.0, 0.0, -1.0, 0.0, 0.0 } },
2326 { DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES, TRUE, { 0.0, -1.0, 1.0, 0.0, 0.0, 0.0 } },
2327 { DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES, TRUE, { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 } }
2330 static inline const char *dbgstr_matrix(const DWRITE_MATRIX *m)
2332 static char buff[64];
2333 sprintf(buff, "{%.2f, %.2f, %.2f, %.2f, %.2f, %.2f}", m->m11, m->m12,
2334 m->m21, m->m22, m->dx, m->dy);
2335 return buff;
2338 static void test_GetGlyphOrientationTransform(void)
2340 IDWriteTextAnalyzer2 *analyzer2;
2341 IDWriteTextAnalyzer1 *analyzer1;
2342 IDWriteTextAnalyzer *analyzer;
2343 FLOAT originx, originy;
2344 DWRITE_MATRIX m;
2345 HRESULT hr;
2346 int i;
2348 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
2349 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2351 hr = IDWriteTextAnalyzer_QueryInterface(analyzer, &IID_IDWriteTextAnalyzer1, (void**)&analyzer1);
2352 IDWriteTextAnalyzer_Release(analyzer);
2353 if (hr != S_OK) {
2354 win_skip("GetGlyphOrientationTransform() is not supported.\n");
2355 return;
2358 /* invalid angle value */
2359 memset(&m, 0xcc, sizeof(m));
2360 hr = IDWriteTextAnalyzer1_GetGlyphOrientationTransform(analyzer1,
2361 DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES + 1, FALSE, &m);
2362 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
2363 ok(m.m11 == 0.0, "got %.2f\n", m.m11);
2365 for (i = 0; i < ARRAY_SIZE(ot_tests); i++) {
2366 memset(&m, 0, sizeof(m));
2367 hr = IDWriteTextAnalyzer1_GetGlyphOrientationTransform(analyzer1, ot_tests[i].angle,
2368 ot_tests[i].is_sideways, &m);
2369 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2370 ok(!memcmp(&ot_tests[i].m, &m, sizeof(m)), "%d: wrong matrix %s\n", i, dbgstr_matrix(&m));
2373 hr = IDWriteTextAnalyzer1_QueryInterface(analyzer1, &IID_IDWriteTextAnalyzer2, (void**)&analyzer2);
2374 IDWriteTextAnalyzer1_Release(analyzer1);
2375 if (hr != S_OK) {
2376 win_skip("IDWriteTextAnalyzer2::GetGlyphOrientationTransform() is not supported.\n");
2377 return;
2380 /* invalid angle value */
2381 memset(&m, 0xcc, sizeof(m));
2382 hr = IDWriteTextAnalyzer2_GetGlyphOrientationTransform(analyzer2,
2383 DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES + 1, FALSE, 0.0, 0.0, &m);
2384 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
2385 ok(m.m11 == 0.0, "got %.2f\n", m.m11);
2387 originx = 50.0;
2388 originy = 60.0;
2389 for (i = 0; i < ARRAY_SIZE(ot_tests); i++) {
2390 DWRITE_GLYPH_ORIENTATION_ANGLE angle = DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES;
2391 DWRITE_MATRIX m_exp;
2393 memset(&m, 0, sizeof(m));
2395 /* zero offset gives same result as a call from IDWriteTextAnalyzer1 */
2396 hr = IDWriteTextAnalyzer2_GetGlyphOrientationTransform(analyzer2, ot_tests[i].angle,
2397 ot_tests[i].is_sideways, 0.0, 0.0, &m);
2398 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2399 ok(!memcmp(&ot_tests[i].m, &m, sizeof(m)), "%d: wrong matrix %s\n", i, dbgstr_matrix(&m));
2401 m_exp = ot_tests[i].m;
2402 hr = IDWriteTextAnalyzer2_GetGlyphOrientationTransform(analyzer2, ot_tests[i].angle,
2403 ot_tests[i].is_sideways, originx, originy, &m);
2404 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2406 /* 90 degrees more for sideways */
2407 if (ot_tests[i].is_sideways) {
2408 switch (ot_tests[i].angle)
2410 case DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES:
2411 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES;
2412 break;
2413 case DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES:
2414 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES;
2415 break;
2416 case DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES:
2417 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES;
2418 break;
2419 case DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES:
2420 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES;
2421 break;
2422 default:
2426 else
2427 angle = ot_tests[i].angle;
2429 /* set expected offsets */
2430 switch (angle)
2432 case DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES:
2433 break;
2434 case DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES:
2435 m_exp.dx = originx + originy;
2436 m_exp.dy = originy - originx;
2437 break;
2438 case DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES:
2439 m_exp.dx = originx + originx;
2440 m_exp.dy = originy + originy;
2441 break;
2442 case DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES:
2443 m_exp.dx = originx - originy;
2444 m_exp.dy = originy + originx;
2445 break;
2446 default:
2450 ok(!memcmp(&m_exp, &m, sizeof(m)), "%d: wrong matrix %s\n", i, dbgstr_matrix(&m));
2453 IDWriteTextAnalyzer2_Release(analyzer2);
2456 static void test_GetBaseline(void)
2458 DWRITE_SCRIPT_ANALYSIS sa = { 0 };
2459 IDWriteTextAnalyzer1 *analyzer1;
2460 IDWriteTextAnalyzer *analyzer;
2461 IDWriteFontFace *fontface;
2462 INT32 baseline;
2463 BOOL exists;
2464 HRESULT hr;
2466 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
2467 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2469 hr = IDWriteTextAnalyzer_QueryInterface(analyzer, &IID_IDWriteTextAnalyzer1, (void**)&analyzer1);
2470 IDWriteTextAnalyzer_Release(analyzer);
2471 if (hr != S_OK) {
2472 win_skip("GetBaseline() is not supported.\n");
2473 return;
2476 fontface = create_fontface();
2478 /* Tahoma does not have a BASE table. */
2480 exists = TRUE;
2481 baseline = 456;
2482 hr = IDWriteTextAnalyzer1_GetBaseline(analyzer1, fontface, DWRITE_BASELINE_DEFAULT, FALSE,
2483 TRUE, sa, NULL, &baseline, &exists);
2484 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2485 ok(!baseline, "Unexpected baseline %d.\n", baseline);
2486 ok(!exists, "Unexpected flag %d.\n", exists);
2488 exists = TRUE;
2489 baseline = 456;
2490 hr = IDWriteTextAnalyzer1_GetBaseline(analyzer1, fontface, DWRITE_BASELINE_DEFAULT, FALSE,
2491 FALSE, sa, NULL, &baseline, &exists);
2492 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2493 ok(!baseline, "Unexpected baseline %d.\n", baseline);
2494 ok(!exists, "Unexpected flag %d.\n", exists);
2496 exists = TRUE;
2497 baseline = 0;
2498 hr = IDWriteTextAnalyzer1_GetBaseline(analyzer1, fontface, DWRITE_BASELINE_CENTRAL, FALSE,
2499 TRUE, sa, NULL, &baseline, &exists);
2500 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2501 ok(baseline != 0, "Unexpected baseline %d.\n", baseline);
2502 ok(!exists, "Unexpected flag %d.\n", exists);
2504 exists = TRUE;
2505 baseline = 0;
2506 hr = IDWriteTextAnalyzer1_GetBaseline(analyzer1, fontface, DWRITE_BASELINE_CENTRAL, FALSE,
2507 FALSE, sa, NULL, &baseline, &exists);
2508 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2509 ok(!baseline, "Unexpected baseline %d.\n", baseline);
2510 ok(!exists, "Unexpected flag %d.\n", exists);
2512 exists = TRUE;
2513 baseline = 456;
2514 hr = IDWriteTextAnalyzer1_GetBaseline(analyzer1, fontface, DWRITE_BASELINE_DEFAULT + 100, FALSE,
2515 TRUE, sa, NULL, &baseline, &exists);
2516 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
2517 ok(!baseline, "Unexpected baseline %d.\n", baseline);
2518 ok(!exists, "Unexpected flag %d.\n", exists);
2520 IDWriteFontFace_Release(fontface);
2521 IDWriteTextAnalyzer1_Release(analyzer1);
2524 static inline BOOL float_eq(FLOAT left, FLOAT right)
2526 int x = *(int *)&left;
2527 int y = *(int *)&right;
2529 if (x < 0)
2530 x = INT_MIN - x;
2531 if (y < 0)
2532 y = INT_MIN - y;
2534 return abs(x - y) <= 8;
2537 static void test_GetGdiCompatibleGlyphPlacements(void)
2539 DWRITE_SHAPING_GLYPH_PROPERTIES glyphprops[1];
2540 DWRITE_SHAPING_TEXT_PROPERTIES textprops[1];
2541 DWRITE_SCRIPT_ANALYSIS sa = { 0 };
2542 IDWriteTextAnalyzer *analyzer;
2543 IDWriteFontFace *fontface;
2544 UINT16 clustermap[1];
2545 HRESULT hr;
2546 UINT32 count;
2547 UINT16 glyphs[1];
2548 FLOAT advance;
2549 DWRITE_GLYPH_OFFSET offsets[1];
2550 DWRITE_FONT_METRICS fontmetrics;
2551 float emsize;
2553 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
2554 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2556 fontface = create_fontface();
2558 IDWriteFontFace_GetMetrics(fontface, &fontmetrics);
2560 count = 0;
2561 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, L"A", 1, fontface, FALSE, FALSE, &sa, NULL, NULL, NULL, NULL, 0, 1,
2562 clustermap, textprops, glyphs, glyphprops, &count);
2563 ok(hr == S_OK, "Failed to get glyphs, hr %#lx.\n", hr);
2564 ok(count == 1, "got %u\n", count);
2566 for (emsize = 12.0f; emsize <= 20.0f; emsize += 1.0f)
2568 FLOAT compatadvance, expected, ppdip;
2569 DWRITE_GLYPH_METRICS metrics;
2571 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, L"A", clustermap, textprops, 1, glyphs, glyphprops,
2572 count, fontface, emsize, FALSE, FALSE, &sa, NULL, NULL, NULL, 0, &advance, offsets);
2573 ok(hr == S_OK, "Failed to get glyph placements, hr %#lx.\n", hr);
2574 ok(advance > 0.0f, "Unexpected advance %f.\n", advance);
2576 /* 1 ppdip, no transform */
2577 ppdip = 1.0;
2578 hr = IDWriteFontFace_GetGdiCompatibleGlyphMetrics(fontface, emsize, ppdip, NULL, FALSE,
2579 glyphs, 1, &metrics, FALSE);
2580 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2582 expected = floorf(metrics.advanceWidth * emsize * ppdip / fontmetrics.designUnitsPerEm + 0.5f) / ppdip;
2583 hr = IDWriteTextAnalyzer_GetGdiCompatibleGlyphPlacements(analyzer, L"A", clustermap, textprops, 1, glyphs,
2584 glyphprops, count, fontface, emsize, ppdip, NULL, FALSE, FALSE, FALSE, &sa, NULL, NULL, NULL, 0,
2585 &compatadvance, offsets);
2586 ok(hr == S_OK, "Failed to get glyph placements, hr %#lx.\n", hr);
2587 ok(compatadvance == expected, "%.0f: got advance %f, expected %f, natural %f\n", emsize,
2588 compatadvance, expected, advance);
2590 /* 1.2 ppdip, no transform */
2591 ppdip = 1.2f;
2592 hr = IDWriteFontFace_GetGdiCompatibleGlyphMetrics(fontface, emsize, ppdip, NULL, FALSE,
2593 glyphs, 1, &metrics, FALSE);
2594 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2596 expected = floorf(metrics.advanceWidth * emsize * ppdip / fontmetrics.designUnitsPerEm + 0.5f) / ppdip;
2597 hr = IDWriteTextAnalyzer_GetGdiCompatibleGlyphPlacements(analyzer, L"A", clustermap, textprops, 1, glyphs,
2598 glyphprops, count, fontface, emsize, ppdip, NULL, FALSE, FALSE, FALSE, &sa, NULL, NULL, NULL, 0,
2599 &compatadvance, offsets);
2600 ok(hr == S_OK, "Failed to get glyph placements, hr %#lx.\n", hr);
2601 ok(float_eq(compatadvance, expected), "%.0f: got advance %f, expected %f, natural %f\n", emsize,
2602 compatadvance, expected, advance);
2605 IDWriteFontFace_Release(fontface);
2606 IDWriteTextAnalyzer_Release(analyzer);
2609 struct bidi_test
2611 const WCHAR text[BIDI_LEVELS_COUNT];
2612 DWRITE_READING_DIRECTION direction;
2613 UINT8 explicit[BIDI_LEVELS_COUNT];
2614 UINT8 resolved[BIDI_LEVELS_COUNT];
2615 BOOL todo;
2618 static const struct bidi_test bidi_tests[] = {
2620 { 0x645, 0x6cc, 0x200c, 0x6a9, 0x646, 0x645, 0 },
2621 DWRITE_READING_DIRECTION_RIGHT_TO_LEFT,
2622 { 1, 1, 1, 1, 1, 1 },
2623 { 1, 1, 1, 1, 1, 1 }
2626 { 0x645, 0x6cc, 0x200c, 0x6a9, 0x646, 0x645, 0 },
2627 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2628 { 0, 0, 0, 0, 0, 0 },
2629 { 1, 1, 1, 1, 1, 1 }
2632 { 0x200c, 0x645, 0x6cc, 0x6a9, 0x646, 0x645, 0 },
2633 DWRITE_READING_DIRECTION_RIGHT_TO_LEFT,
2634 { 1, 1, 1, 1, 1, 1 },
2635 { 1, 1, 1, 1, 1, 1 }
2638 { 0x200c, 0x645, 0x6cc, 0x6a9, 0x646, 0x645, 0 },
2639 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2640 { 0, 0, 0, 0, 0, 0 },
2641 { 0, 1, 1, 1, 1, 1 }
2644 { 'A', 0x200c, 'B', 0 },
2645 DWRITE_READING_DIRECTION_RIGHT_TO_LEFT,
2646 { 1, 1, 1 },
2647 { 2, 2, 2 }
2650 { 'A', 0x200c, 'B', 0 },
2651 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2652 { 0, 0, 0 },
2653 { 0, 0, 0 }
2656 { LRE, PDF, 'a', 'b', 0 },
2657 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2658 { 2, 2, 0, 0 },
2659 { 0, 0, 0, 0 },
2662 { 'a', LRE, PDF, 'b', 0 },
2663 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2664 { 0, 2, 2, 0 },
2665 { 0, 0, 0, 0 },
2668 { RLE, PDF, 'a', 'b', 0 },
2669 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2670 { 1, 1, 0, 0 },
2671 { 0, 0, 0, 0 },
2674 { 'a', RLE, PDF, 'b', 0 },
2675 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2676 { 0, 1, 1, 0 },
2677 { 0, 0, 0, 0 },
2680 { 'a', RLE, PDF, 'b', 0 },
2681 DWRITE_READING_DIRECTION_RIGHT_TO_LEFT,
2682 { 1, 3, 3, 1 },
2683 { 2, 2, 2, 2 },
2686 { LRE, PDF, 'a', 'b', 0 },
2687 DWRITE_READING_DIRECTION_RIGHT_TO_LEFT,
2688 { 2, 2, 1, 1 },
2689 { 1, 1, 2, 2 },
2692 { PDF, 'a', 'b', 0 },
2693 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2694 { 0, 0, 0, 0 },
2695 { 0, 0, 0, 0 }
2698 { LRE, 'a', 'b', PDF, 0 },
2699 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2700 { 2, 2, 2, 2 },
2701 { 0, 2, 2, 2 },
2702 TRUE
2705 { LRI, 'a', 'b', PDI, 0 },
2706 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2707 { 0, 0, 0, 0 },
2708 { 0, 0, 0, 0 },
2709 TRUE
2712 { RLI, 'a', 'b', PDI, 0 },
2713 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2714 { 0, 0, 0, 0 },
2715 { 0, 0, 0, 0 },
2716 TRUE
2719 { 0 }
2723 static void compare_bidi_levels(unsigned int seq, const struct bidi_test *test, UINT32 len, UINT8 *explicit, UINT8 *resolved)
2725 unsigned int i, failcount = 0;
2726 BOOL match;
2728 match = !memcmp(explicit, test->explicit, len);
2729 if (!match) {
2730 if (test->todo) {
2731 failcount++;
2732 todo_wine
2733 ok(0, "test %u: %s wrong explicit levels:\n", seq, wine_dbgstr_w(test->text));
2735 else
2736 ok(0, "test %u: %s wrong explicit levels:\n", seq, wine_dbgstr_w(test->text));
2738 for (i = 0; i < len; i++) {
2739 if (test->explicit[i] != explicit[i]) {
2740 if (test->todo) {
2741 failcount++;
2742 todo_wine
2743 ok(0, "\tat %u, explicit level %u, expected %u\n", i, explicit[i], test->explicit[i]);
2745 else
2746 ok(0, "\tat %u, explicit level %u, expected %u\n", i, explicit[i], test->explicit[i]);
2751 match = !memcmp(resolved, test->resolved, len);
2752 if (!match) {
2753 if (test->todo) {
2754 failcount++;
2755 todo_wine
2756 ok(0, "test %u: %s wrong resolved levels:\n", seq, wine_dbgstr_w(test->text));
2758 else
2759 ok(0, "test %u: %s wrong resolved levels:\n", seq, wine_dbgstr_w(test->text));
2761 for (i = 0; i < len; i++) {
2762 if (test->resolved[i] != resolved[i]) {
2763 if (test->todo) {
2764 failcount++;
2765 todo_wine
2766 ok(0, "\tat %u, resolved level %u, expected %u\n", i, resolved[i], test->resolved[i]);
2768 else
2769 ok(0, "\tat %u, resolved level %u, expected %u\n", i, resolved[i], test->resolved[i]);
2774 todo_wine_if(test->todo && failcount == 0)
2775 ok(1, "test %u: marked as \"todo_wine\" but succeeds\n", seq);
2778 static void test_AnalyzeBidi(void)
2780 const struct bidi_test *ptr = bidi_tests;
2781 IDWriteTextAnalyzer *analyzer;
2782 UINT32 i = 0;
2783 HRESULT hr;
2785 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
2786 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2788 while (*ptr->text)
2790 UINT32 len;
2792 init_textsource(&analysissource, ptr->text, ptr->direction);
2794 len = lstrlenW(ptr->text);
2795 if (len > BIDI_LEVELS_COUNT) {
2796 ok(0, "test %u: increase BIDI_LEVELS_COUNT to at least %u\n", i, len);
2797 i++;
2798 ptr++;
2799 continue;
2802 memset(g_explicit_levels, 0, sizeof(g_explicit_levels));
2803 memset(g_resolved_levels, 0, sizeof(g_resolved_levels));
2804 hr = IDWriteTextAnalyzer_AnalyzeBidi(analyzer, &analysissource.IDWriteTextAnalysisSource_iface, 0,
2805 len, &analysissink);
2806 ok(hr == S_OK, "%u: unexpected hr %#lx.\n", i, hr);
2807 compare_bidi_levels(i, ptr, len, g_explicit_levels, g_resolved_levels);
2809 i++;
2810 ptr++;
2813 IDWriteTextAnalyzer_Release(analyzer);
2816 START_TEST(analyzer)
2818 HRESULT hr;
2820 hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_ISOLATED, &IID_IDWriteFactory, (IUnknown**)&factory);
2821 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2822 if (hr != S_OK)
2824 win_skip("failed to create factory\n");
2825 return;
2828 init_call_sequences(sequences, NUM_CALL_SEQUENCES);
2829 init_call_sequences(expected_seq, 1);
2831 test_AnalyzeScript();
2832 test_AnalyzeLineBreakpoints();
2833 test_AnalyzeBidi();
2834 test_GetScriptProperties();
2835 test_GetTextComplexity();
2836 test_GetGlyphs();
2837 test_numbersubstitution();
2838 test_GetTypographicFeatures();
2839 test_GetGlyphPlacements();
2840 test_ApplyCharacterSpacing();
2841 test_GetGlyphOrientationTransform();
2842 test_GetBaseline();
2843 test_GetGdiCompatibleGlyphPlacements();
2845 IDWriteFactory_Release(factory);