include/mscvpdb.h: Use flexible array members for the rest of structures.
[wine.git] / dlls / dwrite / tests / analyzer.c
blob782be8bfb52ec78abb7be23a50b985650ce3f3db
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"
32 #include "usp10.h"
34 #include "wine/test.h"
36 static IDWriteFactory *factory;
38 static void * create_text_analyzer(REFIID riid)
40 IDWriteTextAnalyzer *analyzer;
41 void *ret = NULL;
43 if (SUCCEEDED(IDWriteFactory_CreateTextAnalyzer(factory, &analyzer)))
45 IDWriteTextAnalyzer_QueryInterface(analyzer, riid, &ret);
46 IDWriteTextAnalyzer_Release(analyzer);
49 return ret;
52 #define LRE 0x202a
53 #define RLE 0x202b
54 #define PDF 0x202c
55 #define LRO 0x202d
56 #define RLO 0x202e
57 #define LRI 0x2066
58 #define RLI 0x2067
59 #define FSI 0x2068
60 #define PDI 0x2069
62 #define MS_GDEF_TAG DWRITE_MAKE_OPENTYPE_TAG('G','D','E','F')
64 #ifdef WORDS_BIGENDIAN
65 #define GET_BE_WORD(x) (x)
66 #define GET_BE_DWORD(x) (x)
67 #define GET_LE_WORD(x) RtlUshortByteSwap(x)
68 #define GET_LE_DWORD(x) RtlUlongByteSwap(x)
69 #else
70 #define GET_BE_WORD(x) RtlUshortByteSwap(x)
71 #define GET_BE_DWORD(x) RtlUlongByteSwap(x)
72 #define GET_LE_WORD(x) (x)
73 #define GET_LE_DWORD(x) (x)
74 #endif
76 struct ot_gdef_classdef_format1
78 WORD format;
79 WORD start_glyph;
80 WORD glyph_count;
81 WORD classes[1];
84 struct ot_gdef_class_range
86 WORD start_glyph;
87 WORD end_glyph;
88 WORD glyph_class;
91 struct ot_gdef_classdef_format2
93 WORD format;
94 WORD range_count;
95 struct ot_gdef_class_range ranges[1];
98 enum analysis_kind {
99 ScriptAnalysis,
100 LastKind
103 static const char *get_analysis_kind_name(enum analysis_kind kind)
105 switch (kind)
107 case ScriptAnalysis:
108 return "ScriptAnalysis";
109 default:
110 return "unknown";
114 struct script_analysis {
115 UINT32 pos;
116 UINT32 len;
117 DWRITE_SCRIPT_SHAPES shapes;
120 struct call_entry {
121 enum analysis_kind kind;
122 struct script_analysis sa;
125 struct testcontext {
126 enum analysis_kind kind;
127 BOOL todo;
128 int *failcount;
129 const char *file;
130 int line;
133 struct call_sequence
135 int count;
136 int size;
137 struct call_entry *sequence;
140 #define NUM_CALL_SEQUENCES 1
141 #define ANALYZER_ID 0
142 static struct call_sequence *sequences[NUM_CALL_SEQUENCES];
143 static struct call_sequence *expected_seq[1];
145 static void add_call(struct call_sequence **seq, int sequence_index, const struct call_entry *call)
147 struct call_sequence *call_seq = seq[sequence_index];
149 if (!call_seq->sequence)
151 call_seq->size = 10;
152 call_seq->sequence = malloc(call_seq->size * sizeof(*call_seq->sequence));
155 if (call_seq->count == call_seq->size)
157 call_seq->size *= 2;
158 call_seq->sequence = realloc(call_seq->sequence, call_seq->size * sizeof(*call_seq->sequence));
161 assert(call_seq->sequence);
163 call_seq->sequence[call_seq->count++] = *call;
166 static inline void flush_sequence(struct call_sequence **seg, int sequence_index)
168 struct call_sequence *call_seq = seg[sequence_index];
170 free(call_seq->sequence);
171 call_seq->sequence = NULL;
172 call_seq->count = call_seq->size = 0;
175 static void init_call_sequences(struct call_sequence **seq, int n)
177 int i;
179 for (i = 0; i < n; i++)
180 seq[i] = calloc(1, sizeof(*seq[i]));
183 static void test_uint(UINT32 actual, UINT32 expected, const char *name, const struct testcontext *ctxt)
185 if (expected != actual && ctxt->todo)
187 (*ctxt->failcount)++;
188 ok_(ctxt->file, ctxt->line) (0, "%s: \"%s\" expecting %u, got %u\n", get_analysis_kind_name(ctxt->kind), name, expected, actual);
190 else
191 ok_(ctxt->file, ctxt->line) (expected == actual, "%s: \"%s\" expecting %u, got %u\n", get_analysis_kind_name(ctxt->kind), name,
192 expected, actual);
195 static void ok_sequence_(struct call_sequence **seq, int sequence_index,
196 const struct call_entry *expected, const char *context, BOOL todo,
197 const char *file, int line)
199 struct call_sequence *call_seq = seq[sequence_index];
200 static const struct call_entry end_of_sequence = { LastKind };
201 const struct call_entry *actual, *sequence;
202 int failcount = 0;
203 struct testcontext ctxt;
205 add_call(seq, sequence_index, &end_of_sequence);
207 sequence = call_seq->sequence;
208 actual = sequence;
210 ctxt.failcount = &failcount;
211 ctxt.todo = todo;
212 ctxt.file = file;
213 ctxt.line = line;
215 while (expected->kind != LastKind && actual->kind != LastKind)
217 if (expected->kind == actual->kind)
219 ctxt.kind = expected->kind;
221 switch (actual->kind)
223 case ScriptAnalysis:
224 test_uint(actual->sa.pos, expected->sa.pos, "position", &ctxt);
225 test_uint(actual->sa.len, expected->sa.len, "length", &ctxt);
226 test_uint(actual->sa.shapes, expected->sa.shapes, "shapes", &ctxt);
227 break;
228 default:
229 ok(0, "%s: callback not handled, %s\n", context, get_analysis_kind_name(actual->kind));
231 expected++;
232 actual++;
234 else if (todo)
236 failcount++;
237 todo_wine
239 ok_(file, line) (0, "%s: call %s was expected, but got call %s instead\n",
240 context, get_analysis_kind_name(expected->kind), get_analysis_kind_name(actual->kind));
243 flush_sequence(seq, sequence_index);
244 return;
246 else
248 ok_(file, line) (0, "%s: call %s was expected, but got call %s instead\n",
249 context, get_analysis_kind_name(expected->kind), get_analysis_kind_name(actual->kind));
250 expected++;
251 actual++;
255 if (todo)
257 todo_wine
259 if (expected->kind != LastKind || actual->kind != LastKind)
261 failcount++;
262 ok_(file, line) (0, "%s: the call sequence is not complete: expected %s - actual %s\n",
263 context, get_analysis_kind_name(expected->kind), get_analysis_kind_name(actual->kind));
267 else if (expected->kind != LastKind || actual->kind != LastKind)
269 ok_(file, line) (0, "%s: the call sequence is not complete: expected %s - actual %s\n",
270 context, get_analysis_kind_name(expected->kind), get_analysis_kind_name(actual->kind));
273 if (todo && !failcount) /* succeeded yet marked todo */
275 todo_wine
277 ok_(file, line)(1, "%s: marked \"todo_wine\" but succeeds\n", context);
281 flush_sequence(seq, sequence_index);
284 #define ok_sequence(seq, index, exp, contx, todo) \
285 ok_sequence_(seq, index, (exp), (contx), (todo), __FILE__, __LINE__)
287 static HRESULT WINAPI analysissink_QueryInterface(IDWriteTextAnalysisSink *iface, REFIID riid, void **obj)
289 if (IsEqualIID(riid, &IID_IDWriteTextAnalysisSink) || IsEqualIID(riid, &IID_IUnknown))
291 *obj = iface;
292 return S_OK;
295 *obj = NULL;
296 return E_NOINTERFACE;
299 static ULONG WINAPI analysissink_AddRef(IDWriteTextAnalysisSink *iface)
301 return 2;
304 static ULONG WINAPI analysissink_Release(IDWriteTextAnalysisSink *iface)
306 return 1;
309 static HRESULT WINAPI analysissink_SetScriptAnalysis(IDWriteTextAnalysisSink *iface,
310 UINT32 position, UINT32 length, DWRITE_SCRIPT_ANALYSIS const* sa)
312 struct call_entry entry;
313 entry.kind = ScriptAnalysis;
314 entry.sa.pos = position;
315 entry.sa.len = length;
316 entry.sa.shapes = sa->shapes;
317 add_call(sequences, ANALYZER_ID, &entry);
318 return S_OK;
321 static DWRITE_SCRIPT_ANALYSIS g_sa;
322 static HRESULT WINAPI analysissink_SetScriptAnalysis2(IDWriteTextAnalysisSink *iface,
323 UINT32 position, UINT32 length, DWRITE_SCRIPT_ANALYSIS const* sa)
325 g_sa = *sa;
326 return S_OK;
329 #define BREAKPOINT_COUNT 20
330 static DWRITE_LINE_BREAKPOINT g_actual_bp[BREAKPOINT_COUNT];
332 static HRESULT WINAPI analysissink_SetLineBreakpoints(IDWriteTextAnalysisSink *iface,
333 UINT32 position,
334 UINT32 length,
335 DWRITE_LINE_BREAKPOINT const* breakpoints)
337 if (position + length > BREAKPOINT_COUNT) {
338 ok(0, "SetLineBreakpoints: reported pos=%u, len=%u overflows expected length %d\n", position, length, BREAKPOINT_COUNT);
339 return E_FAIL;
341 memcpy(&g_actual_bp[position], breakpoints, length*sizeof(DWRITE_LINE_BREAKPOINT));
342 return S_OK;
345 #define BIDI_LEVELS_COUNT 10
346 static UINT8 g_explicit_levels[BIDI_LEVELS_COUNT];
347 static UINT8 g_resolved_levels[BIDI_LEVELS_COUNT];
348 static HRESULT WINAPI analysissink_SetBidiLevel(IDWriteTextAnalysisSink *iface,
349 UINT32 position,
350 UINT32 length,
351 UINT8 explicitLevel,
352 UINT8 resolvedLevel)
354 if (position + length > BIDI_LEVELS_COUNT) {
355 ok(0, "SetBidiLevel: reported pos=%u, len=%u overflows expected length %d\n", position, length, BIDI_LEVELS_COUNT);
356 return E_FAIL;
358 memset(g_explicit_levels + position, explicitLevel, length);
359 memset(g_resolved_levels + position, resolvedLevel, length);
360 return S_OK;
363 static HRESULT WINAPI analysissink_SetNumberSubstitution(IDWriteTextAnalysisSink *iface,
364 UINT32 position,
365 UINT32 length,
366 IDWriteNumberSubstitution* substitution)
368 ok(0, "unexpected\n");
369 return E_NOTIMPL;
372 static IDWriteTextAnalysisSinkVtbl analysissinkvtbl = {
373 analysissink_QueryInterface,
374 analysissink_AddRef,
375 analysissink_Release,
376 analysissink_SetScriptAnalysis,
377 analysissink_SetLineBreakpoints,
378 analysissink_SetBidiLevel,
379 analysissink_SetNumberSubstitution
382 static IDWriteTextAnalysisSinkVtbl analysissinkvtbl2 = {
383 analysissink_QueryInterface,
384 analysissink_AddRef,
385 analysissink_Release,
386 analysissink_SetScriptAnalysis2,
387 analysissink_SetLineBreakpoints,
388 analysissink_SetBidiLevel,
389 analysissink_SetNumberSubstitution
392 static IDWriteTextAnalysisSink analysissink = { &analysissinkvtbl };
393 static IDWriteTextAnalysisSink analysissink2 = { &analysissinkvtbl2 };
395 static HRESULT WINAPI analysissource_QueryInterface(IDWriteTextAnalysisSource *iface,
396 REFIID riid, void **obj)
398 ok(0, "QueryInterface not expected\n");
399 return E_NOTIMPL;
402 static ULONG WINAPI analysissource_AddRef(IDWriteTextAnalysisSource *iface)
404 ok(0, "AddRef not expected\n");
405 return 2;
408 static ULONG WINAPI analysissource_Release(IDWriteTextAnalysisSource *iface)
410 ok(0, "Release not expected\n");
411 return 1;
414 struct testanalysissource
416 IDWriteTextAnalysisSource IDWriteTextAnalysisSource_iface;
417 const WCHAR *text;
418 UINT32 text_length;
419 DWRITE_READING_DIRECTION direction;
422 static void init_textsource(struct testanalysissource *source, const WCHAR *text,
423 INT text_length, DWRITE_READING_DIRECTION direction)
425 source->text = text;
426 source->text_length = text_length == -1 ? lstrlenW(text) : text_length;
427 source->direction = direction;
430 static inline struct testanalysissource *impl_from_IDWriteTextAnalysisSource(IDWriteTextAnalysisSource *iface)
432 return CONTAINING_RECORD(iface, struct testanalysissource, IDWriteTextAnalysisSource_iface);
435 static HRESULT WINAPI analysissource_GetTextAtPosition(IDWriteTextAnalysisSource *iface,
436 UINT32 position, WCHAR const** text, UINT32* text_len)
438 struct testanalysissource *source = impl_from_IDWriteTextAnalysisSource(iface);
440 if (position >= source->text_length)
442 *text = NULL;
443 *text_len = 0;
445 else
447 *text = source->text + position;
448 *text_len = source->text_length - position;
451 return S_OK;
454 static HRESULT WINAPI analysissource_GetTextBeforePosition(IDWriteTextAnalysisSource *iface,
455 UINT32 position, WCHAR const** text, UINT32* text_len)
457 ok(0, "unexpected\n");
458 return E_NOTIMPL;
461 static DWRITE_READING_DIRECTION WINAPI analysissource_GetParagraphReadingDirection(
462 IDWriteTextAnalysisSource *iface)
464 struct testanalysissource *source = impl_from_IDWriteTextAnalysisSource(iface);
465 return source->direction;
468 static HRESULT WINAPI analysissource_GetLocaleName(IDWriteTextAnalysisSource *iface,
469 UINT32 position, UINT32* text_len, WCHAR const** locale)
471 *locale = NULL;
472 *text_len = 0;
473 return S_OK;
476 static HRESULT WINAPI analysissource_GetNumberSubstitution(IDWriteTextAnalysisSource *iface,
477 UINT32 position, UINT32* text_len, IDWriteNumberSubstitution **substitution)
479 ok(0, "unexpected\n");
480 return E_NOTIMPL;
483 static IDWriteTextAnalysisSourceVtbl analysissourcevtbl = {
484 analysissource_QueryInterface,
485 analysissource_AddRef,
486 analysissource_Release,
487 analysissource_GetTextAtPosition,
488 analysissource_GetTextBeforePosition,
489 analysissource_GetParagraphReadingDirection,
490 analysissource_GetLocaleName,
491 analysissource_GetNumberSubstitution
494 static struct testanalysissource analysissource = { { &analysissourcevtbl } };
496 static IDWriteFontFace *create_fontface(void)
498 IDWriteGdiInterop *interop;
499 IDWriteFontFace *fontface;
500 IDWriteFont *font;
501 LOGFONTW logfont;
502 HRESULT hr;
504 hr = IDWriteFactory_GetGdiInterop(factory, &interop);
505 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
507 memset(&logfont, 0, sizeof(logfont));
508 logfont.lfHeight = 12;
509 logfont.lfWidth = 12;
510 logfont.lfWeight = FW_NORMAL;
511 logfont.lfItalic = 1;
512 lstrcpyW(logfont.lfFaceName, L"Tahoma");
514 hr = IDWriteGdiInterop_CreateFontFromLOGFONT(interop, &logfont, &font);
515 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
517 hr = IDWriteFont_CreateFontFace(font, &fontface);
518 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
520 IDWriteFont_Release(font);
521 IDWriteGdiInterop_Release(interop);
523 return fontface;
526 static WCHAR *create_testfontfile(const WCHAR *filename)
528 static WCHAR pathW[MAX_PATH];
529 DWORD written;
530 HANDLE file;
531 HRSRC res;
532 void *ptr;
534 GetTempPathW(ARRAY_SIZE(pathW), pathW);
535 lstrcatW(pathW, filename);
537 file = CreateFileW(pathW, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0);
538 ok(file != INVALID_HANDLE_VALUE, "file creation failed, at %s, error %ld\n", wine_dbgstr_w(pathW),
539 GetLastError());
541 res = FindResourceA(GetModuleHandleA(NULL), (LPCSTR)MAKEINTRESOURCE(1), (LPCSTR)RT_RCDATA);
542 ok(res != 0, "couldn't find resource\n");
543 ptr = LockResource(LoadResource(GetModuleHandleA(NULL), res));
544 WriteFile(file, ptr, SizeofResource(GetModuleHandleA(NULL), res), &written, NULL);
545 ok(written == SizeofResource(GetModuleHandleA(NULL), res), "couldn't write resource\n");
546 CloseHandle(file);
548 return pathW;
551 #define DELETE_FONTFILE(filename) _delete_testfontfile(filename, __LINE__)
552 static void _delete_testfontfile(const WCHAR *filename, int line)
554 BOOL ret = DeleteFileW(filename);
555 ok_(__FILE__,line)(ret, "failed to delete file %s, error %ld\n", wine_dbgstr_w(filename), GetLastError());
558 static IDWriteFontFace *create_testfontface(const WCHAR *filename)
560 IDWriteFontFace *face;
561 IDWriteFontFile *file;
562 HRESULT hr;
564 hr = IDWriteFactory_CreateFontFileReference(factory, filename, NULL, &file);
565 ok(hr == S_OK, "Unexpected hr %#lx.\n",hr);
567 hr = IDWriteFactory_CreateFontFace(factory, DWRITE_FONT_FACE_TYPE_TRUETYPE, 1, &file, 0,
568 DWRITE_FONT_SIMULATIONS_NONE, &face);
569 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
570 IDWriteFontFile_Release(file);
572 return face;
575 struct sa_test {
576 const WCHAR string[50];
577 int str_len;
578 int item_count;
579 struct script_analysis sa[10];
582 static struct sa_test sa_tests[] = {
584 /* just 1 char string */
585 {'t',0}, -1, 1,
586 { { 0, 1, DWRITE_SCRIPT_SHAPES_DEFAULT }}
589 {'t','e','s','t',0}, -1, 1,
590 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT }}
593 {' ',' ',' ',' ','!','$','[','^','{','~',0}, -1, 1,
594 { { 0, 10, DWRITE_SCRIPT_SHAPES_DEFAULT }}
597 {' ',' ',' ','1','2',' ',0}, -1, 1,
598 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
601 /* digits only */
602 {'1','2',0}, -1, 1,
603 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }}
606 /* Arabic */
607 {0x064a,0x064f,0x0633,0x0627,0x0648,0x0650,0x064a,0x0661,0}, -1, 1,
608 { { 0, 8, DWRITE_SCRIPT_SHAPES_DEFAULT }}
611 /* Arabic */
612 {0x0627,0x0644,0x0635,0x0651,0x0650,0x062d,0x0629,0x064f,' ',0x062a,0x064e,
613 0x0627,0x062c,0x064c,' ',0x0639,0x064e,0x0644,0x0649,' ',
614 0x0631,0x064f,0x0624,0x0648,0x0633,0x0650,' ',0x0627,0x0644,
615 0x0623,0x0635,0x0650,0x062d,0x0651,0x064e,0x0627,0x0621,0x0650,0x06f0,0x06f5,0}, -1, 1,
616 { { 0, 40, DWRITE_SCRIPT_SHAPES_DEFAULT }}
619 /* Arabic, Latin */
620 {'1','2','3','-','5','2',0x064a,0x064f,0x0633,0x0627,0x0648,0x0650,0x064a,'7','1','.',0}, -1, 1,
621 { { 0, 16, DWRITE_SCRIPT_SHAPES_DEFAULT }}
624 /* Arabic, English */
625 {'A','B','C','-','D','E','F',' ',0x0621,0x0623,0x0624,0}, -1, 2,
626 { { 0, 8, DWRITE_SCRIPT_SHAPES_DEFAULT },
627 { 8, 3, DWRITE_SCRIPT_SHAPES_DEFAULT },
631 /* leading space, Arabic, English */
632 {' ',0x0621,0x0623,0x0624,'A','B','C','-','D','E','F',0}, -1, 2,
633 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT },
634 { 4, 7, DWRITE_SCRIPT_SHAPES_DEFAULT },
638 /* English, Arabic, trailing space */
639 {'A','B','C','-','D','E','F',0x0621,0x0623,0x0624,' ',0}, -1, 2,
640 { { 0, 7, DWRITE_SCRIPT_SHAPES_DEFAULT },
641 { 7, 4, DWRITE_SCRIPT_SHAPES_DEFAULT },
645 /* C1 Controls, Latin-1 Supplement */
646 {0x80,0x90,0x9f,0xa0,0xc0,0xb8,0xbf,0xc0,0xff,0}, -1, 2,
647 { { 0, 3, DWRITE_SCRIPT_SHAPES_NO_VISUAL },
648 { 3, 6, DWRITE_SCRIPT_SHAPES_DEFAULT },
652 /* Latin Extended-A */
653 {0x100,0x120,0x130,0x140,0x150,0x160,0x170,0x17f,0}, -1, 1,
654 { { 0, 8, DWRITE_SCRIPT_SHAPES_DEFAULT }}
657 /* Latin Extended-B */
658 {0x180,0x190,0x1bf,0x1c0,0x1c3,0x1c4,0x1cc,0x1dc,0x1ff,0x217,0x21b,0x24f,0}, -1, 1,
659 { { 0, 12, DWRITE_SCRIPT_SHAPES_DEFAULT }}
662 /* IPA Extensions */
663 {0x250,0x260,0x270,0x290,0x2af,0}, -1, 1,
664 { { 0, 5, DWRITE_SCRIPT_SHAPES_DEFAULT }}
667 /* Spacing Modifier Letters */
668 {0x2b0,0x2ba,0x2d7,0x2dd,0x2ef,0x2ff,0}, -1, 1,
669 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
672 /* Combining Diacritical Marks */
673 {0x300,0x320,0x340,0x345,0x350,0x36f,0}, -1, 1,
674 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
677 /* Greek and Coptic */
678 {0x370,0x388,0x3d8,0x3e1,0x3e2,0x3fa,0x3ff,0}, -1, 3,
679 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT },
680 { 4, 1, DWRITE_SCRIPT_SHAPES_DEFAULT },
681 { 5, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }
685 /* Cyrillic and Cyrillic Supplement */
686 {0x400,0x40f,0x410,0x44f,0x450,0x45f,0x460,0x481,0x48a,0x4f0,0x4fa,0x4ff,0x500,0x510,0x520,0}, -1, 1,
687 { { 0, 15, DWRITE_SCRIPT_SHAPES_DEFAULT }}
690 /* Armenian */
691 {0x531,0x540,0x559,0x55f,0x570,0x589,0x58a,0}, -1, 1,
692 { { 0, 7, DWRITE_SCRIPT_SHAPES_DEFAULT }}
695 /* Hebrew */
696 {0x5e9,0x5dc,0x5d5,0x5dd,0}, -1, 1,
697 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT }}
700 /* Latin, Hebrew, Latin */
701 {'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}, -1, 3,
702 { { 0, 9, DWRITE_SCRIPT_SHAPES_DEFAULT },
703 { 9, 10, DWRITE_SCRIPT_SHAPES_DEFAULT },
704 { 19, 10, DWRITE_SCRIPT_SHAPES_DEFAULT }}
707 /* Syriac */
708 {0x710,0x712,0x712,0x714,'.',0}, -1, 1,
709 { { 0, 5, DWRITE_SCRIPT_SHAPES_DEFAULT }}
712 /* Arabic Supplement */
713 {0x750,0x760,0x76d,'.',0}, -1, 1,
714 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT }}
717 /* Thaana */
718 {0x780,0x78e,0x798,0x7a6,0x7b0,'.',0}, -1, 1,
719 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
722 /* N'Ko */
723 {0x7c0,0x7ca,0x7e8,0x7eb,0x7f6,'.',0}, -1, 1,
724 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
727 /* Thaana */
728 {0x780,0x798,0x7a5,0x7a6,0x7b0,'.',0}, -1, 1,
729 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
732 /* Devanagari */
733 {0x926,0x947,0x935,0x928,0x93e,0x917,0x930,0x940,'.',0}, -1, 1,
734 { { 0, 9, DWRITE_SCRIPT_SHAPES_DEFAULT }}
737 /* Bengali */
738 {0x9ac,0x9be,0x982,0x9b2,0x9be,'.',0}, -1, 1,
739 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
742 /* Gurmukhi */
743 {0xa17,0xa41,0xa30,0xa2e,0xa41,0xa16,0xa40,'.',0}, -1, 1,
744 { { 0, 8, DWRITE_SCRIPT_SHAPES_DEFAULT }}
747 /* Gujarati */
748 {0xa97,0xac1,0xa9c,0xab0,0xabe,0xaa4,0xac0,'.',0}, -1, 1,
749 { { 0, 8, DWRITE_SCRIPT_SHAPES_DEFAULT }}
752 /* Oriya */
753 {0xb13,0xb21,0xb3c,0xb3f,0xb06,'.',0}, -1, 1,
754 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
757 /* Tamil */
758 {0xba4,0xbae,0xbbf,0xbb4,0xbcd,'.',0}, -1, 1,
759 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
762 /* Telugu */
763 {0xc24,0xc46,0xc32,0xc41,0xc17,0xc41,'.',0}, -1, 1,
764 { { 0, 7, DWRITE_SCRIPT_SHAPES_DEFAULT }}
767 /* Kannada */
768 {0xc95,0xca8,0xccd,0xca8,0xca1,'.',0}, -1, 1,
769 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
772 /* Malayalam */
773 {0xd2e,0xd32,0xd2f,0xd3e,0xd33,0xd02,'.',0}, -1, 1,
774 { { 0, 7, DWRITE_SCRIPT_SHAPES_DEFAULT }}
777 /* Sinhala */
778 {0xd82,0xd85,0xd9a,0xdcf,'.',0}, -1, 1,
779 { { 0, 5, DWRITE_SCRIPT_SHAPES_DEFAULT }}
782 /* Thai */
783 {0x0e04,0x0e27,0x0e32,0x0e21,0x0e1e,0x0e22,0x0e32,0x0e22,0x0e32,0x0e21,
784 0x0e2d,0x0e22,0x0e39,0x0e48,0x0e17,0x0e35,0x0e48,0x0e44,0x0e2b,0x0e19,
785 0x0e04,0x0e27,0x0e32,0x0e21,0x0e2a, 0x0e33,0x0e40,0x0e23,0x0e47,0x0e08,
786 0x0e2d,0x0e22,0x0e39,0x0e48,0x0e17,0x0e35,0x0e48,0x0e19,0x0e31,0x0e48,0x0e19,'.',0}, -1, 1,
787 { { 0, 42, DWRITE_SCRIPT_SHAPES_DEFAULT }}
790 /* Lao */
791 {0xead,0xeb1,0xe81,0xeaa,0xead,0xe99,0xea5,0xeb2,0xea7,'.',0}, -1, 1,
792 { { 0, 10, DWRITE_SCRIPT_SHAPES_DEFAULT }}
795 /* Tibetan */
796 {0xf04,0xf05,0xf0e,0x020,0xf51,0xf7c,0xf53,0xf0b,0xf5a,0xf53,0xf0b,
797 0xf51,0xf44,0xf0b,0xf54,0xf7c,0xf0d,'.',0}, -1, 1,
798 { { 0, 18, DWRITE_SCRIPT_SHAPES_DEFAULT }}
801 /* Myanmar */
802 {0x1019,0x103c,0x1014,0x103a,0x1019,0x102c,0x1021,0x1000,0x1039,0x1001,0x101b,0x102c,'.',0}, -1, 1,
803 { { 0, 13, DWRITE_SCRIPT_SHAPES_DEFAULT }}
806 /* Georgian */
807 {0x10a0,0x10d0,0x10da,0x10f1,0x10fb,0x2d00,'.',0}, -1, 1,
808 { { 0, 7, DWRITE_SCRIPT_SHAPES_DEFAULT }}
811 /* Hangul */
812 {0x1100,0x1110,0x1160,0x1170,0x11a8,'.',0}, -1, 1,
813 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
816 /* Ethiopic */
817 {0x130d,0x12d5,0x12dd,0}, -1, 1,
818 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
821 /* Cherokee */
822 {0x13e3,0x13b3,0x13a9,0x0020,0x13a6,0x13ec,0x13c2,0x13af,0x13cd,0x13d7,0}, -1, 1,
823 { { 0, 10, DWRITE_SCRIPT_SHAPES_DEFAULT }}
826 /* Canadian */
827 {0x1403,0x14c4,0x1483,0x144e,0x1450,0x1466,0}, -1, 1,
828 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
831 /* Ogham */
832 {0x169b,0x1691,0x168c,0x1690,0x168b,0x169c,0}, -1, 1,
833 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
836 /* Runic */
837 {0x16a0,0x16a1,0x16a2,0x16a3,0x16a4,0x16a5,0}, -1, 1,
838 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
841 /* Khmer */
842 {0x1781,0x17c1,0x1798,0x179a,0x1797,0x17b6,0x179f,0x17b6,0x19e0,0}, -1, 1,
843 { { 0, 9, DWRITE_SCRIPT_SHAPES_DEFAULT }}
846 /* Mongolian */
847 {0x182e,0x1823,0x1829,0x182d,0x1823,0x182f,0x0020,0x182a,0x1822,0x1834,0x1822,0x182d,0x180c,0}, -1, 1,
848 { { 0, 13, DWRITE_SCRIPT_SHAPES_DEFAULT }}
851 /* Limbu */
852 {0x1900,0x1910,0x1920,0x1930,0}, -1, 1,
853 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT }}
856 /* Tai Le */
857 {0x1956,0x196d,0x1970,0x1956,0x196c,0x1973,0x1951,0x1968,0x1952,0x1970,0}, -1, 1,
858 { { 0, 10, DWRITE_SCRIPT_SHAPES_DEFAULT }}
861 /* New Tai Lue */
862 {0x1992,0x19c4,0}, -1, 1,
863 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }}
866 /* Buginese */
867 {0x1a00,0x1a10,0}, -1, 1,
868 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }}
871 /* Tai Tham */
872 {0x1a20,0x1a40,0x1a50,0}, -1, 1,
873 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
876 /* Balinese */
877 {0x1b00,0x1b05,0x1b20,0}, -1, 1,
878 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
881 /* Sundanese */
882 {0x1b80,0x1b85,0x1ba0,0}, -1, 1,
883 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
886 /* Batak */
887 {0x1bc0,0x1be5,0x1bfc,0}, -1, 1,
888 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
891 /* Lepcha */
892 {0x1c00,0x1c20,0x1c40,0}, -1, 1,
893 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
896 /* Ol Chiki */
897 {0x1c50,0x1c5a,0x1c77,0}, -1, 1,
898 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
901 /* Sundanese Supplement */
902 {0x1cc0,0x1cc5,0x1cc7,0}, -1, 1,
903 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
906 /* Phonetic Extensions */
907 {0x1d00,0x1d40,0x1d70,0}, -1, 1,
908 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
911 /* Combining diacritical marks */
912 {0x1dc0,0x300,0x1ddf,0}, -1, 1,
913 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
916 /* Latin Extended Additional, Extended-C */
917 {0x1e00,0x1d00,0x2c60,0}, -1, 1,
918 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
921 /* Greek Extended */
922 {0x3f0,0x1f00,0}, -1, 1,
923 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }}
926 /* General Punctuation */
927 {0x1dc0,0x2000,0}, -1, 1,
928 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }}
931 /* Superscripts and Subscripts */
932 {0x2070,0x2086,0x2000,0}, -1, 1,
933 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
936 /* Currency, Combining Diacritical Marks for Symbols. Letterlike Symbols.. */
937 {0x20a0,0x20b8,0x2000,0x20d0,0x2100,0x2150,0x2190,0x2200,0x2300,0x2400,0x2440,0x2460,0x2500,0x2580,0x25a0,0x2600,
938 0x2700,0x27c0,0x27f0,0x2900,0x2980,0x2a00,0x2b00,0}, -1, 1,
939 { { 0, 23, DWRITE_SCRIPT_SHAPES_DEFAULT }}
942 /* Braille */
943 {0x2800,0x2070,0x2000,0}, -1, 1,
944 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
947 /* Glagolitic */
948 {0x2c00,0x2c12,0}, -1, 1,
949 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }}
952 /* Coptic */
953 {0x2c80,0x3e2,0x1f00,0}, -1, 2,
954 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT },
955 { 2, 1, DWRITE_SCRIPT_SHAPES_DEFAULT } }
958 /* Tifinagh */
959 {0x2d30,0x2d4a,0}, -1, 1,
960 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }}
963 /* LRE/PDF */
964 {LRE,PDF,'a','b','c','\r',0}, -1, 3,
965 { { 0, 2, DWRITE_SCRIPT_SHAPES_NO_VISUAL },
966 { 2, 3, DWRITE_SCRIPT_SHAPES_DEFAULT },
967 { 5, 1, DWRITE_SCRIPT_SHAPES_NO_VISUAL } }
970 /* LRE/PDF and other visual and non-visual codes from Common script range */
971 {LRE,PDF,'r','!',0x200b,'\r',0}, -1, 3,
972 { { 0, 2, DWRITE_SCRIPT_SHAPES_NO_VISUAL },
973 { 2, 2, DWRITE_SCRIPT_SHAPES_DEFAULT },
974 { 4, 2, DWRITE_SCRIPT_SHAPES_NO_VISUAL } }
977 /* Inherited on its own */
978 {0x300,0x300,0}, -1, 1,
979 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT } }
982 /* Inherited followed by Latin */
983 {0x300,0x300,'a',0}, -1, 1,
984 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT } }
987 /* Inherited mixed with Arabic and Latin */
988 {0x300,'+',0x627,0x300,'a',0}, -1, 2,
989 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT },
990 { 4, 1, DWRITE_SCRIPT_SHAPES_DEFAULT } }
993 {'a',0x300,'+',0x627,0x300,')','a',0}, -1, 3,
994 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT },
995 { 3, 3, DWRITE_SCRIPT_SHAPES_DEFAULT },
996 { 6, 1, DWRITE_SCRIPT_SHAPES_DEFAULT } }
998 /* Paired punctuation */
1000 {0x627,'(','a',')','a',0}, -1, 2,
1001 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT },
1002 { 2, 3, DWRITE_SCRIPT_SHAPES_DEFAULT } }
1005 {0x627,'[','a',']',0x627,0}, -1, 3,
1006 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT },
1007 { 2, 2, DWRITE_SCRIPT_SHAPES_DEFAULT },
1008 { 4, 1, DWRITE_SCRIPT_SHAPES_DEFAULT } }
1010 /* Combining marks */
1012 /* dotted circle - Common, followed by accent - Inherited */
1013 {0x25cc,0x300,0}, -1, 1,
1014 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT } }
1017 /* combining mark with explicit script value */
1018 {0x25cc,0x300,0x5c4,0}, -1, 1,
1019 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT } }
1022 /* inherited merges with following explicit script */
1023 {0x25cc,0x300,'a',0}, -1, 1,
1024 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT } }
1027 /* TAKRI LETTER A U+11680 */
1028 {0xd805,0xde80,0}, -1, 1,
1029 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT } }
1032 /* Musical symbols, U+1D173 */
1033 {0xd834,0xdd73,0}, -1, 1,
1034 { { 0, 2, DWRITE_SCRIPT_SHAPES_NO_VISUAL } }
1037 /* Tags, U+E0020 */
1038 {0xdb40,0xdc20,0}, -1, 1,
1039 { { 0, 2, DWRITE_SCRIPT_SHAPES_NO_VISUAL } }
1042 /* Null at start of string */
1043 L"\0test", 5, 2,
1044 { { 0, 1, DWRITE_SCRIPT_SHAPES_NO_VISUAL },
1045 { 1, 4, DWRITE_SCRIPT_SHAPES_DEFAULT } }
1048 /* Null embedded in string */
1049 L"te\0st", 5, 3,
1050 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT },
1051 { 2, 1, DWRITE_SCRIPT_SHAPES_NO_VISUAL },
1052 { 3, 2, DWRITE_SCRIPT_SHAPES_DEFAULT } }
1055 /* Null at end of string */
1056 L"test\0", 5, 2,
1057 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT },
1058 { 4, 1, DWRITE_SCRIPT_SHAPES_NO_VISUAL } }
1062 static void init_expected_sa(struct call_sequence **seq, const struct sa_test *test)
1064 static const struct call_entry end_of_sequence = { LastKind };
1065 int i;
1067 flush_sequence(seq, ANALYZER_ID);
1069 /* add expected calls */
1070 for (i = 0; i < test->item_count; i++)
1072 struct call_entry call;
1074 call.kind = ScriptAnalysis;
1075 call.sa.pos = test->sa[i].pos;
1076 call.sa.len = test->sa[i].len;
1077 call.sa.shapes = test->sa[i].shapes;
1078 add_call(seq, 0, &call);
1081 /* and stop marker */
1082 add_call(seq, 0, &end_of_sequence);
1085 static void get_script_analysis(const WCHAR *str, DWRITE_SCRIPT_ANALYSIS *sa)
1087 IDWriteTextAnalyzer *analyzer;
1088 HRESULT hr;
1090 init_textsource(&analysissource, str, -1, DWRITE_READING_DIRECTION_LEFT_TO_RIGHT);
1092 analyzer = create_text_analyzer(&IID_IDWriteTextAnalyzer);
1093 ok(!!analyzer, "Failed to create analyzer instance.\n");
1095 hr = IDWriteTextAnalyzer_AnalyzeScript(analyzer, &analysissource.IDWriteTextAnalysisSource_iface, 0,
1096 lstrlenW(analysissource.text), &analysissink2);
1097 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1099 *sa = g_sa;
1101 IDWriteTextAnalyzer_Release(analyzer);
1104 static void test_AnalyzeScript(void)
1106 IDWriteTextAnalyzer *analyzer;
1107 HRESULT hr;
1108 UINT i;
1110 analyzer = create_text_analyzer(&IID_IDWriteTextAnalyzer);
1111 ok(!!analyzer, "Failed to create analyzer instance.\n");
1113 for (i = 0; i < ARRAY_SIZE(sa_tests); i++)
1115 init_textsource(&analysissource, sa_tests[i].string, sa_tests[i].str_len,
1116 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT);
1118 winetest_push_context("Test %s", wine_dbgstr_wn(sa_tests[i].string, sa_tests[i].str_len));
1120 init_expected_sa(expected_seq, &sa_tests[i]);
1121 hr = IDWriteTextAnalyzer_AnalyzeScript(analyzer, &analysissource.IDWriteTextAnalysisSource_iface, 0,
1122 sa_tests[i].str_len == -1 ? lstrlenW(sa_tests[i].string) : sa_tests[i].str_len, &analysissink);
1123 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1124 ok_sequence(sequences, ANALYZER_ID, expected_seq[0]->sequence,
1125 wine_dbgstr_wn(sa_tests[i].string, sa_tests[i].str_len), FALSE);
1127 winetest_pop_context();
1130 IDWriteTextAnalyzer_Release(analyzer);
1133 struct linebreaks_test {
1134 const WCHAR text[BREAKPOINT_COUNT+1];
1135 DWRITE_LINE_BREAKPOINT bp[BREAKPOINT_COUNT];
1138 static const struct linebreaks_test linebreaks_tests[] =
1140 { {'A','-','B',' ','C',0x58a,'D',0x2010,'E',0x2012,'F',0x2013,'\t',0xc,0xb,0x2028,0x2029,0x200b,0},
1142 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1143 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 0, 0 },
1144 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1145 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 1, 0 },
1146 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1147 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 0, 0 },
1148 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1149 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 0, 0 },
1150 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1151 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 0, 0 },
1152 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1153 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1154 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 1, 0 },
1155 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_MUST_BREAK, 1, 0 },
1156 { DWRITE_BREAK_CONDITION_MUST_BREAK, DWRITE_BREAK_CONDITION_MUST_BREAK, 1, 0 },
1157 { DWRITE_BREAK_CONDITION_MUST_BREAK, DWRITE_BREAK_CONDITION_MUST_BREAK, 1, 0 },
1158 { DWRITE_BREAK_CONDITION_MUST_BREAK, DWRITE_BREAK_CONDITION_MUST_BREAK, 1, 0 },
1159 { DWRITE_BREAK_CONDITION_MUST_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 0, 0 },
1162 /* Soft hyphen, visible word dividers */
1163 { {'A',0xad,'B',0x5be,'C',0xf0b,'D',0x1361,'E',0x17d8,'F',0x17da,'G',0},
1165 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1166 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 0, 1 },
1167 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1168 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 0, 0 },
1169 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1170 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 0, 0 },
1171 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1172 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 0, 0 },
1173 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1174 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 0, 0 },
1175 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, 0, 0 },
1176 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 0, 0 },
1177 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, 0, 0 },
1180 /* LB30 changes in Unicode 13 regarding East Asian parentheses */
1181 { {0x5f35,'G',0x300c,0},
1183 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK },
1184 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK },
1185 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK },
1188 { { 0 } }
1191 static void compare_breakpoints(const struct linebreaks_test *test, DWRITE_LINE_BREAKPOINT *actual)
1193 static const char *conditions[] = {"N","CB","NB","B"};
1194 const WCHAR *text = test->text;
1195 int cmp = memcmp(test->bp, actual, sizeof(*actual)*BREAKPOINT_COUNT);
1196 ok(!cmp, "%s: got wrong breakpoint data\n", wine_dbgstr_w(test->text));
1197 if (cmp) {
1198 int i = 0;
1199 while (*text) {
1200 ok(!memcmp(&test->bp[i], &actual[i], sizeof(*actual)),
1201 "%s: got [%s, %s] (%s, %s), expected [%s, %s] (%s, %s)\n",
1202 wine_dbgstr_wn(&test->text[i], 1),
1203 conditions[g_actual_bp[i].breakConditionBefore],
1204 conditions[g_actual_bp[i].breakConditionAfter],
1205 g_actual_bp[i].isWhitespace ? "WS" : "0",
1206 g_actual_bp[i].isSoftHyphen ? "SHY" : "0",
1207 conditions[test->bp[i].breakConditionBefore],
1208 conditions[test->bp[i].breakConditionAfter],
1209 test->bp[i].isWhitespace ? "WS" : "0",
1210 test->bp[i].isSoftHyphen ? "SHY" : "0");
1211 if (g_actual_bp[i].isSoftHyphen)
1212 ok(!g_actual_bp[i].isWhitespace, "%s: soft hyphen marked as a whitespace\n",
1213 wine_dbgstr_wn(&test->text[i], 1));
1214 text++;
1215 i++;
1220 static void test_AnalyzeLineBreakpoints(void)
1222 const struct linebreaks_test *ptr = linebreaks_tests;
1223 IDWriteTextAnalyzer *analyzer;
1224 UINT32 i = 0;
1225 HRESULT hr;
1227 analyzer = create_text_analyzer(&IID_IDWriteTextAnalyzer);
1228 ok(!!analyzer, "Failed to create analyzer instance.\n");
1230 init_textsource(&analysissource, L"", 0, DWRITE_READING_DIRECTION_LEFT_TO_RIGHT);
1231 hr = IDWriteTextAnalyzer_AnalyzeLineBreakpoints(analyzer, &analysissource.IDWriteTextAnalysisSource_iface, 0, 0,
1232 &analysissink);
1233 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1235 while (*ptr->text)
1237 UINT32 len;
1239 init_textsource(&analysissource, ptr->text, -1, DWRITE_READING_DIRECTION_LEFT_TO_RIGHT);
1241 len = lstrlenW(ptr->text);
1242 if (len > BREAKPOINT_COUNT) {
1243 ok(0, "test %u: increase BREAKPOINT_COUNT to at least %u\n", i, len);
1244 i++;
1245 ptr++;
1246 continue;
1249 memset(g_actual_bp, 0, sizeof(g_actual_bp));
1250 hr = IDWriteTextAnalyzer_AnalyzeLineBreakpoints(analyzer, &analysissource.IDWriteTextAnalysisSource_iface,
1251 0, len, &analysissink);
1252 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1253 compare_breakpoints(ptr, g_actual_bp);
1255 i++;
1256 ptr++;
1259 IDWriteTextAnalyzer_Release(analyzer);
1262 static void test_GetScriptProperties(void)
1264 IDWriteTextAnalyzer1 *analyzer;
1265 DWRITE_SCRIPT_PROPERTIES props;
1266 DWRITE_SCRIPT_ANALYSIS sa;
1267 HRESULT hr;
1269 analyzer = create_text_analyzer(&IID_IDWriteTextAnalyzer1);
1270 if (!analyzer)
1272 win_skip("GetScriptProperties() is not supported.\n");
1273 return;
1276 sa.script = 1000;
1277 hr = IDWriteTextAnalyzer1_GetScriptProperties(analyzer, sa, &props);
1278 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
1280 if (0) /* Crashes on Windows */
1281 hr = IDWriteTextAnalyzer1_GetScriptProperties(analyzer, sa, NULL);
1283 sa.script = 0;
1284 hr = IDWriteTextAnalyzer1_GetScriptProperties(analyzer, sa, &props);
1285 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1287 IDWriteTextAnalyzer1_Release(analyzer);
1290 struct textcomplexity_test {
1291 const WCHAR text[5];
1292 UINT32 length;
1293 BOOL simple;
1294 UINT32 len_read;
1297 static const struct textcomplexity_test textcomplexity_tests[] = {
1298 { {0}, 1, FALSE, 1 },
1299 { {0}, 0, TRUE, 0 },
1300 { {0x610,0}, 0, TRUE, 0 },
1301 { {'A','B','C','D',0}, 3, TRUE, 3 },
1302 { {'A','B','C','D',0}, 5, TRUE, 4 },
1303 { {'A','B','C','D',0}, 10, TRUE, 4 },
1304 { {'A',0x610,'C','D',0}, 1, TRUE, 1 },
1305 { {'A',0x610,'C','D',0}, 2, FALSE, 2 },
1306 { {0x610,'A','C','D',0}, 1, FALSE, 1 },
1307 { {0x610,'A','C','D',0}, 2, FALSE, 1 },
1308 { {0x610,0x610,'C','D',0}, 2, FALSE, 2 },
1309 { {0xd800,'A','B',0}, 1, FALSE, 1 },
1310 { {0xd800,'A','B',0}, 2, FALSE, 1 },
1311 { {0xdc00,'A','B',0}, 2, FALSE, 1 },
1312 { {0x202a,'A',0x202c,0}, 3, FALSE, 1 },
1313 { {0x200e,'A',0}, 2, FALSE, 1 },
1314 { {0x200f,'A',0}, 2, FALSE, 1 },
1315 { {0x202d,'A',0}, 2, FALSE, 1 },
1316 { {0x202e,'A',0}, 2, FALSE, 1 },
1320 static void test_GetTextComplexity(void)
1322 IDWriteTextAnalyzer1 *analyzer;
1323 IDWriteFontFace *fontface;
1324 UINT16 indices[10];
1325 BOOL simple;
1326 HRESULT hr;
1327 UINT32 len;
1328 int i;
1330 analyzer = create_text_analyzer(&IID_IDWriteTextAnalyzer1);
1331 if (!analyzer)
1333 win_skip("GetTextComplexity() is not supported.\n");
1334 return;
1337 if (0) { /* crashes on native */
1338 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer, NULL, 0, NULL, NULL, NULL, NULL);
1339 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer, NULL, 0, NULL, NULL, &len, NULL);
1340 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer, L"ABC", 3, NULL, NULL, NULL, NULL);
1341 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer, L"ABC", 3, NULL, NULL, &len, NULL);
1342 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer, L"ABC", 3, NULL, &simple, NULL, NULL);
1345 len = 1;
1346 simple = TRUE;
1347 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer, NULL, 0, NULL, &simple, &len, NULL);
1348 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
1349 ok(len == 0, "got %d\n", len);
1350 ok(simple == FALSE, "got %d\n", simple);
1352 len = 1;
1353 simple = TRUE;
1354 indices[0] = 1;
1355 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer, L"ABC", 3, NULL, &simple, &len, NULL);
1356 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
1357 ok(len == 0, "got %d\n", len);
1358 ok(simple == FALSE, "got %d\n", simple);
1359 ok(indices[0] == 1, "got %d\n", indices[0]);
1361 fontface = create_fontface();
1363 for (i = 0; i < ARRAY_SIZE(textcomplexity_tests); ++i)
1365 const struct textcomplexity_test *ptr = &textcomplexity_tests[i];
1366 len = 1;
1367 simple = !ptr->simple;
1368 indices[0] = 0;
1369 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer, ptr->text, ptr->length, fontface, &simple, &len, indices);
1370 ok(hr == S_OK, "%d: Unexpected hr %#lx.\n", i, hr);
1371 ok(len == ptr->len_read, "%d: read length: got %d, expected %d\n", i, len, ptr->len_read);
1372 ok(simple == ptr->simple, "%d: simple: got %d, expected %d\n", i, simple, ptr->simple);
1373 if (simple && ptr->length)
1374 ok(indices[0] > 0, "%d: got %d\n", i, indices[0]);
1375 else
1376 ok(indices[0] == 0, "%d: got %d\n", i, indices[0]);
1379 IDWriteFontFace_Release(fontface);
1380 IDWriteTextAnalyzer1_Release(analyzer);
1383 static void test_numbersubstitution(void)
1385 IDWriteNumberSubstitution *substitution;
1386 HRESULT hr;
1388 /* locale is not specified, method does not require it */
1389 hr = IDWriteFactory_CreateNumberSubstitution(factory, DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE, NULL, FALSE, &substitution);
1390 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1391 IDWriteNumberSubstitution_Release(substitution);
1393 /* invalid locale name, method does not require it */
1394 hr = IDWriteFactory_CreateNumberSubstitution(factory, DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE, L"dummy",
1395 FALSE, &substitution);
1396 ok(hr == S_OK, "Failed to create number substitution, hr %#lx.\n", hr);
1397 IDWriteNumberSubstitution_Release(substitution);
1399 /* invalid method */
1400 hr = IDWriteFactory_CreateNumberSubstitution(factory, DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL+1, NULL, FALSE, &substitution);
1401 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
1403 /* invalid method */
1404 hr = IDWriteFactory_CreateNumberSubstitution(factory, -1, NULL, FALSE, &substitution);
1405 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
1407 /* invalid locale */
1408 hr = IDWriteFactory_CreateNumberSubstitution(factory, DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL, NULL, FALSE, &substitution);
1409 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
1411 hr = IDWriteFactory_CreateNumberSubstitution(factory, DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL, L"dummy",
1412 FALSE, &substitution);
1413 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
1415 hr = IDWriteFactory_CreateNumberSubstitution(factory, DWRITE_NUMBER_SUBSTITUTION_METHOD_CONTEXTUAL, L"dummy",
1416 FALSE, &substitution);
1417 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
1419 hr = IDWriteFactory_CreateNumberSubstitution(factory, DWRITE_NUMBER_SUBSTITUTION_METHOD_NATIONAL, L"dummy",
1420 FALSE, &substitution);
1421 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
1423 /* invalid locale, but it's not needed for this method */
1424 hr = IDWriteFactory_CreateNumberSubstitution(factory, DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE, L"dummy", FALSE,
1425 &substitution);
1426 ok(hr == S_OK, "Failed to create number substitution, hr %#lx.\n", hr);
1427 IDWriteNumberSubstitution_Release(substitution);
1430 static void get_fontface_glyphs(IDWriteFontFace *fontface, const WCHAR *str, UINT16 *glyphs)
1432 while (*str) {
1433 UINT32 codepoint = *str;
1434 HRESULT hr;
1436 hr = IDWriteFontFace_GetGlyphIndices(fontface, &codepoint, 1, glyphs++);
1437 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1438 str++;
1442 static void get_fontface_advances(IDWriteFontFace *fontface, FLOAT emsize, const UINT16 *glyphs, FLOAT *advances, UINT32 count)
1444 DWRITE_FONT_METRICS fontmetrics;
1445 UINT32 i;
1447 IDWriteFontFace_GetMetrics(fontface, &fontmetrics);
1448 for (i = 0; i < count; i++) {
1449 DWRITE_GLYPH_METRICS metrics;
1450 HRESULT hr;
1452 hr = IDWriteFontFace_GetDesignGlyphMetrics(fontface, glyphs + i, 1, &metrics, FALSE);
1453 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1455 advances[i] = (FLOAT)metrics.advanceWidth * emsize / (FLOAT)fontmetrics.designUnitsPerEm;
1459 enum ot_gdef_class
1461 GDEF_CLASS_UNCLASSIFIED = 0,
1462 GDEF_CLASS_BASE = 1,
1463 GDEF_CLASS_LIGATURE = 2,
1464 GDEF_CLASS_MARK = 3,
1465 GDEF_CLASS_COMPONENT = 4,
1466 GDEF_CLASS_MAX = GDEF_CLASS_COMPONENT,
1469 struct dwrite_fonttable
1471 BYTE *data;
1472 void *context;
1473 unsigned int size;
1476 static const void *table_read_ensure(const struct dwrite_fonttable *table, unsigned int offset, unsigned int size)
1478 if (size > table->size || offset > table->size - size)
1479 return NULL;
1481 return table->data + offset;
1484 static WORD table_read_be_word(const struct dwrite_fonttable *table, unsigned int offset)
1486 const WORD *ptr = table_read_ensure(table, offset, sizeof(*ptr));
1487 return ptr ? GET_BE_WORD(*ptr) : 0;
1490 static int gdef_class_compare_format2(const void *g, const void *r)
1492 const struct ot_gdef_class_range *range = r;
1493 UINT16 glyph = *(UINT16 *)g;
1495 if (glyph < GET_BE_WORD(range->start_glyph))
1496 return -1;
1497 else if (glyph > GET_BE_WORD(range->end_glyph))
1498 return 1;
1499 else
1500 return 0;
1503 static unsigned int get_glyph_class(const struct dwrite_fonttable *table, UINT16 glyph)
1505 unsigned int glyph_class = GDEF_CLASS_UNCLASSIFIED, offset;
1506 WORD format, count;
1508 offset = table_read_be_word(table, 4);
1510 format = table_read_be_word(table, offset);
1512 if (format == 1)
1514 const struct ot_gdef_classdef_format1 *format1;
1516 count = table_read_be_word(table, offset + FIELD_OFFSET(struct ot_gdef_classdef_format1, glyph_count));
1517 format1 = table_read_ensure(table, offset, FIELD_OFFSET(struct ot_gdef_classdef_format1, classes[count]));
1518 if (format1)
1520 WORD start_glyph = GET_BE_WORD(format1->start_glyph);
1521 if (glyph >= start_glyph && (glyph - start_glyph) < count)
1523 glyph_class = GET_BE_WORD(format1->classes[glyph - start_glyph]);
1524 if (glyph_class > GDEF_CLASS_MAX)
1525 glyph_class = GDEF_CLASS_UNCLASSIFIED;
1529 else if (format == 2)
1531 const struct ot_gdef_classdef_format2 *format2;
1533 count = table_read_be_word(table, offset + FIELD_OFFSET(struct ot_gdef_classdef_format2, range_count));
1534 format2 = table_read_ensure(table, offset, FIELD_OFFSET(struct ot_gdef_classdef_format2, ranges[count]));
1535 if (format2)
1537 const struct ot_gdef_class_range *range = bsearch(&glyph, format2->ranges, count,
1538 sizeof(struct ot_gdef_class_range), gdef_class_compare_format2);
1539 glyph_class = range && glyph <= GET_BE_WORD(range->end_glyph) ?
1540 GET_BE_WORD(range->glyph_class) : GDEF_CLASS_UNCLASSIFIED;
1541 if (glyph_class > GDEF_CLASS_MAX)
1542 glyph_class = GDEF_CLASS_UNCLASSIFIED;
1546 return glyph_class;
1549 static void get_enus_string(IDWriteLocalizedStrings *strings, WCHAR *buff, unsigned int size)
1551 BOOL exists = FALSE;
1552 unsigned int index;
1553 HRESULT hr;
1555 hr = IDWriteLocalizedStrings_FindLocaleName(strings, L"en-us", &index, &exists);
1556 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1558 /* Not all fonts have an en-us name! */
1559 if (!exists)
1560 index = 0;
1562 hr = IDWriteLocalizedStrings_GetString(strings, index, buff, size);
1563 ok(hr == S_OK, "Failed to get name string, hr %#lx.\n", hr);
1566 static void test_glyph_props(IDWriteTextAnalyzer *analyzer, const WCHAR *family, const WCHAR *face,
1567 IDWriteFontFace *fontface)
1569 unsigned int i, ch, count, offset;
1570 struct dwrite_fonttable gdef;
1571 DWRITE_UNICODE_RANGE *ranges;
1572 IDWriteFontFace1 *fontface1;
1573 BOOL exists = FALSE;
1574 HRESULT hr;
1576 hr = IDWriteFontFace_TryGetFontTable(fontface, MS_GDEF_TAG, (const void **)&gdef.data, &gdef.size,
1577 &gdef.context, &exists);
1578 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1580 if (!exists)
1581 return;
1583 offset = table_read_be_word(&gdef, 4);
1584 if (!offset)
1586 IDWriteFontFace_ReleaseFontTable(fontface, gdef.context);
1587 return;
1590 if (FAILED(IDWriteFontFace_QueryInterface(fontface, &IID_IDWriteFontFace1, (void **)&fontface1)))
1592 IDWriteFontFace_ReleaseFontTable(fontface, gdef.context);
1593 return;
1596 hr = IDWriteFontFace1_GetUnicodeRanges(fontface1, 0, NULL, &count);
1597 ok(hr == E_NOT_SUFFICIENT_BUFFER, "Unexpected hr %#lx.\n", hr);
1599 ranges = malloc(count * sizeof(*ranges));
1601 hr = IDWriteFontFace1_GetUnicodeRanges(fontface1, count, ranges, &count);
1602 ok(hr == S_OK, "Failed to get ranges, hr %#lx.\n", hr);
1604 for (i = 0; i < count; ++i)
1606 if (ranges[i].first > 0xffff)
1607 break;
1609 for (ch = ranges[i].first; ch <= ranges[i].last; ch++)
1611 DWRITE_SHAPING_TEXT_PROPERTIES text_props[10];
1612 DWRITE_SHAPING_GLYPH_PROPERTIES glyph_props[10];
1613 UINT16 glyphs[10], clustermap[10], glyph;
1614 unsigned int actual_count, glyph_class;
1615 DWRITE_SCRIPT_ANALYSIS sa;
1616 WCHAR text[1];
1618 hr = IDWriteFontFace1_GetGlyphIndices(fontface1, &ch, 1, &glyph);
1619 ok(hr == S_OK, "Failed to get glyph index, hr %#lx.\n", hr);
1621 if (!glyph)
1622 continue;
1624 sa.script = 999;
1625 sa.shapes = DWRITE_SCRIPT_SHAPES_DEFAULT;
1626 text[0] = (WCHAR)ch;
1627 memset(glyph_props, 0, sizeof(glyph_props));
1628 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, text, 1, fontface, FALSE, FALSE, &sa, NULL,
1629 NULL, NULL, NULL, 0, ARRAY_SIZE(glyphs), clustermap, text_props, glyphs, glyph_props, &actual_count);
1630 ok(hr == S_OK, "Failed to shape, hr %#lx.\n", hr);
1631 if (actual_count > 1)
1632 continue;
1634 glyph_class = get_glyph_class(&gdef, glyphs[0]);
1636 switch (glyph_class)
1638 case GDEF_CLASS_MARK:
1639 ok(glyph_props[0].isDiacritic && glyph_props[0].isZeroWidthSpace,
1640 "%#x -> %u: unexpected glyph properties %u/%u. Class %u. Font %s - %s.\n",
1641 text[0], glyphs[0], glyph_props[0].isDiacritic, glyph_props[0].isZeroWidthSpace,
1642 glyph_class, wine_dbgstr_w(family), wine_dbgstr_w(face));
1643 break;
1644 default:
1645 break;
1648 if (glyph_props[0].isDiacritic)
1649 ok(glyph_props[0].isZeroWidthSpace,
1650 "%#x -> %u: unexpected glyph properties %u/%u. Class %u. Font %s - %s.\n", text[0], glyphs[0],
1651 glyph_props[0].isDiacritic, glyph_props[0].isZeroWidthSpace, glyph_class,
1652 wine_dbgstr_w(family), wine_dbgstr_w(face));
1656 free(ranges);
1658 IDWriteFontFace_ReleaseFontTable(fontface, gdef.context);
1659 IDWriteFontFace1_Release(fontface1);
1662 static void test_GetGlyphs(void)
1664 static const WCHAR test1W[] = {'<','B',' ','C',0};
1665 static const WCHAR test2W[] = {'<','B','\t','C',0};
1666 static const WCHAR test3W[] = {0x202a,0x202c,0};
1667 DWRITE_SHAPING_GLYPH_PROPERTIES shapingprops[20];
1668 DWRITE_SHAPING_TEXT_PROPERTIES props[20];
1669 UINT32 maxglyphcount, actual_count;
1670 FLOAT advances[10], advances2[10];
1671 IDWriteFontCollection *syscoll;
1672 IDWriteTextAnalyzer *analyzer;
1673 IDWriteFontFace *fontface;
1674 DWRITE_SCRIPT_ANALYSIS sa;
1675 DWRITE_GLYPH_OFFSET offsets[10];
1676 UINT16 clustermap[10];
1677 UINT16 glyphs1[10];
1678 UINT16 glyphs2[10];
1679 unsigned int i, j;
1680 HRESULT hr;
1682 analyzer = create_text_analyzer(&IID_IDWriteTextAnalyzer);
1683 ok(!!analyzer, "Failed to create analyzer instance.\n");
1685 fontface = create_fontface();
1687 maxglyphcount = 1;
1688 sa.script = 0;
1689 sa.shapes = DWRITE_SCRIPT_SHAPES_DEFAULT;
1690 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test1W, lstrlenW(test1W), fontface, FALSE, FALSE, &sa, NULL,
1691 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs1, shapingprops, &actual_count);
1692 ok(hr == E_NOT_SUFFICIENT_BUFFER, "Unexpected hr %#lx.\n", hr);
1694 if (0) {
1695 /* NULL fontface - crashes on Windows */
1696 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test1W, lstrlenW(test1W), NULL, FALSE, FALSE, &sa, NULL,
1697 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs1, shapingprops, &actual_count);
1700 /* invalid script id */
1701 maxglyphcount = 10;
1702 actual_count = 0;
1703 sa.script = 999;
1704 sa.shapes = DWRITE_SCRIPT_SHAPES_DEFAULT;
1705 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test1W, lstrlenW(test1W), fontface, FALSE, FALSE, &sa, NULL,
1706 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs1, shapingprops, &actual_count);
1707 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1708 ok(actual_count == 4, "got %d\n", actual_count);
1709 ok(sa.script == 999, "got %u\n", sa.script);
1711 /* no '\t' -> ' ' replacement */
1712 maxglyphcount = 10;
1713 actual_count = 0;
1714 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test1W, lstrlenW(test1W), fontface, FALSE, FALSE, &sa, NULL,
1715 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs1, shapingprops, &actual_count);
1716 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1717 ok(actual_count == 4, "got %d\n", actual_count);
1719 actual_count = 0;
1720 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test2W, lstrlenW(test2W), fontface, FALSE, FALSE, &sa, NULL,
1721 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs2, shapingprops, &actual_count);
1722 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1723 ok(actual_count == 4, "got %d\n", actual_count);
1724 ok(glyphs1[2] != glyphs2[2], "got %d\n", glyphs1[2]);
1726 /* check that mirroring works */
1727 maxglyphcount = 10;
1728 actual_count = 0;
1729 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test1W, lstrlenW(test1W), fontface, FALSE, FALSE, &sa, NULL,
1730 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs1, shapingprops, &actual_count);
1731 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1732 ok(actual_count == 4, "got %d\n", actual_count);
1734 actual_count = 0;
1735 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test1W, lstrlenW(test1W), fontface, FALSE, TRUE, &sa, NULL,
1736 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs2, shapingprops, &actual_count);
1737 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1738 ok(actual_count == 4, "got %d\n", actual_count);
1739 ok(glyphs1[0] != glyphs2[0], "got %d\n", glyphs1[0]);
1741 /* embedded control codes, with unknown script id 0 */
1742 get_fontface_glyphs(fontface, test3W, glyphs2);
1743 get_fontface_advances(fontface, 10.0, glyphs2, advances2, 2);
1745 actual_count = 0;
1746 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test3W, lstrlenW(test3W), fontface, FALSE, TRUE, &sa, NULL,
1747 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs1, shapingprops, &actual_count);
1748 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1749 ok(actual_count == 2, "got %d\n", actual_count);
1750 ok(glyphs1[0] == glyphs2[0], "got %u, expected %u\n", glyphs1[0], glyphs2[0]);
1751 ok(glyphs1[1] == glyphs2[1], "got %u, expected %u\n", glyphs1[1], glyphs2[1]);
1752 ok(shapingprops[0].isClusterStart == 1, "got %d\n", shapingprops[0].isClusterStart);
1753 ok(shapingprops[0].isZeroWidthSpace == 0, "got %d\n", shapingprops[0].isZeroWidthSpace);
1754 ok(shapingprops[1].isClusterStart == 1, "got %d\n", shapingprops[1].isClusterStart);
1755 ok(shapingprops[1].isZeroWidthSpace == 0, "got %d\n", shapingprops[1].isZeroWidthSpace);
1756 ok(clustermap[0] == 0, "got %d\n", clustermap[0]);
1757 ok(clustermap[1] == 1, "got %d\n", clustermap[1]);
1759 memset(advances, 0, sizeof(advances));
1760 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, test3W, clustermap, props, lstrlenW(test3W),
1761 glyphs1, shapingprops, actual_count, fontface, 10.0, FALSE, FALSE, &sa, NULL, NULL,
1762 NULL, 0, advances, offsets);
1763 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1764 ok(advances[0] == advances2[0], "got %.2f, expected %.2f\n", advances[0], advances2[0]);
1765 ok(advances[1] == advances2[1], "got %.2f, expected %.2f\n", advances[1], advances2[1]);
1767 /* embedded control codes with proper script */
1768 sa.script = 0;
1769 get_script_analysis(test3W, &sa);
1770 ok(sa.script != 0, "got %d\n", sa.script);
1771 actual_count = 0;
1772 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test3W, lstrlenW(test3W), fontface, FALSE, FALSE, &sa, NULL,
1773 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs1, shapingprops, &actual_count);
1774 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1775 ok(actual_count == 2, "got %d\n", actual_count);
1776 ok(glyphs1[0] == glyphs2[0], "got %u, expected %u\n", glyphs1[0], glyphs2[0]);
1777 ok(glyphs1[1] == glyphs2[1], "got %u, expected %u\n", glyphs1[1], glyphs2[1]);
1778 ok(shapingprops[0].isClusterStart == 1, "got %d\n", shapingprops[0].isClusterStart);
1779 ok(shapingprops[0].isZeroWidthSpace == 0, "got %d\n", shapingprops[0].isZeroWidthSpace);
1780 ok(shapingprops[1].isClusterStart == 1, "got %d\n", shapingprops[1].isClusterStart);
1781 ok(shapingprops[1].isZeroWidthSpace == 0, "got %d\n", shapingprops[1].isZeroWidthSpace);
1782 ok(clustermap[0] == 0, "got %d\n", clustermap[0]);
1783 ok(clustermap[1] == 1, "got %d\n", clustermap[1]);
1785 memset(advances, 0, sizeof(advances));
1786 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, test3W, clustermap, props, lstrlenW(test3W),
1787 glyphs1, shapingprops, actual_count, fontface, 10.0, FALSE, FALSE, &sa, NULL, NULL,
1788 NULL, 0, advances, offsets);
1789 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1790 ok(advances[0] == advances2[0], "got %.2f, expected %.2f\n", advances[0], advances2[0]);
1791 ok(advances[1] == advances2[1], "got %.2f, expected %.2f\n", advances[1], advances2[1]);
1793 /* DWRITE_SCRIPT_SHAPES_NO_VISUAL run */
1794 maxglyphcount = 10;
1795 actual_count = 0;
1796 sa.script = 0;
1797 sa.shapes = DWRITE_SCRIPT_SHAPES_NO_VISUAL;
1798 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test1W, lstrlenW(test1W), fontface, FALSE, FALSE, &sa, NULL,
1799 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs1, shapingprops, &actual_count);
1800 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1801 ok(actual_count == 4, "got %d\n", actual_count);
1802 ok(sa.script == 0, "got %u\n", sa.script);
1803 ok(!shapingprops[0].isZeroWidthSpace, "got %d\n", shapingprops[0].isZeroWidthSpace);
1805 IDWriteFontFace_Release(fontface);
1807 /* Test setting glyph properties from GDEF. */
1808 hr = IDWriteFactory_GetSystemFontCollection(factory, &syscoll, FALSE);
1809 ok(hr == S_OK, "Failed to get system collection, hr %#lx.\n", hr);
1811 for (i = 0; i < IDWriteFontCollection_GetFontFamilyCount(syscoll); ++i)
1813 IDWriteLocalizedStrings *names;
1814 IDWriteFontFamily *family;
1815 WCHAR familyW[256];
1817 hr = IDWriteFontCollection_GetFontFamily(syscoll, i, &family);
1818 ok(hr == S_OK, "Failed to get font family, hr %#lx.\n", hr);
1820 hr = IDWriteFontFamily_GetFamilyNames(family, &names);
1821 ok(hr == S_OK, "Failed to get family names, hr %#lx.\n", hr);
1822 get_enus_string(names, familyW, ARRAY_SIZE(familyW));
1823 IDWriteLocalizedStrings_Release(names);
1825 for (j = 0; j < IDWriteFontFamily_GetFontCount(family); ++j)
1827 IDWriteFont *font;
1828 WCHAR faceW[256];
1830 hr = IDWriteFontFamily_GetFont(family, j, &font);
1831 ok(hr == S_OK, "Failed to get font instance, hr %#lx.\n", hr);
1833 hr = IDWriteFont_CreateFontFace(font, &fontface);
1834 ok(hr == S_OK, "Failed to create fontface, hr %#lx.\n", hr);
1836 hr = IDWriteFont_GetFaceNames(font, &names);
1837 ok(hr == S_OK, "Failed to get face names, hr %#lx.\n", hr);
1838 get_enus_string(names, faceW, ARRAY_SIZE(faceW));
1839 IDWriteLocalizedStrings_Release(names);
1841 test_glyph_props(analyzer, familyW, faceW, fontface);
1843 IDWriteFontFace_Release(fontface);
1844 IDWriteFont_Release(font);
1847 IDWriteFontFamily_Release(family);
1850 IDWriteFontCollection_Release(syscoll);
1852 IDWriteTextAnalyzer_Release(analyzer);
1855 static void test_GetTypographicFeatures(void)
1857 static const WCHAR arabicW[] = {0x064a,0x064f,0x0633,0};
1858 DWRITE_FONT_FEATURE_TAG tags[20];
1859 IDWriteTextAnalyzer2 *analyzer;
1860 IDWriteFontFace *fontface;
1861 DWRITE_SCRIPT_ANALYSIS sa;
1862 UINT32 count;
1863 HRESULT hr;
1865 analyzer = create_text_analyzer(&IID_IDWriteTextAnalyzer2);
1866 if (!analyzer)
1868 win_skip("GetTypographicFeatures() is not supported.\n");
1869 return;
1872 fontface = create_fontface();
1874 get_script_analysis(L"abc", &sa);
1875 count = 0;
1876 hr = IDWriteTextAnalyzer2_GetTypographicFeatures(analyzer, fontface, sa, NULL, 0, &count, NULL);
1877 ok(hr == E_NOT_SUFFICIENT_BUFFER, "Unexpected hr %#lx.\n", hr);
1878 ok(!!count, "Unexpected count %u.\n", count);
1880 /* invalid locale name is ignored */
1881 get_script_analysis(L"abc", &sa);
1882 count = 0;
1883 hr = IDWriteTextAnalyzer2_GetTypographicFeatures(analyzer, fontface, sa, L"cadabra", 0, &count, NULL);
1884 ok(hr == E_NOT_SUFFICIENT_BUFFER, "Unexpected hr %#lx.\n", hr);
1885 ok(!!count, "Unexpected count %u.\n", count);
1887 /* Make some calls for different scripts. */
1889 get_script_analysis(arabicW, &sa);
1890 memset(tags, 0, sizeof(tags));
1891 count = 0;
1892 hr = IDWriteTextAnalyzer2_GetTypographicFeatures(analyzer, fontface, sa, NULL, ARRAY_SIZE(tags), &count, tags);
1893 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1894 ok(!!count, "Unexpected count %u.\n", count);
1896 get_script_analysis(L"abc", &sa);
1897 memset(tags, 0, sizeof(tags));
1898 count = 0;
1899 hr = IDWriteTextAnalyzer2_GetTypographicFeatures(analyzer, fontface, sa, NULL, ARRAY_SIZE(tags), &count, tags);
1900 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1901 ok(!!count, "Unexpected count %u.\n", count);
1903 IDWriteFontFace_Release(fontface);
1904 IDWriteTextAnalyzer2_Release(analyzer);
1907 static void test_GetGlyphPlacements(void)
1909 DWRITE_SHAPING_GLYPH_PROPERTIES glyphprops[2];
1910 DWRITE_SHAPING_TEXT_PROPERTIES textprops[2];
1911 static const WCHAR aW[] = {'A','D',0};
1912 UINT16 clustermap[2], glyphs[2];
1913 DWRITE_GLYPH_OFFSET offsets[2];
1914 IDWriteTextAnalyzer *analyzer;
1915 IDWriteFontFace *fontface;
1916 DWRITE_SCRIPT_ANALYSIS sa;
1917 FLOAT advances[2];
1918 UINT32 count, len;
1919 WCHAR *path;
1920 HRESULT hr;
1922 analyzer = create_text_analyzer(&IID_IDWriteTextAnalyzer);
1923 ok(!!analyzer, "Failed to create analyzer instance.\n");
1925 path = create_testfontfile(L"wine_test_font.ttf");
1926 fontface = create_testfontface(path);
1928 get_script_analysis(aW, &sa);
1929 count = 0;
1930 len = lstrlenW(aW);
1931 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, aW, len, fontface, FALSE, FALSE, &sa, NULL,
1932 NULL, NULL, NULL, 0, len, clustermap, textprops, glyphs, glyphprops, &count);
1933 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1934 ok(count == 2, "got %u\n", count);
1936 /* just return on zero glyphs */
1937 advances[0] = advances[1] = 1.0;
1938 offsets[0].advanceOffset = offsets[0].ascenderOffset = 2.0;
1939 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, clustermap, textprops,
1940 len, glyphs, glyphprops, 0, fontface, 0.0, FALSE, FALSE, &sa, NULL, NULL,
1941 NULL, 0, advances, offsets);
1942 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1943 ok(advances[0] == 1.0, "got %.2f\n", advances[0]);
1944 ok(offsets[0].advanceOffset == 2.0 && offsets[0].ascenderOffset == 2.0, "got %.2f,%.2f\n",
1945 offsets[0].advanceOffset, offsets[0].ascenderOffset);
1947 /* advances/offsets are scaled with provided font emSize and designed eM box size */
1948 advances[0] = advances[1] = 1.0;
1949 memset(offsets, 0xcc, sizeof(offsets));
1950 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, clustermap, textprops,
1951 len, glyphs, glyphprops, len, fontface, 0.0, FALSE, FALSE, &sa, NULL, NULL,
1952 NULL, 0, advances, offsets);
1953 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1954 ok(advances[0] == 0.0, "got %.2f\n", advances[0]);
1955 ok(offsets[0].advanceOffset == 0.0 && offsets[0].ascenderOffset == 0.0, "got %.2f,%.2f\n",
1956 offsets[0].advanceOffset, offsets[0].ascenderOffset);
1958 advances[0] = advances[1] = 1.0;
1959 memset(offsets, 0xcc, sizeof(offsets));
1960 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, clustermap, textprops,
1961 len, glyphs, glyphprops, len, fontface, 2048.0, FALSE, FALSE, &sa, NULL, NULL,
1962 NULL, 0, advances, offsets);
1963 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1964 ok(advances[0] == 1000.0, "got %.2f\n", advances[0]);
1965 ok(offsets[0].advanceOffset == 0.0 && offsets[0].ascenderOffset == 0.0, "got %.2f,%.2f\n",
1966 offsets[0].advanceOffset, offsets[0].ascenderOffset);
1968 advances[0] = advances[1] = 1.0;
1969 memset(offsets, 0xcc, sizeof(offsets));
1970 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, clustermap, textprops,
1971 len, glyphs, glyphprops, len, fontface, 1024.0, FALSE, FALSE, &sa, NULL, NULL,
1972 NULL, 0, advances, offsets);
1973 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1974 ok(advances[0] == 500.0, "got %.2f\n", advances[0]);
1975 ok(advances[1] == 500.0, "got %.2f\n", advances[1]);
1976 ok(offsets[0].advanceOffset == 0.0 && offsets[0].ascenderOffset == 0.0, "got %.2f,%.2f\n",
1977 offsets[0].advanceOffset, offsets[0].ascenderOffset);
1979 advances[0] = advances[1] = 1.0;
1980 memset(offsets, 0xcc, sizeof(offsets));
1981 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, clustermap, textprops,
1982 len, glyphs, glyphprops, len, fontface, 20.48, FALSE, FALSE, &sa, NULL, NULL,
1983 NULL, 0, advances, offsets);
1984 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1985 ok(advances[0] == 10.0, "got %.2f\n", advances[0]);
1986 ok(advances[1] == 10.0, "got %.2f\n", advances[1]);
1987 ok(offsets[0].advanceOffset == 0.0 && offsets[0].ascenderOffset == 0.0, "got %.2f,%.2f\n",
1988 offsets[0].advanceOffset, offsets[0].ascenderOffset);
1990 /* without clustermap */
1991 advances[0] = advances[1] = 1.0;
1992 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, NULL, textprops,
1993 len, glyphs, glyphprops, len, fontface, 1024.0, FALSE, FALSE, &sa, NULL, NULL,
1994 NULL, 0, advances, offsets);
1995 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
1996 ok(advances[0] == 500.0, "got %.2f\n", advances[0]);
1997 ok(advances[1] == 500.0, "got %.2f\n", advances[1]);
1999 /* it's happy to use negative size too */
2000 advances[0] = advances[1] = 1.0;
2001 memset(offsets, 0xcc, sizeof(offsets));
2002 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, clustermap, textprops,
2003 len, glyphs, glyphprops, len, fontface, -10.24, FALSE, FALSE, &sa, NULL, NULL,
2004 NULL, 0, advances, offsets);
2005 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2006 ok(advances[0] == -5.0, "got %.2f\n", advances[0]);
2007 ok(offsets[0].advanceOffset == 0.0 && offsets[0].ascenderOffset == 0.0, "got %.2f,%.2f\n",
2008 offsets[0].advanceOffset, offsets[0].ascenderOffset);
2010 /* DWRITE_SCRIPT_SHAPES_NO_VISUAL has no effect on placement */
2011 sa.shapes = DWRITE_SCRIPT_SHAPES_NO_VISUAL;
2012 advances[0] = advances[1] = 1.0f;
2013 memset(offsets, 0xcc, sizeof(offsets));
2014 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, clustermap, textprops,
2015 len, glyphs, glyphprops, len, fontface, 2048.0f, FALSE, FALSE, &sa, NULL, NULL,
2016 NULL, 0, advances, offsets);
2017 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2018 ok(advances[0] == 1000.0f, "got %.2f\n", advances[0]);
2019 ok(advances[1] == 1000.0f, "got %.2f\n", advances[1]);
2020 ok(offsets[0].advanceOffset == 0.0f && offsets[0].ascenderOffset == 0.0f, "got %.2f,%.2f\n",
2021 offsets[0].advanceOffset, offsets[0].ascenderOffset);
2023 /* isZeroWidthSpace */
2024 sa.shapes = DWRITE_SCRIPT_SHAPES_DEFAULT;
2025 advances[0] = advances[1] = 1.0f;
2026 memset(offsets, 0xcc, sizeof(offsets));
2027 glyphprops[0].isZeroWidthSpace = 1;
2028 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, clustermap, textprops,
2029 len, glyphs, glyphprops, len, fontface, 2048.0f, FALSE, FALSE, &sa, NULL, NULL,
2030 NULL, 0, advances, offsets);
2031 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2032 ok(advances[0] == 0.0f, "got %.2f\n", advances[0]);
2033 ok(advances[1] == 1000.0f, "got %.2f\n", advances[1]);
2034 ok(offsets[0].advanceOffset == 0.0f && offsets[0].ascenderOffset == 0.0f, "got %.2f,%.2f\n",
2035 offsets[0].advanceOffset, offsets[0].ascenderOffset);
2037 IDWriteTextAnalyzer_Release(analyzer);
2038 IDWriteFontFace_Release(fontface);
2039 DELETE_FONTFILE(path);
2042 struct spacing_test {
2043 FLOAT leading;
2044 FLOAT trailing;
2045 FLOAT min_advance;
2046 FLOAT advances[3];
2047 FLOAT offsets[3];
2048 FLOAT modified_advances[3];
2049 FLOAT modified_offsets[3];
2050 BOOL single_cluster;
2051 DWRITE_SHAPING_GLYPH_PROPERTIES props[3];
2054 static const struct spacing_test spacing_tests[] =
2056 /* Default spacing glyph properties. */
2057 #define P_S { 0 }
2058 /* isZeroWidthSpace */
2059 #define P_Z { 0, 0, 0, 1, 0 }
2060 /* isDiacritic */
2061 #define P_D { 0, 0, 1, 0, 0 }
2062 /* isDiacritic + isZeroWidthSpace, that's how diacritics are shaped. */
2063 #define P_D_Z { 0, 0, 1, 1, 0 }
2065 { 0.0, 0.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 } }, /* 0 */
2066 { 0.0, 0.0, 2.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 } },
2067 { 1.0, 0.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 11.0, 12.0 }, { 3.0, 4.0 } },
2068 { 1.0, 1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 12.0, 13.0 }, { 3.0, 4.0 } },
2069 { 1.0, -1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 3.0, 4.0 } },
2070 { 0.0, -10.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 0.0, 1.0 }, { 2.0, 3.0 } }, /* 5 */
2071 { -5.0, -4.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 5.0, 5.0 }, { -1.0, -0.5 } },
2072 { -5.0, -5.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 5.0, 5.0 }, { -0.5, 0.0 } },
2073 { 2.0, 0.0, 5.0, { 1.0, 2.0 }, { 2.0, 3.0 }, { 7.0, 7.0 }, { 6.0, 6.5 } },
2074 { 2.0, 1.0, 5.0, { 1.0, 2.0 }, { 2.0, 3.0 }, { 8.0, 8.0 }, { 6.0, 6.5 } },
2075 { 2.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 7.0, 7.0 }, { 4.0, 5.0 } }, /* 10 */
2076 { 1.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 6.0, 6.0 }, { 3.0, 4.0 } },
2077 { -10.0, 1.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 6.0, 6.0 }, { -3.0, -3.0 } },
2078 { 0.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 5.0, 5.0 }, { 2.0, 3.0 } },
2079 { 1.0, -10.0, -5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 0.0, 0.0 }, { 2.0, 3.0 } },
2080 { -10.0, 1.0, 5.0, { 8.0, 11.0 }, { 2.0, 3.0 }, { 6.0, 6.0 }, { -1.0, -3.0 } }, /* 15 */
2081 /* cluster of more than 1 glyph */
2082 { 0.0f, 0.0f, 0.0f, { 10.0f, 11.0f }, { 2.0f, 3.0f }, { 10.0f, 11.0f }, { 2.0f, 3.0f }, TRUE },
2083 { 1.0f, 0.0f, 0.0f, { 10.0f, 11.0f }, { 2.0f, 3.5f }, { 11.0f, 11.0f }, { 3.0f, 3.5f }, TRUE },
2084 { 1.0f, 1.0f, 0.0f, { 10.0f, 11.0f }, { 2.0f, 3.0f }, { 11.0f, 12.0f }, { 3.0f, 3.0f }, TRUE },
2085 { 1.0, -1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 11.0, 10.0 }, { 3.0, 3.0 }, TRUE },
2086 { 0.0, -10.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 1.0 }, { 2.0, 3.0 }, TRUE }, /* 20 */
2087 { 0.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 1.0 }, { 2.0, 3.0 }, TRUE },
2088 { 1.0, -10.0, -5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 0.0, 0.0 }, { 2.0, 3.0 }, TRUE },
2089 { -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 },
2090 { -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 },
2091 { -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 */
2092 { -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 },
2093 { 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 },
2094 { -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 },
2095 /* isZeroWidthSpace set */
2096 { 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 } },
2097 { 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 */
2098 { 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 } },
2099 { 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 } },
2100 { -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 } },
2101 { -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 } },
2102 { 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 */
2103 { 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 } },
2104 { 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 } },
2105 { 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 } },
2106 { -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 } },
2107 { 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 */
2108 { 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 } },
2109 { -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 } },
2110 /* isDiacritic */
2111 { 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 } },
2112 { 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 } },
2113 { 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 */
2114 { 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 } },
2115 { -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 } },
2116 { -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 } },
2117 { 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 } },
2118 { 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 */
2119 { 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 } },
2120 { 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 } },
2121 { -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 } },
2122 { 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 } },
2123 { 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 */
2124 { -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 } },
2125 /* isZeroWidthSpace in a cluster */
2126 { 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 } },
2127 { 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 } },
2128 { 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 } },
2129 { 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 */
2130 { -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 } },
2131 { -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 } },
2132 { 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 } },
2133 { 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 } },
2134 { 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 } },
2135 { 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 } },
2136 { 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 } },
2137 { 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 } },
2138 { 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 } },
2139 { 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 } },
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_Z, P_Z } }, /* 65 */
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_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_Z, P_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_Z } },
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_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_Z } }, /* 70 */
2146 /* isDiacritic in a cluster */
2147 { 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 } },
2148 { 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 } },
2149 { 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 } },
2150 { 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 } },
2151 { -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 */
2152 { -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 } },
2153 { 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 } },
2154 { 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 } },
2155 { 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 } },
2156 { 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 */
2157 { -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 } },
2158 { 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 } },
2159 { 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 } },
2160 { -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 } },
2161 /* isZeroWidthSpace + isDiacritic */
2162 { 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 */
2163 { 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 } },
2164 { 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 } },
2165 { 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 } },
2166 { -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 } },
2167 { -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 */
2168 { 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 } },
2169 { 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 } },
2170 { 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 } },
2171 { 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 } },
2172 { -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 */
2173 { 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 } },
2174 { 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 } },
2175 { -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 } },
2176 /* isZeroWidthSpace + isDiacritic in a cluster */
2177 { 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 } },
2178 { 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 */
2179 { 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 } },
2180 { 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 } },
2181 { -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 } },
2182 { -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 } },
2183 { 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 */
2184 { 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 } },
2185 { 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 } },
2186 { 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 } },
2187 { -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 } },
2188 { 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 */
2189 { 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 } },
2190 { -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 } },
2192 #undef P_S
2193 #undef P_D
2194 #undef P_Z
2195 #undef P_D_Z
2198 static void test_ApplyCharacterSpacing(void)
2200 DWRITE_SHAPING_GLYPH_PROPERTIES props[3];
2201 IDWriteTextAnalyzer1 *analyzer;
2202 UINT16 clustermap[2];
2203 HRESULT hr;
2204 int i;
2206 analyzer = create_text_analyzer(&IID_IDWriteTextAnalyzer1);
2207 if (!analyzer)
2209 win_skip("ApplyCharacterSpacing() is not supported.\n");
2210 return;
2213 for (i = 0; i < ARRAY_SIZE(spacing_tests); ++i)
2215 const struct spacing_test *ptr = spacing_tests + i;
2216 DWRITE_GLYPH_OFFSET offsets[3];
2217 UINT32 glyph_count;
2218 FLOAT advances[3];
2220 offsets[0].advanceOffset = ptr->offsets[0];
2221 offsets[1].advanceOffset = ptr->offsets[1];
2222 offsets[2].advanceOffset = ptr->offsets[2];
2223 /* Ascender offsets are never touched as spacing applies in reading direction only,
2224 we'll only test them to see if they are not changed */
2225 offsets[0].ascenderOffset = 23.0;
2226 offsets[1].ascenderOffset = 32.0;
2227 offsets[2].ascenderOffset = 31.0;
2229 advances[0] = advances[1] = 123.45f;
2230 memcpy(props, ptr->props, sizeof(props));
2231 glyph_count = ptr->advances[2] > 0.0 ? 3 : 2;
2232 if (ptr->single_cluster)
2234 clustermap[0] = 0;
2235 clustermap[1] = 0;
2236 props[0].isClusterStart = 1;
2238 else
2240 /* trivial case with one glyph per cluster */
2241 clustermap[0] = 0;
2242 clustermap[1] = 1;
2243 props[0].isClusterStart = props[1].isClusterStart = 1;
2246 winetest_push_context("Test %u", i);
2248 hr = IDWriteTextAnalyzer1_ApplyCharacterSpacing(analyzer,
2249 ptr->leading,
2250 ptr->trailing,
2251 ptr->min_advance,
2252 ARRAY_SIZE(clustermap),
2253 glyph_count,
2254 clustermap,
2255 ptr->advances,
2256 offsets,
2257 props,
2258 advances,
2259 offsets);
2260 ok(hr == (ptr->min_advance < 0.0f ? E_INVALIDARG : S_OK), "Unexpected hr %#lx.\n", hr);
2262 if (hr == S_OK) {
2263 ok(ptr->modified_advances[0] == advances[0], "Got advance[0] %.2f, expected %.2f.\n", advances[0], ptr->modified_advances[0]);
2264 ok(ptr->modified_advances[1] == advances[1], "Got advance[1] %.2f, expected %.2f.\n", advances[1], ptr->modified_advances[1]);
2265 if (glyph_count > 2)
2266 ok(ptr->modified_advances[2] == advances[2], "Got advance[2] %.2f, expected %.2f.\n", advances[2], ptr->modified_advances[2]);
2268 ok(ptr->modified_offsets[0] == offsets[0].advanceOffset, "Got offset[0] %.2f, expected %.2f.\n",
2269 offsets[0].advanceOffset, ptr->modified_offsets[0]);
2270 ok(ptr->modified_offsets[1] == offsets[1].advanceOffset, "Got offset[1] %.2f, expected %.2f.\n",
2271 offsets[1].advanceOffset, ptr->modified_offsets[1]);
2272 if (glyph_count > 2)
2273 ok(ptr->modified_offsets[2] == offsets[2].advanceOffset, "Got offset[2] %.2f, expected %.2f.\n",
2274 offsets[2].advanceOffset, ptr->modified_offsets[2]);
2276 ok(offsets[0].ascenderOffset == 23.0, "Unexpected ascenderOffset %.2f.\n", offsets[0].ascenderOffset);
2277 ok(offsets[1].ascenderOffset == 32.0, "Unexpected ascenderOffset %.2f.\n", offsets[1].ascenderOffset);
2278 ok(offsets[2].ascenderOffset == 31.0, "Unexpected ascenderOffset %.2f.\n", offsets[2].ascenderOffset);
2280 else {
2281 ok(ptr->modified_advances[0] == advances[0], "Got advance[0] %.2f, expected %.2f.\n", advances[0], ptr->modified_advances[0]);
2282 ok(ptr->modified_advances[1] == advances[1], "Got advance[1] %.2f, expected %.2f.\n", advances[1], ptr->modified_advances[1]);
2283 ok(ptr->offsets[0] == offsets[0].advanceOffset, "Got offset[0] %.2f, expected %.2f.\n",
2284 offsets[0].advanceOffset, ptr->modified_offsets[0]);
2285 ok(ptr->offsets[1] == offsets[1].advanceOffset, "Got offset[1] %.2f, expected %.2f.\n",
2286 offsets[1].advanceOffset, ptr->modified_offsets[1]);
2287 ok(offsets[0].ascenderOffset == 23.0, "Unexpected ascenderOffset %.2f.\n", offsets[0].ascenderOffset);
2288 ok(offsets[1].ascenderOffset == 32.0, "Unexpected ascenderOffset %.2f.\n", offsets[1].ascenderOffset);
2291 /* same, with argument aliasing */
2292 memcpy(advances, ptr->advances, glyph_count * sizeof(*advances));
2293 offsets[0].advanceOffset = ptr->offsets[0];
2294 offsets[1].advanceOffset = ptr->offsets[1];
2295 offsets[2].advanceOffset = ptr->offsets[2];
2296 /* Ascender offsets are never touched as spacing applies in reading direction only,
2297 we'll only test them to see if they are not changed */
2298 offsets[0].ascenderOffset = 23.0f;
2299 offsets[1].ascenderOffset = 32.0f;
2300 offsets[2].ascenderOffset = 31.0f;
2302 hr = IDWriteTextAnalyzer1_ApplyCharacterSpacing(analyzer,
2303 ptr->leading,
2304 ptr->trailing,
2305 ptr->min_advance,
2306 ARRAY_SIZE(clustermap),
2307 glyph_count,
2308 clustermap,
2309 advances,
2310 offsets,
2311 props,
2312 advances,
2313 offsets);
2314 ok(hr == (ptr->min_advance < 0.0f ? E_INVALIDARG : S_OK), "Unexpected hr %#lx.\n", hr);
2316 if (hr == S_OK)
2318 ok(ptr->modified_advances[0] == advances[0], "Got advance[0] %.2f, expected %.2f.\n", advances[0], ptr->modified_advances[0]);
2319 ok(ptr->modified_advances[1] == advances[1], "Got advance[1] %.2f, expected %.2f.\n", advances[1], ptr->modified_advances[1]);
2320 if (glyph_count > 2)
2321 ok(ptr->modified_advances[2] == advances[2], "Got advance[2] %.2f, expected %.2f.\n", advances[2], ptr->modified_advances[2]);
2323 ok(ptr->modified_offsets[0] == offsets[0].advanceOffset, "Got offset[0] %.2f, expected %.2f.\n",
2324 offsets[0].advanceOffset, ptr->modified_offsets[0]);
2325 ok(ptr->modified_offsets[1] == offsets[1].advanceOffset, "Got offset[1] %.2f, expected %.2f.\n",
2326 offsets[1].advanceOffset, ptr->modified_offsets[1]);
2327 if (glyph_count > 2)
2328 ok(ptr->modified_offsets[2] == offsets[2].advanceOffset, "Got offset[2] %.2f, expected %.2f.\n",
2329 offsets[2].advanceOffset, ptr->modified_offsets[2]);
2331 ok(offsets[0].ascenderOffset == 23.0f, "Unexpected ascenderOffset %.2f.\n", offsets[0].ascenderOffset);
2332 ok(offsets[1].ascenderOffset == 32.0f, "Unexpected ascenderOffset %.2f.\n", offsets[1].ascenderOffset);
2333 ok(offsets[2].ascenderOffset == 31.0f, "Unexpected ascenderOffset %.2f.\n", offsets[2].ascenderOffset);
2335 else
2337 /* with aliased advances original values are retained */
2338 ok(ptr->advances[0] == advances[0], "Got advance[0] %.2f, expected %.2f.\n", advances[0], ptr->advances[0]);
2339 ok(ptr->advances[1] == advances[1], "Got advance[1] %.2f, expected %.2f.\n", advances[1], ptr->advances[1]);
2340 ok(ptr->offsets[0] == offsets[0].advanceOffset, "Got offset[0] %.2f, expected %.2f.\n",
2341 offsets[0].advanceOffset, ptr->modified_offsets[0]);
2342 ok(ptr->offsets[1] == offsets[1].advanceOffset, "Got offset[1] %.2f, expected %.2f.\n",
2343 offsets[1].advanceOffset, ptr->modified_offsets[1]);
2344 ok(offsets[0].ascenderOffset == 23.0f, "Unexpected ascenderOffset %.2f.\n", offsets[0].ascenderOffset);
2345 ok(offsets[1].ascenderOffset == 32.0f, "Unexpected ascenderOffset %.2f.\n", offsets[1].ascenderOffset);
2348 winetest_pop_context();
2351 IDWriteTextAnalyzer1_Release(analyzer);
2354 struct orientation_transf_test {
2355 DWRITE_GLYPH_ORIENTATION_ANGLE angle;
2356 BOOL is_sideways;
2357 DWRITE_MATRIX m;
2360 static const struct orientation_transf_test ot_tests[] = {
2361 { DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES, FALSE, { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 } },
2362 { DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES, FALSE, { 0.0, 1.0, -1.0, 0.0, 0.0, 0.0 } },
2363 { DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES, FALSE, { -1.0, 0.0, 0.0, -1.0, 0.0, 0.0 } },
2364 { DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES, FALSE, { 0.0, -1.0, 1.0, 0.0, 0.0, 0.0 } },
2365 { DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES, TRUE, { 0.0, 1.0, -1.0, 0.0, 0.0, 0.0 } },
2366 { DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES, TRUE, { -1.0, 0.0, 0.0, -1.0, 0.0, 0.0 } },
2367 { DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES, TRUE, { 0.0, -1.0, 1.0, 0.0, 0.0, 0.0 } },
2368 { DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES, TRUE, { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 } }
2371 static inline const char *dbgstr_matrix(const DWRITE_MATRIX *m)
2373 static char buff[64];
2374 sprintf(buff, "{%.2f, %.2f, %.2f, %.2f, %.2f, %.2f}", m->m11, m->m12,
2375 m->m21, m->m22, m->dx, m->dy);
2376 return buff;
2379 static void test_GetGlyphOrientationTransform(void)
2381 IDWriteTextAnalyzer2 *analyzer2;
2382 IDWriteTextAnalyzer1 *analyzer;
2383 FLOAT originx, originy;
2384 DWRITE_MATRIX m;
2385 HRESULT hr;
2386 int i;
2388 analyzer = create_text_analyzer(&IID_IDWriteTextAnalyzer1);
2389 if (!analyzer)
2391 win_skip("GetGlyphOrientationTransform() is not supported.\n");
2392 return;
2395 /* invalid angle value */
2396 memset(&m, 0xcc, sizeof(m));
2397 hr = IDWriteTextAnalyzer1_GetGlyphOrientationTransform(analyzer,
2398 DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES + 1, FALSE, &m);
2399 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
2400 ok(m.m11 == 0.0, "got %.2f\n", m.m11);
2402 for (i = 0; i < ARRAY_SIZE(ot_tests); ++i)
2404 memset(&m, 0, sizeof(m));
2405 hr = IDWriteTextAnalyzer1_GetGlyphOrientationTransform(analyzer, ot_tests[i].angle,
2406 ot_tests[i].is_sideways, &m);
2407 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2408 ok(!memcmp(&ot_tests[i].m, &m, sizeof(m)), "%d: wrong matrix %s\n", i, dbgstr_matrix(&m));
2411 IDWriteTextAnalyzer1_Release(analyzer);
2413 analyzer2 = create_text_analyzer(&IID_IDWriteTextAnalyzer2);
2414 if (!analyzer2)
2416 win_skip("IDWriteTextAnalyzer2::GetGlyphOrientationTransform() is not supported.\n");
2417 return;
2420 /* invalid angle value */
2421 memset(&m, 0xcc, sizeof(m));
2422 hr = IDWriteTextAnalyzer2_GetGlyphOrientationTransform(analyzer2,
2423 DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES + 1, FALSE, 0.0, 0.0, &m);
2424 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
2425 ok(m.m11 == 0.0, "got %.2f\n", m.m11);
2427 originx = 50.0;
2428 originy = 60.0;
2429 for (i = 0; i < ARRAY_SIZE(ot_tests); i++) {
2430 DWRITE_GLYPH_ORIENTATION_ANGLE angle = DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES;
2431 DWRITE_MATRIX m_exp;
2433 memset(&m, 0, sizeof(m));
2435 /* zero offset gives same result as a call from IDWriteTextAnalyzer1 */
2436 hr = IDWriteTextAnalyzer2_GetGlyphOrientationTransform(analyzer2, ot_tests[i].angle,
2437 ot_tests[i].is_sideways, 0.0, 0.0, &m);
2438 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2439 ok(!memcmp(&ot_tests[i].m, &m, sizeof(m)), "%d: wrong matrix %s\n", i, dbgstr_matrix(&m));
2441 m_exp = ot_tests[i].m;
2442 hr = IDWriteTextAnalyzer2_GetGlyphOrientationTransform(analyzer2, ot_tests[i].angle,
2443 ot_tests[i].is_sideways, originx, originy, &m);
2444 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2446 /* 90 degrees more for sideways */
2447 if (ot_tests[i].is_sideways) {
2448 switch (ot_tests[i].angle)
2450 case DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES:
2451 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES;
2452 break;
2453 case DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES:
2454 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES;
2455 break;
2456 case DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES:
2457 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES;
2458 break;
2459 case DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES:
2460 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES;
2461 break;
2462 default:
2466 else
2467 angle = ot_tests[i].angle;
2469 /* set expected offsets */
2470 switch (angle)
2472 case DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES:
2473 break;
2474 case DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES:
2475 m_exp.dx = originx + originy;
2476 m_exp.dy = originy - originx;
2477 break;
2478 case DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES:
2479 m_exp.dx = originx + originx;
2480 m_exp.dy = originy + originy;
2481 break;
2482 case DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES:
2483 m_exp.dx = originx - originy;
2484 m_exp.dy = originy + originx;
2485 break;
2486 default:
2490 ok(!memcmp(&m_exp, &m, sizeof(m)), "%d: wrong matrix %s\n", i, dbgstr_matrix(&m));
2493 IDWriteTextAnalyzer2_Release(analyzer2);
2496 static void test_GetBaseline(void)
2498 DWRITE_SCRIPT_ANALYSIS sa = { 0 };
2499 IDWriteTextAnalyzer1 *analyzer;
2500 IDWriteFontFace *fontface;
2501 INT32 baseline;
2502 BOOL exists;
2503 HRESULT hr;
2505 analyzer = create_text_analyzer(&IID_IDWriteTextAnalyzer1);
2506 if (!analyzer)
2508 win_skip("GetBaseline() is not supported.\n");
2509 return;
2512 fontface = create_fontface();
2514 /* Tahoma does not have a BASE table. */
2516 exists = TRUE;
2517 baseline = 456;
2518 hr = IDWriteTextAnalyzer1_GetBaseline(analyzer, fontface, DWRITE_BASELINE_DEFAULT, FALSE,
2519 TRUE, sa, NULL, &baseline, &exists);
2520 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2521 ok(!baseline, "Unexpected baseline %d.\n", baseline);
2522 ok(!exists, "Unexpected flag %d.\n", exists);
2524 exists = TRUE;
2525 baseline = 456;
2526 hr = IDWriteTextAnalyzer1_GetBaseline(analyzer, fontface, DWRITE_BASELINE_DEFAULT, FALSE,
2527 FALSE, sa, NULL, &baseline, &exists);
2528 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2529 ok(!baseline, "Unexpected baseline %d.\n", baseline);
2530 ok(!exists, "Unexpected flag %d.\n", exists);
2532 exists = TRUE;
2533 baseline = 0;
2534 hr = IDWriteTextAnalyzer1_GetBaseline(analyzer, fontface, DWRITE_BASELINE_CENTRAL, FALSE,
2535 TRUE, sa, NULL, &baseline, &exists);
2536 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2537 ok(baseline != 0, "Unexpected baseline %d.\n", baseline);
2538 ok(!exists, "Unexpected flag %d.\n", exists);
2540 exists = TRUE;
2541 baseline = 0;
2542 hr = IDWriteTextAnalyzer1_GetBaseline(analyzer, fontface, DWRITE_BASELINE_CENTRAL, FALSE,
2543 FALSE, sa, NULL, &baseline, &exists);
2544 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2545 ok(!baseline, "Unexpected baseline %d.\n", baseline);
2546 ok(!exists, "Unexpected flag %d.\n", exists);
2548 exists = TRUE;
2549 baseline = 456;
2550 hr = IDWriteTextAnalyzer1_GetBaseline(analyzer, fontface, DWRITE_BASELINE_DEFAULT + 100, FALSE,
2551 TRUE, sa, NULL, &baseline, &exists);
2552 ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
2553 ok(!baseline, "Unexpected baseline %d.\n", baseline);
2554 ok(!exists, "Unexpected flag %d.\n", exists);
2556 IDWriteFontFace_Release(fontface);
2557 IDWriteTextAnalyzer1_Release(analyzer);
2560 static inline BOOL float_eq(FLOAT left, FLOAT right)
2562 int x = *(int *)&left;
2563 int y = *(int *)&right;
2565 if (x < 0)
2566 x = INT_MIN - x;
2567 if (y < 0)
2568 y = INT_MIN - y;
2570 return abs(x - y) <= 8;
2573 static void test_GetGdiCompatibleGlyphPlacements(void)
2575 DWRITE_SHAPING_GLYPH_PROPERTIES glyphprops[1];
2576 DWRITE_SHAPING_TEXT_PROPERTIES textprops[1];
2577 DWRITE_SCRIPT_ANALYSIS sa = { 0 };
2578 IDWriteTextAnalyzer *analyzer;
2579 IDWriteFontFace *fontface;
2580 UINT16 clustermap[1];
2581 HRESULT hr;
2582 UINT32 count;
2583 UINT16 glyphs[1];
2584 FLOAT advance;
2585 DWRITE_GLYPH_OFFSET offsets[1];
2586 DWRITE_FONT_METRICS fontmetrics;
2587 float emsize;
2589 analyzer = create_text_analyzer(&IID_IDWriteTextAnalyzer);
2590 ok(!!analyzer, "Failed to create analyzer instance.\n");
2592 fontface = create_fontface();
2594 IDWriteFontFace_GetMetrics(fontface, &fontmetrics);
2596 count = 0;
2597 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, L"A", 1, fontface, FALSE, FALSE, &sa, NULL, NULL, NULL, NULL, 0, 1,
2598 clustermap, textprops, glyphs, glyphprops, &count);
2599 ok(hr == S_OK, "Failed to get glyphs, hr %#lx.\n", hr);
2600 ok(count == 1, "got %u\n", count);
2602 for (emsize = 12.0f; emsize <= 20.0f; emsize += 1.0f)
2604 FLOAT compatadvance, expected, ppdip;
2605 DWRITE_GLYPH_METRICS metrics;
2607 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, L"A", clustermap, textprops, 1, glyphs, glyphprops,
2608 count, fontface, emsize, FALSE, FALSE, &sa, NULL, NULL, NULL, 0, &advance, offsets);
2609 ok(hr == S_OK, "Failed to get glyph placements, hr %#lx.\n", hr);
2610 ok(advance > 0.0f, "Unexpected advance %f.\n", advance);
2612 /* 1 ppdip, no transform */
2613 ppdip = 1.0;
2614 hr = IDWriteFontFace_GetGdiCompatibleGlyphMetrics(fontface, emsize, ppdip, NULL, FALSE,
2615 glyphs, 1, &metrics, FALSE);
2616 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2618 expected = floorf(metrics.advanceWidth * emsize * ppdip / fontmetrics.designUnitsPerEm + 0.5f) / ppdip;
2619 hr = IDWriteTextAnalyzer_GetGdiCompatibleGlyphPlacements(analyzer, L"A", clustermap, textprops, 1, glyphs,
2620 glyphprops, count, fontface, emsize, ppdip, NULL, FALSE, FALSE, FALSE, &sa, NULL, NULL, NULL, 0,
2621 &compatadvance, offsets);
2622 ok(hr == S_OK, "Failed to get glyph placements, hr %#lx.\n", hr);
2623 ok(compatadvance == expected, "%.0f: got advance %f, expected %f, natural %f\n", emsize,
2624 compatadvance, expected, advance);
2626 /* 1.2 ppdip, no transform */
2627 ppdip = 1.2f;
2628 hr = IDWriteFontFace_GetGdiCompatibleGlyphMetrics(fontface, emsize, ppdip, NULL, FALSE,
2629 glyphs, 1, &metrics, FALSE);
2630 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2632 expected = floorf(metrics.advanceWidth * emsize * ppdip / fontmetrics.designUnitsPerEm + 0.5f) / ppdip;
2633 hr = IDWriteTextAnalyzer_GetGdiCompatibleGlyphPlacements(analyzer, L"A", clustermap, textprops, 1, glyphs,
2634 glyphprops, count, fontface, emsize, ppdip, NULL, FALSE, FALSE, FALSE, &sa, NULL, NULL, NULL, 0,
2635 &compatadvance, offsets);
2636 ok(hr == S_OK, "Failed to get glyph placements, hr %#lx.\n", hr);
2637 ok(float_eq(compatadvance, expected), "%.0f: got advance %f, expected %f, natural %f\n", emsize,
2638 compatadvance, expected, advance);
2641 IDWriteFontFace_Release(fontface);
2642 IDWriteTextAnalyzer_Release(analyzer);
2645 struct bidi_test
2647 const WCHAR text[BIDI_LEVELS_COUNT];
2648 DWRITE_READING_DIRECTION direction;
2649 UINT8 explicit[BIDI_LEVELS_COUNT];
2650 UINT8 resolved[BIDI_LEVELS_COUNT];
2651 BOOL todo;
2654 static const struct bidi_test bidi_tests[] = {
2656 { 0x645, 0x6cc, 0x200c, 0x6a9, 0x646, 0x645, 0 },
2657 DWRITE_READING_DIRECTION_RIGHT_TO_LEFT,
2658 { 1, 1, 1, 1, 1, 1 },
2659 { 1, 1, 1, 1, 1, 1 }
2662 { 0x645, 0x6cc, 0x200c, 0x6a9, 0x646, 0x645, 0 },
2663 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2664 { 0, 0, 0, 0, 0, 0 },
2665 { 1, 1, 1, 1, 1, 1 }
2668 { 0x200c, 0x645, 0x6cc, 0x6a9, 0x646, 0x645, 0 },
2669 DWRITE_READING_DIRECTION_RIGHT_TO_LEFT,
2670 { 1, 1, 1, 1, 1, 1 },
2671 { 1, 1, 1, 1, 1, 1 }
2674 { 0x200c, 0x645, 0x6cc, 0x6a9, 0x646, 0x645, 0 },
2675 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2676 { 0, 0, 0, 0, 0, 0 },
2677 { 0, 1, 1, 1, 1, 1 }
2680 { 'A', 0x200c, 'B', 0 },
2681 DWRITE_READING_DIRECTION_RIGHT_TO_LEFT,
2682 { 1, 1, 1 },
2683 { 2, 2, 2 }
2686 { 'A', 0x200c, 'B', 0 },
2687 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2688 { 0, 0, 0 },
2689 { 0, 0, 0 }
2692 { LRE, PDF, 'a', 'b', 0 },
2693 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2694 { 2, 2, 0, 0 },
2695 { 0, 0, 0, 0 },
2698 { 'a', LRE, PDF, 'b', 0 },
2699 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2700 { 0, 2, 2, 0 },
2701 { 0, 0, 0, 0 },
2704 { RLE, PDF, 'a', 'b', 0 },
2705 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2706 { 1, 1, 0, 0 },
2707 { 0, 0, 0, 0 },
2710 { 'a', RLE, PDF, 'b', 0 },
2711 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2712 { 0, 1, 1, 0 },
2713 { 0, 0, 0, 0 },
2716 { 'a', RLE, PDF, 'b', 0 },
2717 DWRITE_READING_DIRECTION_RIGHT_TO_LEFT,
2718 { 1, 3, 3, 1 },
2719 { 2, 2, 2, 2 },
2722 { LRE, PDF, 'a', 'b', 0 },
2723 DWRITE_READING_DIRECTION_RIGHT_TO_LEFT,
2724 { 2, 2, 1, 1 },
2725 { 1, 1, 2, 2 },
2728 { PDF, 'a', 'b', 0 },
2729 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2730 { 0, 0, 0, 0 },
2731 { 0, 0, 0, 0 }
2734 { LRE, 'a', 'b', PDF, 0 },
2735 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2736 { 2, 2, 2, 2 },
2737 { 0, 2, 2, 2 },
2738 TRUE
2741 { LRI, 'a', 'b', PDI, 0 },
2742 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2743 { 0, 0, 0, 0 },
2744 { 0, 0, 0, 0 },
2745 TRUE
2748 { RLI, 'a', 'b', PDI, 0 },
2749 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT,
2750 { 0, 0, 0, 0 },
2751 { 0, 0, 0, 0 },
2752 TRUE
2755 { 0 }
2759 static void compare_bidi_levels(unsigned int seq, const struct bidi_test *test, UINT32 len, UINT8 *explicit, UINT8 *resolved)
2761 unsigned int i, failcount = 0;
2762 BOOL match;
2764 match = !memcmp(explicit, test->explicit, len);
2765 if (!match) {
2766 if (test->todo) {
2767 failcount++;
2768 todo_wine
2769 ok(0, "test %u: %s wrong explicit levels:\n", seq, wine_dbgstr_w(test->text));
2771 else
2772 ok(0, "test %u: %s wrong explicit levels:\n", seq, wine_dbgstr_w(test->text));
2774 for (i = 0; i < len; i++) {
2775 if (test->explicit[i] != explicit[i]) {
2776 if (test->todo) {
2777 failcount++;
2778 todo_wine
2779 ok(0, "\tat %u, explicit level %u, expected %u\n", i, explicit[i], test->explicit[i]);
2781 else
2782 ok(0, "\tat %u, explicit level %u, expected %u\n", i, explicit[i], test->explicit[i]);
2787 match = !memcmp(resolved, test->resolved, len);
2788 if (!match) {
2789 if (test->todo) {
2790 failcount++;
2791 todo_wine
2792 ok(0, "test %u: %s wrong resolved levels:\n", seq, wine_dbgstr_w(test->text));
2794 else
2795 ok(0, "test %u: %s wrong resolved levels:\n", seq, wine_dbgstr_w(test->text));
2797 for (i = 0; i < len; i++) {
2798 if (test->resolved[i] != resolved[i]) {
2799 if (test->todo) {
2800 failcount++;
2801 todo_wine
2802 ok(0, "\tat %u, resolved level %u, expected %u\n", i, resolved[i], test->resolved[i]);
2804 else
2805 ok(0, "\tat %u, resolved level %u, expected %u\n", i, resolved[i], test->resolved[i]);
2810 todo_wine_if(test->todo && failcount == 0)
2811 ok(1, "test %u: marked as \"todo_wine\" but succeeds\n", seq);
2814 static void test_AnalyzeBidi(void)
2816 const struct bidi_test *ptr = bidi_tests;
2817 IDWriteTextAnalyzer *analyzer;
2818 UINT32 i = 0;
2819 HRESULT hr;
2821 analyzer = create_text_analyzer(&IID_IDWriteTextAnalyzer);
2822 ok(!!analyzer, "Failed to create analyzer instance.\n");
2824 while (*ptr->text)
2826 UINT32 len;
2828 init_textsource(&analysissource, ptr->text, -1, ptr->direction);
2830 len = lstrlenW(ptr->text);
2831 if (len > BIDI_LEVELS_COUNT) {
2832 ok(0, "test %u: increase BIDI_LEVELS_COUNT to at least %u\n", i, len);
2833 i++;
2834 ptr++;
2835 continue;
2838 memset(g_explicit_levels, 0, sizeof(g_explicit_levels));
2839 memset(g_resolved_levels, 0, sizeof(g_resolved_levels));
2840 hr = IDWriteTextAnalyzer_AnalyzeBidi(analyzer, &analysissource.IDWriteTextAnalysisSource_iface, 0,
2841 len, &analysissink);
2842 ok(hr == S_OK, "%u: unexpected hr %#lx.\n", i, hr);
2843 compare_bidi_levels(i, ptr, len, g_explicit_levels, g_resolved_levels);
2845 i++;
2846 ptr++;
2849 IDWriteTextAnalyzer_Release(analyzer);
2852 enum script_id
2854 Script_Unknown = 0,
2855 Script_Arabic = 3,
2856 Script_Latin = 49,
2859 static void test_glyph_justification_property(void)
2861 static const struct
2863 enum script_id script;
2864 const WCHAR *text;
2865 unsigned short justification[10];
2866 } tests[] =
2869 Script_Latin,
2870 L"a b\tc",
2872 SCRIPT_JUSTIFY_CHARACTER,
2873 SCRIPT_JUSTIFY_BLANK,
2874 SCRIPT_JUSTIFY_CHARACTER,
2875 SCRIPT_JUSTIFY_BLANK,
2876 SCRIPT_JUSTIFY_CHARACTER,
2880 Script_Latin,
2881 L" a b",
2883 SCRIPT_JUSTIFY_BLANK,
2884 SCRIPT_JUSTIFY_CHARACTER,
2885 SCRIPT_JUSTIFY_BLANK,
2886 SCRIPT_JUSTIFY_CHARACTER,
2890 Script_Arabic,
2891 L" a b",
2893 SCRIPT_JUSTIFY_ARABIC_BLANK,
2894 SCRIPT_JUSTIFY_NONE,
2895 SCRIPT_JUSTIFY_ARABIC_BLANK,
2896 SCRIPT_JUSTIFY_NONE,
2899 { Script_Unknown, L"a", { SCRIPT_JUSTIFY_CHARACTER } },
2900 { Script_Latin, L"\x640", { SCRIPT_JUSTIFY_CHARACTER } },
2901 { Script_Arabic, L"\x640", { SCRIPT_JUSTIFY_ARABIC_KASHIDA } },
2903 { Script_Arabic, L"\x633\x627", { SCRIPT_JUSTIFY_ARABIC_SEEN, SCRIPT_JUSTIFY_ARABIC_ALEF } },
2904 { Script_Arabic, L"\x633\x625", { SCRIPT_JUSTIFY_ARABIC_SEEN, SCRIPT_JUSTIFY_ARABIC_ALEF } },
2905 { Script_Arabic, L"\x633\x623", { SCRIPT_JUSTIFY_ARABIC_SEEN, SCRIPT_JUSTIFY_ARABIC_ALEF } },
2906 { Script_Arabic, L"\x633\x622", { SCRIPT_JUSTIFY_ARABIC_SEEN, SCRIPT_JUSTIFY_ARABIC_ALEF } },
2908 { Script_Arabic, L"\x644\x647", { SCRIPT_JUSTIFY_NONE, SCRIPT_JUSTIFY_ARABIC_HA } },
2910 { Script_Arabic, L"\x628\x631", { SCRIPT_JUSTIFY_NONE, SCRIPT_JUSTIFY_ARABIC_BARA } },
2911 { Script_Arabic, L"\x645\x631", { SCRIPT_JUSTIFY_NONE, SCRIPT_JUSTIFY_ARABIC_BARA } },
2912 { Script_Arabic, L"\x645\x632", { SCRIPT_JUSTIFY_NONE, SCRIPT_JUSTIFY_ARABIC_BARA } },
2914 { Script_Arabic, L"\x644\x633\x645", { SCRIPT_JUSTIFY_NONE, SCRIPT_JUSTIFY_ARABIC_SEEN_M, SCRIPT_JUSTIFY_ARABIC_NORMAL } },
2916 DWRITE_SHAPING_GLYPH_PROPERTIES glyph_props[16];
2917 DWRITE_SHAPING_TEXT_PROPERTIES text_props[16];
2918 UINT16 clustermap[16], glyphs[16];
2919 IDWriteTextAnalyzer *analyzer;
2920 DWRITE_SCRIPT_ANALYSIS sa;
2921 IDWriteFontFace *fontface;
2922 UINT32 glyph_count;
2923 unsigned int i, j;
2924 HRESULT hr;
2926 analyzer = create_text_analyzer(&IID_IDWriteTextAnalyzer);
2927 ok(!!analyzer, "Failed to create analyzer instance.\n");
2929 fontface = create_fontface();
2931 for (i = 0; i < ARRAY_SIZE(tests); ++i)
2933 if (tests[i].script == Script_Arabic && !strcmp(winetest_platform, "wine"))
2934 continue;
2936 winetest_push_context("Test %s", debugstr_w(tests[i].text));
2938 sa.script = tests[i].script;
2939 sa.shapes = DWRITE_SCRIPT_SHAPES_DEFAULT;
2941 /* Use RTL for Arabic, it affects returned justification classes. */
2942 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, tests[i].text, wcslen(tests[i].text), fontface,
2943 FALSE, sa.script == Script_Arabic, &sa, L"en-US", NULL, NULL, NULL, 0, ARRAY_SIZE(glyphs), clustermap,
2944 text_props, glyphs, glyph_props, &glyph_count);
2945 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2947 for (j = 0; j < glyph_count; ++j)
2949 winetest_push_context("Glyph %u", j);
2950 ok(glyph_props[j].justification == tests[i].justification[j], "Unexpected justification value %u.\n",
2951 glyph_props[j].justification);
2952 winetest_pop_context();
2955 winetest_pop_context();
2958 IDWriteFontFace_Release(fontface);
2961 START_TEST(analyzer)
2963 HRESULT hr;
2965 hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_ISOLATED, &IID_IDWriteFactory, (IUnknown**)&factory);
2966 ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
2967 if (hr != S_OK)
2969 win_skip("failed to create factory\n");
2970 return;
2973 init_call_sequences(sequences, NUM_CALL_SEQUENCES);
2974 init_call_sequences(expected_seq, 1);
2976 test_AnalyzeScript();
2977 test_AnalyzeLineBreakpoints();
2978 test_AnalyzeBidi();
2979 test_GetScriptProperties();
2980 test_GetTextComplexity();
2981 test_GetGlyphs();
2982 test_numbersubstitution();
2983 test_GetTypographicFeatures();
2984 test_GetGlyphPlacements();
2985 test_ApplyCharacterSpacing();
2986 test_GetGlyphOrientationTransform();
2987 test_GetBaseline();
2988 test_GetGdiCompatibleGlyphPlacements();
2989 test_glyph_justification_property();
2991 IDWriteFactory_Release(factory);