dwrite: Round advances returned from GetGdiCompatibleGlyphPlacements().
[wine/multimedia.git] / dlls / dwrite / tests / analyzer.c
blob402787fbd97d84ce3c7cbba35987690c6764f9ad
1 /*
2 * Text analyzing tests
4 * Copyright 2012-2014 Nikolay Sivov for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #define COBJMACROS
23 #include <assert.h>
24 #include <stdio.h>
25 #include <limits.h>
26 #include <math.h>
28 #include "initguid.h"
29 #include "windows.h"
30 #include "dwrite.h"
31 #include "dwrite_2.h"
33 #include "wine/test.h"
35 static IDWriteFactory *factory;
36 static const WCHAR test_fontfile[] = {'w','i','n','e','_','t','e','s','t','_','f','o','n','t','.','t','t','f',0};
38 enum analysis_kind {
39 ScriptAnalysis,
40 LastKind
43 static const char *get_analysis_kind_name(enum analysis_kind kind)
45 switch (kind)
47 case ScriptAnalysis:
48 return "ScriptAnalysis";
49 default:
50 return "unknown";
54 struct script_analysis {
55 UINT32 pos;
56 UINT32 len;
57 DWRITE_SCRIPT_SHAPES shapes;
60 struct call_entry {
61 enum analysis_kind kind;
62 struct script_analysis sa;
65 struct testcontext {
66 enum analysis_kind kind;
67 BOOL todo;
68 int *failcount;
69 const char *file;
70 int line;
73 struct call_sequence
75 int count;
76 int size;
77 struct call_entry *sequence;
80 #define NUM_CALL_SEQUENCES 1
81 #define ANALYZER_ID 0
82 static struct call_sequence *sequences[NUM_CALL_SEQUENCES];
83 static struct call_sequence *expected_seq[1];
85 static void add_call(struct call_sequence **seq, int sequence_index, const struct call_entry *call)
87 struct call_sequence *call_seq = seq[sequence_index];
89 if (!call_seq->sequence)
91 call_seq->size = 10;
92 call_seq->sequence = HeapAlloc(GetProcessHeap(), 0,
93 call_seq->size * sizeof (struct call_entry));
96 if (call_seq->count == call_seq->size)
98 call_seq->size *= 2;
99 call_seq->sequence = HeapReAlloc(GetProcessHeap(), 0,
100 call_seq->sequence,
101 call_seq->size * sizeof (struct call_entry));
104 assert(call_seq->sequence);
106 call_seq->sequence[call_seq->count++] = *call;
109 static inline void flush_sequence(struct call_sequence **seg, int sequence_index)
111 struct call_sequence *call_seq = seg[sequence_index];
113 HeapFree(GetProcessHeap(), 0, call_seq->sequence);
114 call_seq->sequence = NULL;
115 call_seq->count = call_seq->size = 0;
118 static inline void flush_sequences(struct call_sequence **seq, int n)
120 int i;
121 for (i = 0; i < n; i++)
122 flush_sequence(seq, i);
125 static void init_call_sequences(struct call_sequence **seq, int n)
127 int i;
129 for (i = 0; i < n; i++)
130 seq[i] = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct call_sequence));
133 static void test_uint(UINT32 actual, UINT32 expected, const char *name, const struct testcontext *ctxt)
135 if (expected != actual && ctxt->todo)
137 (*ctxt->failcount)++;
138 ok_(ctxt->file, ctxt->line) (0, "%s: \"%s\" expecting %u, got %u\n", get_analysis_kind_name(ctxt->kind), name, expected, actual);
140 else
141 ok_(ctxt->file, ctxt->line) (expected == actual, "%s: \"%s\" expecting %u, got %u\n", get_analysis_kind_name(ctxt->kind), name,
142 expected, actual);
145 static void ok_sequence_(struct call_sequence **seq, int sequence_index,
146 const struct call_entry *expected, const char *context, BOOL todo,
147 const char *file, int line)
149 struct call_sequence *call_seq = seq[sequence_index];
150 static const struct call_entry end_of_sequence = { LastKind };
151 const struct call_entry *actual, *sequence;
152 int failcount = 0;
153 struct testcontext ctxt;
155 add_call(seq, sequence_index, &end_of_sequence);
157 sequence = call_seq->sequence;
158 actual = sequence;
160 ctxt.failcount = &failcount;
161 ctxt.todo = todo;
162 ctxt.file = file;
163 ctxt.line = line;
165 while (expected->kind != LastKind && actual->kind != LastKind)
167 if (expected->kind == actual->kind)
169 ctxt.kind = expected->kind;
171 switch (actual->kind)
173 case ScriptAnalysis:
174 test_uint(actual->sa.pos, expected->sa.pos, "position", &ctxt);
175 test_uint(actual->sa.len, expected->sa.len, "length", &ctxt);
176 test_uint(actual->sa.shapes, expected->sa.shapes, "shapes", &ctxt);
177 break;
178 default:
179 ok(0, "%s: callback not handled, %s\n", context, get_analysis_kind_name(actual->kind));
181 expected++;
182 actual++;
184 else if (todo)
186 failcount++;
187 todo_wine
189 ok_(file, line) (0, "%s: call %s was expected, but got call %s instead\n",
190 context, get_analysis_kind_name(expected->kind), get_analysis_kind_name(actual->kind));
193 flush_sequence(seq, sequence_index);
194 return;
196 else
198 ok_(file, line) (0, "%s: call %s was expected, but got call %s instead\n",
199 context, get_analysis_kind_name(expected->kind), get_analysis_kind_name(actual->kind));
200 expected++;
201 actual++;
205 if (todo)
207 todo_wine
209 if (expected->kind != LastKind || actual->kind != LastKind)
211 failcount++;
212 ok_(file, line) (0, "%s: the call sequence is not complete: expected %s - actual %s\n",
213 context, get_analysis_kind_name(expected->kind), get_analysis_kind_name(actual->kind));
217 else if (expected->kind != LastKind || actual->kind != LastKind)
219 ok_(file, line) (0, "%s: the call sequence is not complete: expected %s - actual %s\n",
220 context, get_analysis_kind_name(expected->kind), get_analysis_kind_name(actual->kind));
223 if (todo && !failcount) /* succeeded yet marked todo */
225 todo_wine
227 ok_(file, line)(1, "%s: marked \"todo_wine\" but succeeds\n", context);
231 flush_sequence(seq, sequence_index);
234 #define ok_sequence(seq, index, exp, contx, todo) \
235 ok_sequence_(seq, index, (exp), (contx), (todo), __FILE__, __LINE__)
237 static HRESULT WINAPI analysissink_QueryInterface(IDWriteTextAnalysisSink *iface, REFIID riid, void **obj)
239 if (IsEqualIID(riid, &IID_IDWriteTextAnalysisSink) || IsEqualIID(riid, &IID_IUnknown))
241 *obj = iface;
242 return S_OK;
245 *obj = NULL;
246 return E_NOINTERFACE;
249 static ULONG WINAPI analysissink_AddRef(IDWriteTextAnalysisSink *iface)
251 return 2;
254 static ULONG WINAPI analysissink_Release(IDWriteTextAnalysisSink *iface)
256 return 1;
259 static HRESULT WINAPI analysissink_SetScriptAnalysis(IDWriteTextAnalysisSink *iface,
260 UINT32 position, UINT32 length, DWRITE_SCRIPT_ANALYSIS const* sa)
262 struct call_entry entry;
263 entry.kind = ScriptAnalysis;
264 entry.sa.pos = position;
265 entry.sa.len = length;
266 entry.sa.shapes = sa->shapes;
267 add_call(sequences, ANALYZER_ID, &entry);
268 return S_OK;
271 static DWRITE_SCRIPT_ANALYSIS g_sa;
272 static HRESULT WINAPI analysissink_SetScriptAnalysis2(IDWriteTextAnalysisSink *iface,
273 UINT32 position, UINT32 length, DWRITE_SCRIPT_ANALYSIS const* sa)
275 g_sa = *sa;
276 return S_OK;
279 #define BREAKPOINT_COUNT 20
280 static DWRITE_LINE_BREAKPOINT g_actual_bp[BREAKPOINT_COUNT];
282 static HRESULT WINAPI analysissink_SetLineBreakpoints(IDWriteTextAnalysisSink *iface,
283 UINT32 position,
284 UINT32 length,
285 DWRITE_LINE_BREAKPOINT const* breakpoints)
287 if (position + length > BREAKPOINT_COUNT) {
288 ok(0, "SetLineBreakpoints: reported pos=%u, len=%u overflows expected length %d\n", position, length, BREAKPOINT_COUNT);
289 return E_FAIL;
291 memcpy(&g_actual_bp[position], breakpoints, length*sizeof(DWRITE_LINE_BREAKPOINT));
292 return S_OK;
295 static HRESULT WINAPI analysissink_SetBidiLevel(IDWriteTextAnalysisSink *iface,
296 UINT32 position,
297 UINT32 length,
298 UINT8 explicitLevel,
299 UINT8 resolvedLevel)
301 ok(0, "unexpected\n");
302 return E_NOTIMPL;
305 static HRESULT WINAPI analysissink_SetNumberSubstitution(IDWriteTextAnalysisSink *iface,
306 UINT32 position,
307 UINT32 length,
308 IDWriteNumberSubstitution* substitution)
310 ok(0, "unexpected\n");
311 return E_NOTIMPL;
314 static IDWriteTextAnalysisSinkVtbl analysissinkvtbl = {
315 analysissink_QueryInterface,
316 analysissink_AddRef,
317 analysissink_Release,
318 analysissink_SetScriptAnalysis,
319 analysissink_SetLineBreakpoints,
320 analysissink_SetBidiLevel,
321 analysissink_SetNumberSubstitution
324 static IDWriteTextAnalysisSinkVtbl analysissinkvtbl2 = {
325 analysissink_QueryInterface,
326 analysissink_AddRef,
327 analysissink_Release,
328 analysissink_SetScriptAnalysis2,
329 analysissink_SetLineBreakpoints,
330 analysissink_SetBidiLevel,
331 analysissink_SetNumberSubstitution
334 static IDWriteTextAnalysisSink analysissink = { &analysissinkvtbl };
335 static IDWriteTextAnalysisSink analysissink2 = { &analysissinkvtbl2 };
337 static HRESULT WINAPI analysissource_QueryInterface(IDWriteTextAnalysisSource *iface,
338 REFIID riid, void **obj)
340 ok(0, "QueryInterface not expected\n");
341 return E_NOTIMPL;
344 static ULONG WINAPI analysissource_AddRef(IDWriteTextAnalysisSource *iface)
346 ok(0, "AddRef not expected\n");
347 return 2;
350 static ULONG WINAPI analysissource_Release(IDWriteTextAnalysisSource *iface)
352 ok(0, "Release not expected\n");
353 return 1;
356 static const WCHAR *g_source;
358 static HRESULT WINAPI analysissource_GetTextAtPosition(IDWriteTextAnalysisSource *iface,
359 UINT32 position, WCHAR const** text, UINT32* text_len)
361 if (position >= lstrlenW(g_source))
363 *text = NULL;
364 *text_len = 0;
366 else
368 *text = &g_source[position];
369 *text_len = lstrlenW(g_source) - position;
372 return S_OK;
375 static HRESULT WINAPI analysissource_GetTextBeforePosition(IDWriteTextAnalysisSource *iface,
376 UINT32 position, WCHAR const** text, UINT32* text_len)
378 ok(0, "unexpected\n");
379 return E_NOTIMPL;
382 static DWRITE_READING_DIRECTION WINAPI analysissource_GetParagraphReadingDirection(
383 IDWriteTextAnalysisSource *iface)
385 ok(0, "unexpected\n");
386 return DWRITE_READING_DIRECTION_RIGHT_TO_LEFT;
389 static HRESULT WINAPI analysissource_GetLocaleName(IDWriteTextAnalysisSource *iface,
390 UINT32 position, UINT32* text_len, WCHAR const** locale)
392 *locale = NULL;
393 *text_len = 0;
394 return S_OK;
397 static HRESULT WINAPI analysissource_GetNumberSubstitution(IDWriteTextAnalysisSource *iface,
398 UINT32 position, UINT32* text_len, IDWriteNumberSubstitution **substitution)
400 ok(0, "unexpected\n");
401 return E_NOTIMPL;
404 static IDWriteTextAnalysisSourceVtbl analysissourcevtbl = {
405 analysissource_QueryInterface,
406 analysissource_AddRef,
407 analysissource_Release,
408 analysissource_GetTextAtPosition,
409 analysissource_GetTextBeforePosition,
410 analysissource_GetParagraphReadingDirection,
411 analysissource_GetLocaleName,
412 analysissource_GetNumberSubstitution
415 static IDWriteTextAnalysisSource analysissource = { &analysissourcevtbl };
417 static IDWriteFontFace *create_fontface(void)
419 static const WCHAR tahomaW[] = {'T','a','h','o','m','a',0};
420 IDWriteGdiInterop *interop;
421 IDWriteFontFace *fontface;
422 IDWriteFont *font;
423 LOGFONTW logfont;
424 HRESULT hr;
426 hr = IDWriteFactory_GetGdiInterop(factory, &interop);
427 ok(hr == S_OK, "got 0x%08x\n", hr);
429 memset(&logfont, 0, sizeof(logfont));
430 logfont.lfHeight = 12;
431 logfont.lfWidth = 12;
432 logfont.lfWeight = FW_NORMAL;
433 logfont.lfItalic = 1;
434 lstrcpyW(logfont.lfFaceName, tahomaW);
436 hr = IDWriteGdiInterop_CreateFontFromLOGFONT(interop, &logfont, &font);
437 ok(hr == S_OK, "got 0x%08x\n", hr);
439 hr = IDWriteFont_CreateFontFace(font, &fontface);
440 ok(hr == S_OK, "got 0x%08x\n", hr);
442 IDWriteFont_Release(font);
443 IDWriteGdiInterop_Release(interop);
445 return fontface;
448 static WCHAR *create_testfontfile(const WCHAR *filename)
450 static WCHAR pathW[MAX_PATH];
451 DWORD written;
452 HANDLE file;
453 HRSRC res;
454 void *ptr;
456 GetTempPathW(sizeof(pathW)/sizeof(WCHAR), pathW);
457 lstrcatW(pathW, filename);
459 file = CreateFileW(pathW, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0);
460 ok(file != INVALID_HANDLE_VALUE, "file creation failed, at %s, error %d\n", wine_dbgstr_w(pathW),
461 GetLastError());
463 res = FindResourceA(GetModuleHandleA(NULL), (LPCSTR)MAKEINTRESOURCE(1), (LPCSTR)RT_RCDATA);
464 ok(res != 0, "couldn't find resource\n");
465 ptr = LockResource(LoadResource(GetModuleHandleA(NULL), res));
466 WriteFile(file, ptr, SizeofResource(GetModuleHandleA(NULL), res), &written, NULL);
467 ok(written == SizeofResource(GetModuleHandleA(NULL), res), "couldn't write resource\n");
468 CloseHandle(file);
470 return pathW;
473 #define DELETE_FONTFILE(filename) _delete_testfontfile(filename, __LINE__)
474 static void _delete_testfontfile(const WCHAR *filename, int line)
476 BOOL ret = DeleteFileW(filename);
477 ok_(__FILE__,line)(ret, "failed to delete file %s, error %d\n", wine_dbgstr_w(filename), GetLastError());
480 static IDWriteFontFace *create_testfontface(const WCHAR *filename)
482 IDWriteFontFace *face;
483 IDWriteFontFile *file;
484 HRESULT hr;
486 hr = IDWriteFactory_CreateFontFileReference(factory, filename, NULL, &file);
487 ok(hr == S_OK, "got 0x%08x\n",hr);
489 hr = IDWriteFactory_CreateFontFace(factory, DWRITE_FONT_FACE_TYPE_TRUETYPE, 1, &file, 0,
490 DWRITE_FONT_SIMULATIONS_NONE, &face);
491 ok(hr == S_OK, "got 0x%08x\n", hr);
492 IDWriteFontFile_Release(file);
494 return face;
497 struct sa_test {
498 const WCHAR string[50];
499 int item_count;
500 struct script_analysis sa[10];
503 static struct sa_test sa_tests[] = {
505 /* just 1 char string */
506 {'t',0}, 1,
507 { { 0, 1, DWRITE_SCRIPT_SHAPES_DEFAULT }}
510 {'t','e','s','t',0}, 1,
511 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT }}
514 {' ',' ',' ',' ','!','$','[','^','{','~',0}, 1,
515 { { 0, 10, DWRITE_SCRIPT_SHAPES_DEFAULT }}
518 {' ',' ',' ','1','2',' ',0}, 1,
519 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
522 /* digits only */
523 {'1','2',0}, 1,
524 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }}
527 /* Arabic */
528 {0x064a,0x064f,0x0633,0x0627,0x0648,0x0650,0x064a,0x0661,0}, 1,
529 { { 0, 8, DWRITE_SCRIPT_SHAPES_DEFAULT }}
532 /* Arabic */
533 {0x0627,0x0644,0x0635,0x0651,0x0650,0x062d,0x0629,0x064f,' ',0x062a,0x064e,
534 0x0627,0x062c,0x064c,' ',0x0639,0x064e,0x0644,0x0649,' ',
535 0x0631,0x064f,0x0624,0x0648,0x0633,0x0650,' ',0x0627,0x0644,
536 0x0623,0x0635,0x0650,0x062d,0x0651,0x064e,0x0627,0x0621,0x0650,0x06f0,0x06f5,0}, 1,
537 { { 0, 40, DWRITE_SCRIPT_SHAPES_DEFAULT }}
540 /* Arabic, Latin */
541 {'1','2','3','-','5','2',0x064a,0x064f,0x0633,0x0627,0x0648,0x0650,0x064a,'7','1','.',0}, 1,
542 { { 0, 16, DWRITE_SCRIPT_SHAPES_DEFAULT }}
545 /* Arabic, English */
546 {'A','B','C','-','D','E','F',' ',0x0621,0x0623,0x0624,0}, 2,
547 { { 0, 8, DWRITE_SCRIPT_SHAPES_DEFAULT },
548 { 8, 3, DWRITE_SCRIPT_SHAPES_DEFAULT },
552 /* leading space, Arabic, English */
553 {' ',0x0621,0x0623,0x0624,'A','B','C','-','D','E','F',0}, 2,
554 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT },
555 { 4, 7, DWRITE_SCRIPT_SHAPES_DEFAULT },
559 /* English, Arabic, trailing space */
560 {'A','B','C','-','D','E','F',0x0621,0x0623,0x0624,' ',0}, 2,
561 { { 0, 7, DWRITE_SCRIPT_SHAPES_DEFAULT },
562 { 7, 4, DWRITE_SCRIPT_SHAPES_DEFAULT },
566 /* C1 Controls, Latin-1 Supplement */
567 {0x80,0x90,0x9f,0xa0,0xc0,0xb8,0xbf,0xc0,0xff,0}, 2,
568 { { 0, 3, DWRITE_SCRIPT_SHAPES_NO_VISUAL },
569 { 3, 6, DWRITE_SCRIPT_SHAPES_DEFAULT },
573 /* Latin Extended-A */
574 {0x100,0x120,0x130,0x140,0x150,0x160,0x170,0x17f,0}, 1,
575 { { 0, 8, DWRITE_SCRIPT_SHAPES_DEFAULT }}
578 /* Latin Extended-B */
579 {0x180,0x190,0x1bf,0x1c0,0x1c3,0x1c4,0x1cc,0x1dc,0x1ff,0x217,0x21b,0x24f,0}, 1,
580 { { 0, 12, DWRITE_SCRIPT_SHAPES_DEFAULT }}
583 /* IPA Extensions */
584 {0x250,0x260,0x270,0x290,0x2af,0}, 1,
585 { { 0, 5, DWRITE_SCRIPT_SHAPES_DEFAULT }}
588 /* Spacing Modifier Letters */
589 {0x2b0,0x2ba,0x2d7,0x2dd,0x2ef,0x2ff,0}, 1,
590 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
593 /* Combining Diacritical Marks */
594 {0x300,0x320,0x340,0x345,0x350,0x36f,0}, 1,
595 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
598 /* Greek and Coptic */
599 {0x370,0x388,0x3d8,0x3e1,0x3e2,0x3fa,0x3ff,0}, 3,
600 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT },
601 { 4, 1, DWRITE_SCRIPT_SHAPES_DEFAULT },
602 { 5, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }
606 /* Cyrillic and Cyrillic Supplement */
607 {0x400,0x40f,0x410,0x44f,0x450,0x45f,0x460,0x481,0x48a,0x4f0,0x4fa,0x4ff,0x500,0x510,0x520,0}, 1,
608 { { 0, 15, DWRITE_SCRIPT_SHAPES_DEFAULT }}
611 /* Armenian */
612 {0x531,0x540,0x559,0x55f,0x570,0x589,0x58a,0}, 1,
613 { { 0, 7, DWRITE_SCRIPT_SHAPES_DEFAULT }}
616 /* Hebrew */
617 {0x5e9,0x5dc,0x5d5,0x5dd,0}, 1,
618 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT }}
621 /* Latin, Hebrew, Latin */
622 {'p','a','r','t',' ','o','n','e',' ',0x5d7,0x5dc,0x5e7,' ',0x5e9,0x5ea,0x5d9,0x5d9,0x5dd,' ','p','a','r','t',' ','t','h','r','e','e',0}, 3,
623 { { 0, 9, DWRITE_SCRIPT_SHAPES_DEFAULT },
624 { 9, 10, DWRITE_SCRIPT_SHAPES_DEFAULT },
625 { 19, 10, DWRITE_SCRIPT_SHAPES_DEFAULT }}
628 /* Syriac */
629 {0x710,0x712,0x712,0x714,'.',0}, 1,
630 { { 0, 5, DWRITE_SCRIPT_SHAPES_DEFAULT }}
633 /* Arabic Supplement */
634 {0x750,0x760,0x76d,'.',0}, 1,
635 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT }}
638 /* Thaana */
639 {0x780,0x78e,0x798,0x7a6,0x7b0,'.',0}, 1,
640 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
643 /* N'Ko */
644 {0x7c0,0x7ca,0x7e8,0x7eb,0x7f6,'.',0}, 1,
645 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
648 /* Thaana */
649 {0x780,0x798,0x7a5,0x7a6,0x7b0,'.',0}, 1,
650 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
653 /* Devanagari */
654 {0x926,0x947,0x935,0x928,0x93e,0x917,0x930,0x940,'.',0}, 1,
655 { { 0, 9, DWRITE_SCRIPT_SHAPES_DEFAULT }}
658 /* Bengali */
659 {0x9ac,0x9be,0x982,0x9b2,0x9be,'.',0}, 1,
660 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
663 /* Gurmukhi */
664 {0xa17,0xa41,0xa30,0xa2e,0xa41,0xa16,0xa40,'.',0}, 1,
665 { { 0, 8, DWRITE_SCRIPT_SHAPES_DEFAULT }}
668 /* Gujarati */
669 {0xa97,0xac1,0xa9c,0xab0,0xabe,0xaa4,0xac0,'.',0}, 1,
670 { { 0, 8, DWRITE_SCRIPT_SHAPES_DEFAULT }}
673 /* Oriya */
674 {0xb13,0xb21,0xb3c,0xb3f,0xb06,'.',0}, 1,
675 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
678 /* Tamil */
679 {0xba4,0xbae,0xbbf,0xbb4,0xbcd,'.',0}, 1,
680 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
683 /* Telugu */
684 {0xc24,0xc46,0xc32,0xc41,0xc17,0xc41,'.',0}, 1,
685 { { 0, 7, DWRITE_SCRIPT_SHAPES_DEFAULT }}
688 /* Kannada */
689 {0xc95,0xca8,0xccd,0xca8,0xca1,'.',0}, 1,
690 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
693 /* Malayalam */
694 {0xd2e,0xd32,0xd2f,0xd3e,0xd33,0xd02,'.',0}, 1,
695 { { 0, 7, DWRITE_SCRIPT_SHAPES_DEFAULT }}
698 /* Sinhala */
699 {0xd82,0xd85,0xd9a,0xdcf,'.',0}, 1,
700 { { 0, 5, DWRITE_SCRIPT_SHAPES_DEFAULT }}
703 /* Thai */
704 {0x0e04,0x0e27,0x0e32,0x0e21,0x0e1e,0x0e22,0x0e32,0x0e22,0x0e32,0x0e21,
705 0x0e2d,0x0e22,0x0e39,0x0e48,0x0e17,0x0e35,0x0e48,0x0e44,0x0e2b,0x0e19,
706 0x0e04,0x0e27,0x0e32,0x0e21,0x0e2a, 0x0e33,0x0e40,0x0e23,0x0e47,0x0e08,
707 0x0e2d,0x0e22,0x0e39,0x0e48,0x0e17,0x0e35,0x0e48,0x0e19,0x0e31,0x0e48,0x0e19,'.',0}, 1,
708 { { 0, 42, DWRITE_SCRIPT_SHAPES_DEFAULT }}
711 /* Lao */
712 {0xead,0xeb1,0xe81,0xeaa,0xead,0xe99,0xea5,0xeb2,0xea7,'.',0}, 1,
713 { { 0, 10, DWRITE_SCRIPT_SHAPES_DEFAULT }}
716 /* Tibetan */
717 {0xf04,0xf05,0xf0e,0x020,0xf51,0xf7c,0xf53,0xf0b,0xf5a,0xf53,0xf0b,
718 0xf51,0xf44,0xf0b,0xf54,0xf7c,0xf0d,'.',0}, 1,
719 { { 0, 18, DWRITE_SCRIPT_SHAPES_DEFAULT }}
722 /* Myanmar */
723 {0x1019,0x103c,0x1014,0x103a,0x1019,0x102c,0x1021,0x1000,0x1039,0x1001,0x101b,0x102c,'.',0}, 1,
724 { { 0, 13, DWRITE_SCRIPT_SHAPES_DEFAULT }}
727 /* Georgian */
728 {0x10a0,0x10d0,0x10da,0x10f1,0x10fb,0x2d00,'.',0}, 1,
729 { { 0, 7, DWRITE_SCRIPT_SHAPES_DEFAULT }}
732 /* Hangul */
733 {0x1100,0x1110,0x1160,0x1170,0x11a8,'.',0}, 1,
734 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
737 /* Ethiopic */
738 {0x130d,0x12d5,0x12dd,0}, 1,
739 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
742 /* Cherokee */
743 {0x13e3,0x13b3,0x13a9,0x0020,0x13a6,0x13ec,0x13c2,0x13af,0x13cd,0x13d7,0}, 1,
744 { { 0, 10, DWRITE_SCRIPT_SHAPES_DEFAULT }}
747 /* Canadian */
748 {0x1403,0x14c4,0x1483,0x144e,0x1450,0x1466,0}, 1,
749 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
752 /* Ogham */
753 {0x169b,0x1691,0x168c,0x1690,0x168b,0x169c,0}, 1,
754 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
757 /* Runic */
758 {0x16a0,0x16a1,0x16a2,0x16a3,0x16a4,0x16a5,0}, 1,
759 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT }}
762 /* Khmer */
763 {0x1781,0x17c1,0x1798,0x179a,0x1797,0x17b6,0x179f,0x17b6,0x19e0,0}, 1,
764 { { 0, 9, DWRITE_SCRIPT_SHAPES_DEFAULT }}
767 /* Mongolian */
768 {0x182e,0x1823,0x1829,0x182d,0x1823,0x182f,0x0020,0x182a,0x1822,0x1834,0x1822,0x182d,0x180c,0}, 1,
769 { { 0, 13, DWRITE_SCRIPT_SHAPES_DEFAULT }}
772 /* Limbu */
773 {0x1900,0x1910,0x1920,0x1930,0}, 1,
774 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT }}
777 /* Tai Le */
778 {0x1956,0x196d,0x1970,0x1956,0x196c,0x1973,0x1951,0x1968,0x1952,0x1970,0}, 1,
779 { { 0, 10, DWRITE_SCRIPT_SHAPES_DEFAULT }}
782 /* New Tai Lue */
783 {0x1992,0x19c4,0}, 1,
784 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }}
787 /* Buginese */
788 {0x1a00,0x1a10,0}, 1,
789 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }}
792 /* Tai Tham */
793 {0x1a20,0x1a40,0x1a50,0}, 1,
794 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
797 /* Balinese */
798 {0x1b00,0x1b05,0x1b20,0}, 1,
799 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
802 /* Sundanese */
803 {0x1b80,0x1b85,0x1ba0,0}, 1,
804 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
807 /* Batak */
808 {0x1bc0,0x1be5,0x1bfc,0}, 1,
809 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
812 /* Lepcha */
813 {0x1c00,0x1c20,0x1c40,0}, 1,
814 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
817 /* Ol Chiki */
818 {0x1c50,0x1c5a,0x1c77,0}, 1,
819 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
822 /* Sundanese Supplement */
823 {0x1cc0,0x1cc5,0x1cc7,0}, 1,
824 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
827 /* Phonetic Extensions */
828 {0x1d00,0x1d40,0x1d70,0}, 1,
829 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
832 /* Combining diacritical marks */
833 {0x1dc0,0x300,0x1ddf,0}, 1,
834 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
837 /* Latin Extended Additional, Extended-C */
838 {0x1e00,0x1d00,0x2c60,0}, 1,
839 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
842 /* Greek Extended */
843 {0x3f0,0x1f00,0}, 1,
844 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }}
847 /* General Punctuation */
848 {0x1dc0,0x2000,0}, 1,
849 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }}
852 /* Superscripts and Subscripts */
853 {0x2070,0x2086,0x2000,0}, 1,
854 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
857 /* Currency, Combining Diacritical Marks for Symbols. Letterlike Symbols.. */
858 {0x20a0,0x20b8,0x2000,0x20d0,0x2100,0x2150,0x2190,0x2200,0x2300,0x2400,0x2440,0x2460,0x2500,0x2580,0x25a0,0x2600,
859 0x2700,0x27c0,0x27f0,0x2900,0x2980,0x2a00,0x2b00,0}, 1,
860 { { 0, 23, DWRITE_SCRIPT_SHAPES_DEFAULT }}
863 /* Braille */
864 {0x2800,0x2070,0x2000,0}, 1,
865 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT }}
868 /* Glagolitic */
869 {0x2c00,0x2c12,0}, 1,
870 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }}
873 /* Coptic */
874 {0x2c80,0x3e2,0x1f00,0}, 2,
875 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT },
876 { 2, 1, DWRITE_SCRIPT_SHAPES_DEFAULT } }
879 /* Tifinagh */
880 {0x2d30,0x2d4a,0}, 1,
881 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT }}
884 /* LRE/PDF */
885 {0x202a,0x202c,'a','b','c','\r',0}, 3,
886 { { 0, 2, DWRITE_SCRIPT_SHAPES_NO_VISUAL },
887 { 2, 3, DWRITE_SCRIPT_SHAPES_DEFAULT },
888 { 5, 1, DWRITE_SCRIPT_SHAPES_NO_VISUAL } }
891 /* LRE/PDF and other visual and non-visual codes from Common script range */
892 {0x202a,0x202c,'r','!',0x200b,'\r',0}, 3,
893 { { 0, 2, DWRITE_SCRIPT_SHAPES_NO_VISUAL },
894 { 2, 2, DWRITE_SCRIPT_SHAPES_DEFAULT },
895 { 4, 2, DWRITE_SCRIPT_SHAPES_NO_VISUAL } }
897 /* keep this as end marker */
898 { {0} }
901 static void init_expected_sa(struct call_sequence **seq, const struct sa_test *test)
903 static const struct call_entry end_of_sequence = { LastKind };
904 int i;
906 flush_sequence(seq, 0);
908 /* add expected calls */
909 for (i = 0; i < test->item_count; i++)
911 struct call_entry call;
913 call.kind = ScriptAnalysis;
914 call.sa.pos = test->sa[i].pos;
915 call.sa.len = test->sa[i].len;
916 call.sa.shapes = test->sa[i].shapes;
917 add_call(seq, 0, &call);
920 /* and stop marker */
921 add_call(seq, 0, &end_of_sequence);
924 static void get_script_analysis(const WCHAR *str, DWRITE_SCRIPT_ANALYSIS *sa)
926 IDWriteTextAnalyzer *analyzer;
927 HRESULT hr;
929 g_source = str;
931 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
932 ok(hr == S_OK, "got 0x%08x\n", hr);
934 hr = IDWriteTextAnalyzer_AnalyzeScript(analyzer, &analysissource, 0, lstrlenW(g_source), &analysissink2);
935 ok(hr == S_OK, "got 0x%08x\n", hr);
937 *sa = g_sa;
940 static void test_AnalyzeScript(void)
942 const struct sa_test *ptr = sa_tests;
943 IDWriteTextAnalyzer *analyzer;
944 HRESULT hr;
946 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
947 ok(hr == S_OK, "got 0x%08x\n", hr);
949 while (*ptr->string)
951 g_source = ptr->string;
953 init_expected_sa(expected_seq, ptr);
954 hr = IDWriteTextAnalyzer_AnalyzeScript(analyzer, &analysissource, 0, lstrlenW(g_source), &analysissink);
955 ok(hr == S_OK, "got 0x%08x\n", hr);
956 ok_sequence(sequences, ANALYZER_ID, expected_seq[0]->sequence, wine_dbgstr_w(ptr->string), FALSE);
957 ptr++;
960 IDWriteTextAnalyzer_Release(analyzer);
963 struct linebreaks_test {
964 const WCHAR text[BREAKPOINT_COUNT+1];
965 DWRITE_LINE_BREAKPOINT bp[BREAKPOINT_COUNT];
968 static struct linebreaks_test linebreaks_tests[] = {
969 { {'A','-','B',' ','C',0x58a,'D',0x2010,'E',0x2012,'F',0x2013,'\t',0},
971 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, FALSE, FALSE },
972 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, FALSE, FALSE },
973 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, FALSE, FALSE },
974 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, TRUE, FALSE },
975 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, FALSE, FALSE },
976 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, FALSE, FALSE },
977 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, FALSE, FALSE },
978 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, FALSE, FALSE },
979 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, FALSE, FALSE },
980 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, FALSE, FALSE },
981 { DWRITE_BREAK_CONDITION_CAN_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, FALSE, FALSE },
982 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, FALSE, FALSE },
983 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, DWRITE_BREAK_CONDITION_CAN_BREAK, TRUE, FALSE }
986 { { 0 } }
989 static void compare_breakpoints(const struct linebreaks_test *test, DWRITE_LINE_BREAKPOINT *actual)
991 const WCHAR *text = test->text;
992 int cmp = memcmp(test->bp, actual, sizeof(*actual)*BREAKPOINT_COUNT);
993 ok(!cmp, "%s: got wrong breakpoint data\n", wine_dbgstr_w(test->text));
994 if (cmp) {
995 int i = 0;
996 while (*text) {
997 ok(!memcmp(&test->bp[i], &actual[i], sizeof(*actual)),
998 "%s: got (%d, %d, %d, %d), expected (%d, %d, %d, %d)\n",
999 wine_dbgstr_wn(&test->text[i], 1),
1000 g_actual_bp[i].breakConditionBefore,
1001 g_actual_bp[i].breakConditionAfter,
1002 g_actual_bp[i].isWhitespace,
1003 g_actual_bp[i].isSoftHyphen,
1004 test->bp[i].breakConditionBefore,
1005 test->bp[i].breakConditionAfter,
1006 test->bp[i].isWhitespace,
1007 test->bp[i].isSoftHyphen);
1008 text++;
1009 i++;
1014 static void test_AnalyzeLineBreakpoints(void)
1016 static const WCHAR emptyW[] = {0};
1017 const struct linebreaks_test *ptr = linebreaks_tests;
1018 IDWriteTextAnalyzer *analyzer;
1019 HRESULT hr;
1021 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
1022 ok(hr == S_OK, "got 0x%08x\n", hr);
1024 g_source = emptyW;
1025 hr = IDWriteTextAnalyzer_AnalyzeLineBreakpoints(analyzer, &analysissource, 0, 0, &analysissink);
1026 ok(hr == S_OK, "got 0x%08x\n", hr);
1028 while (*ptr->text)
1030 g_source = ptr->text;
1032 memset(g_actual_bp, 0, sizeof(g_actual_bp));
1033 hr = IDWriteTextAnalyzer_AnalyzeLineBreakpoints(analyzer, &analysissource, 0, lstrlenW(g_source), &analysissink);
1034 ok(hr == S_OK, "got 0x%08x\n", hr);
1035 compare_breakpoints(ptr, g_actual_bp);
1037 ptr++;
1040 IDWriteTextAnalyzer_Release(analyzer);
1043 static void test_GetScriptProperties(void)
1045 IDWriteTextAnalyzer1 *analyzer1;
1046 IDWriteTextAnalyzer *analyzer;
1047 DWRITE_SCRIPT_ANALYSIS sa;
1048 DWRITE_SCRIPT_PROPERTIES props;
1049 HRESULT hr;
1051 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
1052 ok(hr == S_OK, "got 0x%08x\n", hr);
1054 hr = IDWriteTextAnalyzer_QueryInterface(analyzer, &IID_IDWriteTextAnalyzer1, (void**)&analyzer1);
1055 IDWriteTextAnalyzer_Release(analyzer);
1056 if (hr != S_OK) {
1057 win_skip("GetScriptProperties() is not supported.\n");
1058 return;
1061 sa.script = 1000;
1062 hr = IDWriteTextAnalyzer1_GetScriptProperties(analyzer1, sa, &props);
1063 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1065 if (0) /* crashes on native */
1066 hr = IDWriteTextAnalyzer1_GetScriptProperties(analyzer1, sa, NULL);
1068 sa.script = 0;
1069 hr = IDWriteTextAnalyzer1_GetScriptProperties(analyzer1, sa, &props);
1070 ok(hr == S_OK, "got 0x%08x\n", hr);
1072 IDWriteTextAnalyzer1_Release(analyzer1);
1075 struct textcomplexity_test {
1076 const WCHAR text[5];
1077 UINT32 length;
1078 BOOL simple;
1079 UINT32 len_read;
1082 static const struct textcomplexity_test textcomplexity_tests[] = {
1083 { {0}, 1, FALSE, 1 },
1084 { {0}, 0, TRUE, 0 },
1085 { {0x610,0}, 0, TRUE, 0 },
1086 { {'A','B','C','D',0}, 3, TRUE, 3 },
1087 { {'A','B','C','D',0}, 5, TRUE, 4 },
1088 { {'A','B','C','D',0}, 10, TRUE, 4 },
1089 { {'A',0x610,'C','D',0}, 1, TRUE, 1 },
1090 { {'A',0x610,'C','D',0}, 2, FALSE, 2 },
1091 { {0x610,'A','C','D',0}, 1, FALSE, 1 },
1092 { {0x610,'A','C','D',0}, 2, FALSE, 1 },
1093 { {0x610,0x610,'C','D',0}, 2, FALSE, 2 },
1094 { {0xd800,'A','B',0}, 1, FALSE, 1 },
1095 { {0xd800,'A','B',0}, 2, FALSE, 1 },
1096 { {0xdc00,'A','B',0}, 2, FALSE, 1 }
1099 static void test_GetTextComplexity(void)
1101 static const WCHAR textW[] = {'A','B','C',0};
1102 IDWriteTextAnalyzer1 *analyzer1;
1103 IDWriteTextAnalyzer *analyzer;
1104 IDWriteFontFace *fontface;
1105 UINT16 indices[10];
1106 BOOL simple;
1107 HRESULT hr;
1108 UINT32 len;
1109 int i;
1111 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
1112 ok(hr == S_OK, "got 0x%08x\n", hr);
1114 hr = IDWriteTextAnalyzer_QueryInterface(analyzer, &IID_IDWriteTextAnalyzer1, (void**)&analyzer1);
1115 IDWriteTextAnalyzer_Release(analyzer);
1116 if (hr != S_OK) {
1117 win_skip("GetTextComplexity() is not supported.\n");
1118 return;
1121 if (0) { /* crashes on native */
1122 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer1, NULL, 0, NULL, NULL, NULL, NULL);
1123 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer1, NULL, 0, NULL, NULL, &len, NULL);
1124 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer1, textW, 3, NULL, NULL, NULL, NULL);
1125 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer1, textW, 3, NULL, NULL, &len, NULL);
1126 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer1, textW, 3, NULL, &simple, NULL, NULL);
1129 len = 1;
1130 simple = TRUE;
1131 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer1, NULL, 0, NULL, &simple, &len, NULL);
1132 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1133 ok(len == 0, "got %d\n", len);
1134 ok(simple == FALSE, "got %d\n", simple);
1136 len = 1;
1137 simple = TRUE;
1138 indices[0] = 1;
1139 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer1, textW, 3, NULL, &simple, &len, NULL);
1140 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1141 ok(len == 0, "got %d\n", len);
1142 ok(simple == FALSE, "got %d\n", simple);
1143 ok(indices[0] == 1, "got %d\n", indices[0]);
1145 fontface = create_fontface();
1147 for (i = 0; i < sizeof(textcomplexity_tests)/sizeof(struct textcomplexity_test); i++) {
1148 const struct textcomplexity_test *ptr = &textcomplexity_tests[i];
1149 len = 1;
1150 simple = !ptr->simple;
1151 indices[0] = 0;
1152 hr = IDWriteTextAnalyzer1_GetTextComplexity(analyzer1, ptr->text, ptr->length, fontface, &simple, &len, indices);
1153 ok(hr == S_OK, "%d: got 0x%08x\n", i, hr);
1154 ok(len == ptr->len_read, "%d: read length: got %d, expected %d\n", i, len, ptr->len_read);
1155 ok(simple == ptr->simple, "%d: simple: got %d, expected %d\n", i, simple, ptr->simple);
1156 if (simple && ptr->length)
1157 ok(indices[0] > 0, "%d: got %d\n", i, indices[0]);
1158 else
1159 ok(indices[0] == 0, "%d: got %d\n", i, indices[0]);
1162 IDWriteFontFace_Release(fontface);
1163 IDWriteTextAnalyzer1_Release(analyzer1);
1166 static void test_numbersubstitution(void)
1168 static const WCHAR dummyW[] = {'d','u','m','m','y',0};
1169 IDWriteNumberSubstitution *substitution;
1170 HRESULT hr;
1172 hr = IDWriteFactory_CreateNumberSubstitution(factory, DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE, NULL, FALSE, &substitution);
1173 ok(hr == S_OK, "got 0x%08x\n", hr);
1174 IDWriteNumberSubstitution_Release(substitution);
1176 /* invalid method */
1177 hr = IDWriteFactory_CreateNumberSubstitution(factory, DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL+1, NULL, FALSE, &substitution);
1178 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1180 /* invalid method */
1181 hr = IDWriteFactory_CreateNumberSubstitution(factory, -1, NULL, FALSE, &substitution);
1182 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1184 /* invalid locale */
1185 hr = IDWriteFactory_CreateNumberSubstitution(factory, DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL, dummyW, FALSE, &substitution);
1186 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1188 hr = IDWriteFactory_CreateNumberSubstitution(factory, DWRITE_NUMBER_SUBSTITUTION_METHOD_CONTEXTUAL, dummyW, FALSE, &substitution);
1189 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1191 hr = IDWriteFactory_CreateNumberSubstitution(factory, DWRITE_NUMBER_SUBSTITUTION_METHOD_NATIONAL, dummyW, FALSE, &substitution);
1192 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1194 /* invalid locale, but it's not needed for this method */
1195 hr = IDWriteFactory_CreateNumberSubstitution(factory, DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE, dummyW, FALSE, &substitution);
1196 ok(hr == S_OK, "got 0x%08x\n", hr);
1197 IDWriteNumberSubstitution_Release(substitution);
1200 static void test_GetGlyphs(void)
1202 static const WCHAR test1W[] = {'<','B',' ','C',0};
1203 static const WCHAR test2W[] = {'<','B','\t','C',0};
1204 static const WCHAR test3W[] = {0x202a,0x202c,0};
1205 DWRITE_SHAPING_GLYPH_PROPERTIES shapingprops[20];
1206 DWRITE_SHAPING_TEXT_PROPERTIES props[20];
1207 UINT32 maxglyphcount, actual_count;
1208 IDWriteTextAnalyzer *analyzer;
1209 IDWriteFontFace *fontface;
1210 DWRITE_SCRIPT_ANALYSIS sa;
1211 DWRITE_GLYPH_OFFSET offsets[10];
1212 UINT16 clustermap[10];
1213 UINT16 glyphs1[10];
1214 UINT16 glyphs2[10];
1215 FLOAT advances[10];
1216 HRESULT hr;
1218 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
1219 ok(hr == S_OK, "got 0x%08x\n", hr);
1221 fontface = create_fontface();
1223 maxglyphcount = 1;
1224 sa.script = 0;
1225 sa.shapes = DWRITE_SCRIPT_SHAPES_DEFAULT;
1226 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test1W, lstrlenW(test1W), fontface, FALSE, FALSE, &sa, NULL,
1227 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs1, shapingprops, &actual_count);
1228 ok(hr == E_NOT_SUFFICIENT_BUFFER, "got 0x%08x\n", hr);
1230 if (0) {
1231 /* NULL fontface - crashes on Windows */
1232 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test1W, lstrlenW(test1W), NULL, FALSE, FALSE, &sa, NULL,
1233 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs1, shapingprops, &actual_count);
1236 /* invalid script id */
1237 maxglyphcount = 10;
1238 actual_count = 0;
1239 sa.script = 999;
1240 sa.shapes = DWRITE_SCRIPT_SHAPES_DEFAULT;
1241 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test1W, lstrlenW(test1W), fontface, FALSE, FALSE, &sa, NULL,
1242 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs1, shapingprops, &actual_count);
1243 ok(hr == S_OK, "got 0x%08x\n", hr);
1244 ok(actual_count == 4, "got %d\n", actual_count);
1245 ok(sa.script == 999, "got %u\n", sa.script);
1247 /* no '\t' -> ' ' replacement */
1248 maxglyphcount = 10;
1249 actual_count = 0;
1250 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test1W, lstrlenW(test1W), fontface, FALSE, FALSE, &sa, NULL,
1251 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs1, shapingprops, &actual_count);
1252 ok(hr == S_OK, "got 0x%08x\n", hr);
1253 ok(actual_count == 4, "got %d\n", actual_count);
1255 actual_count = 0;
1256 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test2W, lstrlenW(test2W), fontface, FALSE, FALSE, &sa, NULL,
1257 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs2, shapingprops, &actual_count);
1258 ok(hr == S_OK, "got 0x%08x\n", hr);
1259 ok(actual_count == 4, "got %d\n", actual_count);
1260 ok(glyphs1[2] != glyphs2[2], "got %d\n", glyphs1[2]);
1262 /* check that mirroring works */
1263 maxglyphcount = 10;
1264 actual_count = 0;
1265 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test1W, lstrlenW(test1W), fontface, FALSE, FALSE, &sa, NULL,
1266 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs1, shapingprops, &actual_count);
1267 ok(hr == S_OK, "got 0x%08x\n", hr);
1268 ok(actual_count == 4, "got %d\n", actual_count);
1270 actual_count = 0;
1271 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test1W, lstrlenW(test1W), fontface, FALSE, TRUE, &sa, NULL,
1272 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs2, shapingprops, &actual_count);
1273 ok(hr == S_OK, "got 0x%08x\n", hr);
1274 ok(actual_count == 4, "got %d\n", actual_count);
1275 ok(glyphs1[0] != glyphs2[0], "got %d\n", glyphs1[0]);
1277 /* embedded control codes, with unknown script id 0 */
1278 actual_count = 0;
1279 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test3W, lstrlenW(test3W), fontface, FALSE, TRUE, &sa, NULL,
1280 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs1, shapingprops, &actual_count);
1281 ok(hr == S_OK, "got 0x%08x\n", hr);
1282 ok(actual_count == 2, "got %d\n", actual_count);
1283 ok(glyphs1[0] == 0, "got %d\n", glyphs1[0]);
1284 ok(glyphs1[1] == 0, "got %d\n", glyphs1[1]);
1285 ok(shapingprops[0].isClusterStart == 1, "got %d\n", shapingprops[0].isClusterStart);
1286 ok(shapingprops[0].isZeroWidthSpace == 0, "got %d\n", shapingprops[0].isZeroWidthSpace);
1287 ok(shapingprops[1].isClusterStart == 1, "got %d\n", shapingprops[1].isClusterStart);
1288 ok(shapingprops[1].isZeroWidthSpace == 0, "got %d\n", shapingprops[1].isZeroWidthSpace);
1289 ok(clustermap[0] == 0, "got %d\n", clustermap[0]);
1290 ok(clustermap[1] == 1, "got %d\n", clustermap[1]);
1292 memset(advances, 0, sizeof(advances));
1293 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, test3W, clustermap, props, lstrlenW(test3W),
1294 glyphs1, shapingprops, actual_count, fontface, 10.0, FALSE, FALSE, &sa, NULL, NULL,
1295 NULL, 0, advances, offsets);
1296 ok(hr == S_OK, "got 0x%08x\n", hr);
1297 ok(advances[0] == 10.0, "got %.2f\n", advances[0]);
1298 ok(advances[1] == 10.0, "got %.2f\n", advances[1]);
1300 /* embedded control codes with proper script */
1301 sa.script = 0;
1302 get_script_analysis(test3W, &sa);
1303 ok(sa.script != 0, "got %d\n", sa.script);
1304 actual_count = 0;
1305 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, test3W, lstrlenW(test3W), fontface, FALSE, FALSE, &sa, NULL,
1306 NULL, NULL, NULL, 0, maxglyphcount, clustermap, props, glyphs1, shapingprops, &actual_count);
1307 ok(hr == S_OK, "got 0x%08x\n", hr);
1308 ok(actual_count == 2, "got %d\n", actual_count);
1309 ok(glyphs1[0] == 0, "got %d\n", glyphs1[0]);
1310 ok(glyphs1[1] == 0, "got %d\n", glyphs1[1]);
1311 ok(shapingprops[0].isClusterStart == 1, "got %d\n", shapingprops[0].isClusterStart);
1312 ok(shapingprops[0].isZeroWidthSpace == 0, "got %d\n", shapingprops[0].isZeroWidthSpace);
1313 ok(shapingprops[1].isClusterStart == 1, "got %d\n", shapingprops[1].isClusterStart);
1314 ok(shapingprops[1].isZeroWidthSpace == 0, "got %d\n", shapingprops[1].isZeroWidthSpace);
1315 ok(clustermap[0] == 0, "got %d\n", clustermap[0]);
1316 ok(clustermap[1] == 1, "got %d\n", clustermap[1]);
1318 memset(advances, 0, sizeof(advances));
1319 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, test3W, clustermap, props, lstrlenW(test3W),
1320 glyphs1, shapingprops, actual_count, fontface, 10.0, FALSE, FALSE, &sa, NULL, NULL,
1321 NULL, 0, advances, offsets);
1322 ok(hr == S_OK, "got 0x%08x\n", hr);
1323 ok(advances[0] == 10.0, "got %.2f\n", advances[0]);
1324 ok(advances[1] == 10.0, "got %.2f\n", advances[1]);
1326 IDWriteTextAnalyzer_Release(analyzer);
1327 IDWriteFontFace_Release(fontface);
1330 static BOOL has_feature(const DWRITE_FONT_FEATURE_TAG *tags, UINT32 count, DWRITE_FONT_FEATURE_TAG feature)
1332 UINT32 i;
1334 for (i = 0; i < count; i++)
1335 if (tags[i] == feature) return TRUE;
1336 return FALSE;
1339 static void test_GetTypographicFeatures(void)
1341 static const WCHAR localeW[] = {'c','a','d','a','b','r','a',0};
1342 static const WCHAR arabicW[] = {0x064a,0x064f,0x0633,0};
1343 static const WCHAR abcW[] = {'a','b','c',0};
1344 DWRITE_FONT_FEATURE_TAG tags[20];
1345 IDWriteTextAnalyzer2 *analyzer2;
1346 IDWriteTextAnalyzer *analyzer;
1347 IDWriteFontFace *fontface;
1348 DWRITE_SCRIPT_ANALYSIS sa;
1349 UINT32 count;
1350 HRESULT hr;
1351 BOOL ret;
1353 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
1354 ok(hr == S_OK, "got 0x%08x\n", hr);
1356 hr = IDWriteTextAnalyzer_QueryInterface(analyzer, &IID_IDWriteTextAnalyzer2, (void**)&analyzer2);
1357 IDWriteTextAnalyzer_Release(analyzer);
1358 if (hr != S_OK) {
1359 win_skip("GetTypographicFeatures() is not supported.\n");
1360 return;
1363 fontface = create_fontface();
1365 get_script_analysis(abcW, &sa);
1366 count = 0;
1367 hr = IDWriteTextAnalyzer2_GetTypographicFeatures(analyzer2, fontface, sa, NULL, 0, &count, NULL);
1368 todo_wine {
1369 ok(hr == E_NOT_SUFFICIENT_BUFFER, "got 0x%08x\n", hr);
1370 ok(count > 0, "got %u\n", count);
1372 /* invalid locale name is ignored */
1373 get_script_analysis(abcW, &sa);
1374 count = 0;
1375 hr = IDWriteTextAnalyzer2_GetTypographicFeatures(analyzer2, fontface, sa, localeW, 0, &count, NULL);
1376 todo_wine {
1377 ok(hr == E_NOT_SUFFICIENT_BUFFER, "got 0x%08x\n", hr);
1378 ok(count > 0, "got %u\n", count);
1380 /* both GSUB and GPOS features are reported */
1381 get_script_analysis(arabicW, &sa);
1382 memset(tags, 0, sizeof(tags));
1383 count = 0;
1384 hr = IDWriteTextAnalyzer2_GetTypographicFeatures(analyzer2, fontface, sa, NULL, sizeof(tags)/sizeof(tags[0]), &count, tags);
1385 ok(hr == S_OK, "got 0x%08x\n", hr);
1386 todo_wine {
1387 ok(count > 0, "got %u\n", count);
1388 ret = has_feature(tags, count, DWRITE_FONT_FEATURE_TAG_CONTEXTUAL_ALTERNATES);
1389 ok(ret, "expected 'calt' feature\n");
1390 ret = has_feature(tags, count, DWRITE_FONT_FEATURE_TAG_MARK_TO_MARK_POSITIONING);
1391 ok(ret, "expected 'mkmk' feature\n");
1393 get_script_analysis(abcW, &sa);
1394 memset(tags, 0, sizeof(tags));
1395 count = 0;
1396 hr = IDWriteTextAnalyzer2_GetTypographicFeatures(analyzer2, fontface, sa, NULL, sizeof(tags)/sizeof(tags[0]), &count, tags);
1397 ok(hr == S_OK, "got 0x%08x\n", hr);
1398 todo_wine {
1399 ok(count > 0, "got %u\n", count);
1400 ret = has_feature(tags, count, DWRITE_FONT_FEATURE_TAG_GLYPH_COMPOSITION_DECOMPOSITION);
1401 ok(ret, "expected 'ccmp' feature\n");
1402 ret = has_feature(tags, count, DWRITE_FONT_FEATURE_TAG_MARK_TO_MARK_POSITIONING);
1403 ok(ret, "expected 'mkmk' feature\n");
1405 ret = has_feature(tags, count, DWRITE_FONT_FEATURE_TAG_CONTEXTUAL_ALTERNATES);
1406 ok(!ret, "unexpected 'calt' feature\n");
1408 IDWriteFontFace_Release(fontface);
1409 IDWriteTextAnalyzer2_Release(analyzer2);
1412 static void test_GetGlyphPlacements(void)
1414 DWRITE_SHAPING_GLYPH_PROPERTIES glyphprops[2];
1415 DWRITE_SHAPING_TEXT_PROPERTIES textprops[2];
1416 static const WCHAR aW[] = {'A','D',0};
1417 UINT16 clustermap[2], glyphs[2];
1418 DWRITE_GLYPH_OFFSET offsets[2];
1419 IDWriteTextAnalyzer *analyzer;
1420 IDWriteFontFace *fontface;
1421 DWRITE_SCRIPT_ANALYSIS sa;
1422 FLOAT advances[2];
1423 UINT32 count, len;
1424 WCHAR *path;
1425 HRESULT hr;
1427 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
1428 ok(hr == S_OK, "got 0x%08x\n", hr);
1430 path = create_testfontfile(test_fontfile);
1431 fontface = create_testfontface(path);
1433 get_script_analysis(aW, &sa);
1434 count = 0;
1435 len = lstrlenW(aW);
1436 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, aW, len, fontface, FALSE, FALSE, &sa, NULL,
1437 NULL, NULL, NULL, 0, len, clustermap, textprops, glyphs, glyphprops, &count);
1438 ok(hr == S_OK, "got 0x%08x\n", hr);
1439 ok(count == 2, "got %u\n", count);
1441 /* just return on zero glyphs */
1442 advances[0] = advances[1] = 1.0;
1443 offsets[0].advanceOffset = offsets[0].ascenderOffset = 2.0;
1444 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, clustermap, textprops,
1445 len, glyphs, glyphprops, 0, fontface, 0.0, FALSE, FALSE, &sa, NULL, NULL,
1446 NULL, 0, advances, offsets);
1447 ok(hr == S_OK, "got 0x%08x\n", hr);
1448 ok(advances[0] == 1.0, "got %.2f\n", advances[0]);
1449 ok(offsets[0].advanceOffset == 2.0 && offsets[0].ascenderOffset == 2.0, "got %.2f,%.2f\n",
1450 offsets[0].advanceOffset, offsets[0].ascenderOffset);
1452 /* advances/offsets are scaled with provided font emSize and designed eM box size */
1453 advances[0] = advances[1] = 1.0;
1454 memset(offsets, 0xcc, sizeof(offsets));
1455 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, clustermap, textprops,
1456 len, glyphs, glyphprops, len, fontface, 0.0, FALSE, FALSE, &sa, NULL, NULL,
1457 NULL, 0, advances, offsets);
1458 ok(hr == S_OK, "got 0x%08x\n", hr);
1459 ok(advances[0] == 0.0, "got %.2f\n", advances[0]);
1460 ok(offsets[0].advanceOffset == 0.0 && offsets[0].ascenderOffset == 0.0, "got %.2f,%.2f\n",
1461 offsets[0].advanceOffset, offsets[0].ascenderOffset);
1463 advances[0] = advances[1] = 1.0;
1464 memset(offsets, 0xcc, sizeof(offsets));
1465 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, clustermap, textprops,
1466 len, glyphs, glyphprops, len, fontface, 2048.0, FALSE, FALSE, &sa, NULL, NULL,
1467 NULL, 0, advances, offsets);
1468 ok(hr == S_OK, "got 0x%08x\n", hr);
1469 ok(advances[0] == 1000.0, "got %.2f\n", advances[0]);
1470 ok(offsets[0].advanceOffset == 0.0 && offsets[0].ascenderOffset == 0.0, "got %.2f,%.2f\n",
1471 offsets[0].advanceOffset, offsets[0].ascenderOffset);
1473 advances[0] = advances[1] = 1.0;
1474 memset(offsets, 0xcc, sizeof(offsets));
1475 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, clustermap, textprops,
1476 len, glyphs, glyphprops, len, fontface, 1024.0, FALSE, FALSE, &sa, NULL, NULL,
1477 NULL, 0, advances, offsets);
1478 ok(hr == S_OK, "got 0x%08x\n", hr);
1479 ok(advances[0] == 500.0, "got %.2f\n", advances[0]);
1480 ok(advances[1] == 500.0, "got %.2f\n", advances[1]);
1481 ok(offsets[0].advanceOffset == 0.0 && offsets[0].ascenderOffset == 0.0, "got %.2f,%.2f\n",
1482 offsets[0].advanceOffset, offsets[0].ascenderOffset);
1484 advances[0] = advances[1] = 1.0;
1485 memset(offsets, 0xcc, sizeof(offsets));
1486 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, clustermap, textprops,
1487 len, glyphs, glyphprops, len, fontface, 20.48, FALSE, FALSE, &sa, NULL, NULL,
1488 NULL, 0, advances, offsets);
1489 ok(hr == S_OK, "got 0x%08x\n", hr);
1490 ok(advances[0] == 10.0, "got %.2f\n", advances[0]);
1491 ok(advances[1] == 10.0, "got %.2f\n", advances[1]);
1492 ok(offsets[0].advanceOffset == 0.0 && offsets[0].ascenderOffset == 0.0, "got %.2f,%.2f\n",
1493 offsets[0].advanceOffset, offsets[0].ascenderOffset);
1495 /* without clustermap */
1496 advances[0] = advances[1] = 1.0;
1497 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, NULL, textprops,
1498 len, glyphs, glyphprops, len, fontface, 1024.0, FALSE, FALSE, &sa, NULL, NULL,
1499 NULL, 0, advances, offsets);
1500 ok(hr == S_OK, "got 0x%08x\n", hr);
1501 ok(advances[0] == 500.0, "got %.2f\n", advances[0]);
1502 ok(advances[1] == 500.0, "got %.2f\n", advances[1]);
1504 /* it's happy to use negative size too */
1505 advances[0] = advances[1] = 1.0;
1506 memset(offsets, 0xcc, sizeof(offsets));
1507 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, aW, clustermap, textprops,
1508 len, glyphs, glyphprops, len, fontface, -10.24, FALSE, FALSE, &sa, NULL, NULL,
1509 NULL, 0, advances, offsets);
1510 ok(hr == S_OK, "got 0x%08x\n", hr);
1511 ok(advances[0] == -5.0, "got %.2f\n", advances[0]);
1512 ok(offsets[0].advanceOffset == 0.0 && offsets[0].ascenderOffset == 0.0, "got %.2f,%.2f\n",
1513 offsets[0].advanceOffset, offsets[0].ascenderOffset);
1515 IDWriteTextAnalyzer_Release(analyzer);
1516 IDWriteFontFace_Release(fontface);
1517 DELETE_FONTFILE(path);
1520 struct spacing_test {
1521 FLOAT leading;
1522 FLOAT trailing;
1523 FLOAT min_advance;
1524 FLOAT advances[3];
1525 FLOAT offsets[3];
1526 FLOAT modified_advances[3];
1527 FLOAT modified_offsets[3];
1528 BOOL single_cluster;
1529 BOOL is_ZWS[3];
1532 static const struct spacing_test spacing_tests[] = {
1533 { 0.0, 0.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 } }, /* 0 */
1534 { 0.0, 0.0, 2.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 } },
1535 { 1.0, 0.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 11.0, 12.0 }, { 3.0, 4.0 } },
1536 { 1.0, 1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 12.0, 13.0 }, { 3.0, 4.0 } },
1537 { 1.0, -1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 3.0, 4.0 } },
1538 { 0.0, -10.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 0.0, 1.0 }, { 2.0, 3.0 } }, /* 5 */
1539 { -5.0, -4.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 5.0, 5.0 }, { -1.0, -0.5 } },
1540 { -5.0, -5.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 5.0, 5.0 }, { -0.5, 0.0 } },
1541 { 2.0, 0.0, 5.0, { 1.0, 2.0 }, { 2.0, 3.0 }, { 7.0, 7.0 }, { 6.0, 6.5 } },
1542 { 2.0, 1.0, 5.0, { 1.0, 2.0 }, { 2.0, 3.0 }, { 8.0, 8.0 }, { 6.0, 6.5 } },
1543 { 2.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 7.0, 7.0 }, { 4.0, 5.0 } }, /* 10 */
1544 { 1.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 6.0, 6.0 }, { 3.0, 4.0 } },
1545 { -10.0, 1.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 6.0, 6.0 }, { -3.0, -3.0 } },
1546 { 0.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 5.0, 5.0 }, { 2.0, 3.0 } },
1547 { 1.0, -10.0, -5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 0.0, 0.0 }, { 2.0, 3.0 } },
1548 { -10.0, 1.0, 5.0, { 8.0, 11.0 }, { 2.0, 3.0 }, { 6.0, 6.0 }, { -1.0, -3.0 } }, /* 15 */
1549 /* cluster of more than 1 glyph */
1550 { 0.0, 0.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 }, TRUE },
1551 { 1.0, 0.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.5 }, { 11.0, 11.0 }, { 3.0, 3.5 }, TRUE },
1552 { 1.0, 1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 11.0, 12.0 }, { 3.0, 3.0 }, TRUE },
1553 { 1.0, -1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 11.0, 10.0 }, { 3.0, 3.0 }, TRUE },
1554 { 0.0, -10.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 1.0 }, { 2.0, 3.0 }, TRUE }, /* 20 */
1555 { 0.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 1.0 }, { 2.0, 3.0 }, TRUE },
1556 { 1.0, -10.0, -5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 0.0, 0.0 }, { 2.0, 3.0 }, TRUE },
1557 { -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 },
1558 { -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 },
1559 { -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 */
1560 { -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 },
1561 { 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 },
1562 { -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 },
1563 /* isZeroWidthSpace set */
1564 { 1.0, 0.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 12.0 }, { 2.0, 4.0 }, FALSE, { TRUE, FALSE } },
1565 { 1.0, 1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 13.0 }, { 2.0, 4.0 }, FALSE, { TRUE, FALSE } }, /* 30 */
1566 { 1.0, -1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 3.0, 3.0 }, FALSE, { FALSE, TRUE } },
1567 { 0.0, -10.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 1.0 }, { 2.0, 3.0 }, FALSE, { TRUE, FALSE } },
1568 { -5.0, -4.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 5.0, 11.0 }, { -1.0, 3.0 }, FALSE, { FALSE, TRUE } },
1569 { -5.0, -5.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 }, FALSE, { TRUE, TRUE } },
1570 { 2.0, 0.0, 5.0, { 1.0, 2.0 }, { 2.0, 3.0 }, { 7.0, 2.0 }, { 6.0, 3.0 }, FALSE, { FALSE, TRUE } }, /* 35 */
1571 { 2.0, 1.0, 5.0, { 1.0, 2.0 }, { 2.0, 3.0 }, { 8.0, 2.0 }, { 6.0, 3.0 }, FALSE, { FALSE, TRUE } },
1572 { 2.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 }, FALSE, { TRUE, TRUE } },
1573 { 1.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 6.0, 11.0 }, { 3.0, 3.0 }, FALSE, { FALSE, TRUE } },
1574 { -10.0, 1.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 }, FALSE, { TRUE, TRUE } },
1575 { 0.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 5.0, 11.0 }, { 2.0, 3.0 }, FALSE, { FALSE, TRUE } }, /* 40 */
1576 { 1.0, -10.0, -5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 0.0, 0.0 }, { 2.0, 3.0 }, FALSE, { TRUE, FALSE } },
1577 { -10.0, 1.0, 5.0, { 8.0, 11.0 }, { 2.0, 3.0 }, { 6.0, 11.0 }, { -1.0, 3.0 }, FALSE, { FALSE, TRUE } },
1580 static void test_ApplyCharacterSpacing(void)
1582 DWRITE_SHAPING_GLYPH_PROPERTIES props[3];
1583 IDWriteTextAnalyzer1 *analyzer1;
1584 IDWriteTextAnalyzer *analyzer;
1585 UINT16 clustermap[2];
1586 HRESULT hr;
1587 int i;
1589 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
1590 ok(hr == S_OK, "got 0x%08x\n", hr);
1592 hr = IDWriteTextAnalyzer_QueryInterface(analyzer, &IID_IDWriteTextAnalyzer1, (void**)&analyzer1);
1593 IDWriteTextAnalyzer_Release(analyzer);
1594 if (hr != S_OK) {
1595 win_skip("ApplyCharacterSpacing() is not supported.\n");
1596 return;
1599 for (i = 0; i < sizeof(spacing_tests)/sizeof(spacing_tests[0]); i++) {
1600 const struct spacing_test *ptr = spacing_tests + i;
1601 DWRITE_GLYPH_OFFSET offsets[3];
1602 UINT32 glyph_count;
1603 FLOAT advances[3];
1605 offsets[0].advanceOffset = ptr->offsets[0];
1606 offsets[1].advanceOffset = ptr->offsets[1];
1607 offsets[2].advanceOffset = ptr->offsets[2];
1608 /* Ascender offsets are never thouched as spacing applies in reading direction only,
1609 we'll only test them to see if they are not changed */
1610 offsets[0].ascenderOffset = 23.0;
1611 offsets[1].ascenderOffset = 32.0;
1612 offsets[2].ascenderOffset = 31.0;
1614 glyph_count = ptr->advances[2] > 0.0 ? 3 : 2;
1615 if (ptr->single_cluster) {
1616 clustermap[0] = 0;
1617 clustermap[1] = 0;
1619 else {
1620 /* trivial case with one glyph per cluster */
1621 clustermap[0] = 0;
1622 clustermap[1] = 1;
1625 advances[0] = advances[1] = 123.45;
1626 memset(props, 0, sizeof(props));
1627 props[0].isZeroWidthSpace = ptr->is_ZWS[0];
1628 props[1].isZeroWidthSpace = ptr->is_ZWS[1];
1629 props[2].isZeroWidthSpace = ptr->is_ZWS[2];
1631 hr = IDWriteTextAnalyzer1_ApplyCharacterSpacing(analyzer1,
1632 ptr->leading,
1633 ptr->trailing,
1634 ptr->min_advance,
1635 sizeof(clustermap)/sizeof(clustermap[0]),
1636 glyph_count,
1637 clustermap,
1638 ptr->advances,
1639 offsets,
1640 props,
1641 advances,
1642 offsets);
1643 /* invalid argument cases */
1644 if (ptr->min_advance < 0.0)
1645 ok(hr == E_INVALIDARG, "%d: got 0x%08x\n", i, hr);
1646 else
1647 ok(hr == S_OK, "%d: got 0x%08x\n", i, hr);
1648 if (hr == S_OK) {
1649 ok(ptr->modified_advances[0] == advances[0], "%d: got advance[0] %.2f, expected %.2f\n", i, advances[0], ptr->modified_advances[0]);
1650 ok(ptr->modified_advances[1] == advances[1], "%d: got advance[1] %.2f, expected %.2f\n", i, advances[1], ptr->modified_advances[1]);
1651 if (glyph_count > 2)
1652 ok(ptr->modified_advances[2] == advances[2], "%d: got advance[2] %.2f, expected %.2f\n", i, advances[2], ptr->modified_advances[2]);
1654 ok(ptr->modified_offsets[0] == offsets[0].advanceOffset, "%d: got offset[0] %.2f, expected %.2f\n", i,
1655 offsets[0].advanceOffset, ptr->modified_offsets[0]);
1656 ok(ptr->modified_offsets[1] == offsets[1].advanceOffset, "%d: got offset[1] %.2f, expected %.2f\n", i,
1657 offsets[1].advanceOffset, ptr->modified_offsets[1]);
1658 if (glyph_count > 2)
1659 ok(ptr->modified_offsets[2] == offsets[2].advanceOffset, "%d: got offset[2] %.2f, expected %.2f\n", i,
1660 offsets[2].advanceOffset, ptr->modified_offsets[2]);
1662 ok(offsets[0].ascenderOffset == 23.0, "%d: unexpected ascenderOffset %.2f\n", i, offsets[0].ascenderOffset);
1663 ok(offsets[1].ascenderOffset == 32.0, "%d: unexpected ascenderOffset %.2f\n", i, offsets[1].ascenderOffset);
1664 ok(offsets[2].ascenderOffset == 31.0, "%d: unexpected ascenderOffset %.2f\n", i, offsets[2].ascenderOffset);
1666 else {
1667 ok(ptr->modified_advances[0] == advances[0], "%d: got advance[0] %.2f, expected %.2f\n", i, advances[0], ptr->modified_advances[0]);
1668 ok(ptr->modified_advances[1] == advances[1], "%d: got advance[1] %.2f, expected %.2f\n", i, advances[1], ptr->modified_advances[1]);
1669 ok(ptr->offsets[0] == offsets[0].advanceOffset, "%d: got offset[0] %.2f, expected %.2f\n", i,
1670 offsets[0].advanceOffset, ptr->modified_offsets[0]);
1671 ok(ptr->offsets[1] == offsets[1].advanceOffset, "%d: got offset[1] %.2f, expected %.2f\n", i,
1672 offsets[1].advanceOffset, ptr->modified_offsets[1]);
1673 ok(offsets[0].ascenderOffset == 23.0, "%d: unexpected ascenderOffset %.2f\n", i, offsets[0].ascenderOffset);
1674 ok(offsets[1].ascenderOffset == 32.0, "%d: unexpected ascenderOffset %.2f\n", i, offsets[1].ascenderOffset);
1678 IDWriteTextAnalyzer1_Release(analyzer1);
1681 struct orientation_transf_test {
1682 DWRITE_GLYPH_ORIENTATION_ANGLE angle;
1683 BOOL is_sideways;
1684 DWRITE_MATRIX m;
1687 static const struct orientation_transf_test ot_tests[] = {
1688 { DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES, FALSE, { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 } },
1689 { DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES, FALSE, { 0.0, 1.0, -1.0, 0.0, 0.0, 0.0 } },
1690 { DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES, FALSE, { -1.0, 0.0, 0.0, -1.0, 0.0, 0.0 } },
1691 { DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES, FALSE, { 0.0, -1.0, 1.0, 0.0, 0.0, 0.0 } },
1692 { DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES, TRUE, { 0.0, 1.0, -1.0, 0.0, 0.0, 0.0 } },
1693 { DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES, TRUE, { -1.0, 0.0, 0.0, -1.0, 0.0, 0.0 } },
1694 { DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES, TRUE, { 0.0, -1.0, 1.0, 0.0, 0.0, 0.0 } },
1695 { DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES, TRUE, { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 } }
1698 static inline const char *dbgstr_matrix(const DWRITE_MATRIX *m)
1700 static char buff[64];
1701 sprintf(buff, "{%.2f, %.2f, %.2f, %.2f, %.2f, %.2f}", m->m11, m->m12,
1702 m->m21, m->m22, m->dx, m->dy);
1703 return buff;
1706 static void test_GetGlyphOrientationTransform(void)
1708 IDWriteTextAnalyzer2 *analyzer2;
1709 IDWriteTextAnalyzer1 *analyzer1;
1710 IDWriteTextAnalyzer *analyzer;
1711 FLOAT originx, originy;
1712 DWRITE_MATRIX m;
1713 HRESULT hr;
1714 int i;
1716 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
1717 ok(hr == S_OK, "got 0x%08x\n", hr);
1719 hr = IDWriteTextAnalyzer_QueryInterface(analyzer, &IID_IDWriteTextAnalyzer1, (void**)&analyzer1);
1720 IDWriteTextAnalyzer_Release(analyzer);
1721 if (hr != S_OK) {
1722 win_skip("GetGlyphOrientationTransform() is not supported.\n");
1723 return;
1726 /* invalid angle value */
1727 memset(&m, 0xcc, sizeof(m));
1728 hr = IDWriteTextAnalyzer1_GetGlyphOrientationTransform(analyzer1,
1729 DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES + 1, FALSE, &m);
1730 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1731 ok(m.m11 == 0.0, "got %.2f\n", m.m11);
1733 for (i = 0; i < sizeof(ot_tests)/sizeof(ot_tests[0]); i++) {
1734 memset(&m, 0, sizeof(m));
1735 hr = IDWriteTextAnalyzer1_GetGlyphOrientationTransform(analyzer1, ot_tests[i].angle,
1736 ot_tests[i].is_sideways, &m);
1737 ok(hr == S_OK, "got 0x%08x\n", hr);
1738 ok(!memcmp(&ot_tests[i].m, &m, sizeof(m)), "%d: wrong matrix %s\n", i, dbgstr_matrix(&m));
1741 hr = IDWriteTextAnalyzer1_QueryInterface(analyzer1, &IID_IDWriteTextAnalyzer2, (void**)&analyzer2);
1742 IDWriteTextAnalyzer1_Release(analyzer1);
1743 if (hr != S_OK) {
1744 win_skip("IDWriteTextAnalyzer2::GetGlyphOrientationTransform() is not supported.\n");
1745 return;
1748 /* invalid angle value */
1749 memset(&m, 0xcc, sizeof(m));
1750 hr = IDWriteTextAnalyzer2_GetGlyphOrientationTransform(analyzer2,
1751 DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES + 1, FALSE, 0.0, 0.0, &m);
1752 ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1753 ok(m.m11 == 0.0, "got %.2f\n", m.m11);
1755 originx = 50.0;
1756 originy = 60.0;
1757 for (i = 0; i < sizeof(ot_tests)/sizeof(ot_tests[0]); i++) {
1758 DWRITE_GLYPH_ORIENTATION_ANGLE angle = DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES;
1759 DWRITE_MATRIX m_exp;
1761 memset(&m, 0, sizeof(m));
1763 /* zero offset gives same result as a call from IDWriteTextAnalyzer1 */
1764 hr = IDWriteTextAnalyzer2_GetGlyphOrientationTransform(analyzer2, ot_tests[i].angle,
1765 ot_tests[i].is_sideways, 0.0, 0.0, &m);
1766 ok(hr == S_OK, "got 0x%08x\n", hr);
1767 ok(!memcmp(&ot_tests[i].m, &m, sizeof(m)), "%d: wrong matrix %s\n", i, dbgstr_matrix(&m));
1769 m_exp = ot_tests[i].m;
1770 hr = IDWriteTextAnalyzer2_GetGlyphOrientationTransform(analyzer2, ot_tests[i].angle,
1771 ot_tests[i].is_sideways, originx, originy, &m);
1772 ok(hr == S_OK, "got 0x%08x\n", hr);
1774 /* 90 degrees more for sideways */
1775 if (ot_tests[i].is_sideways) {
1776 switch (ot_tests[i].angle)
1778 case DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES:
1779 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES;
1780 break;
1781 case DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES:
1782 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES;
1783 break;
1784 case DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES:
1785 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES;
1786 break;
1787 case DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES:
1788 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES;
1789 break;
1790 default:
1794 else
1795 angle = ot_tests[i].angle;
1797 /* set expected offsets */
1798 switch (angle)
1800 case DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES:
1801 break;
1802 case DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES:
1803 m_exp.dx = originx + originy;
1804 m_exp.dy = originy - originx;
1805 break;
1806 case DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES:
1807 m_exp.dx = originx + originx;
1808 m_exp.dy = originy + originy;
1809 break;
1810 case DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES:
1811 m_exp.dx = originx - originy;
1812 m_exp.dy = originy + originx;
1813 break;
1814 default:
1818 ok(!memcmp(&m_exp, &m, sizeof(m)), "%d: wrong matrix %s\n", i, dbgstr_matrix(&m));
1821 IDWriteTextAnalyzer2_Release(analyzer2);
1824 static void test_GetBaseline(void)
1826 DWRITE_SCRIPT_ANALYSIS sa = { 0 };
1827 IDWriteTextAnalyzer1 *analyzer1;
1828 IDWriteTextAnalyzer *analyzer;
1829 IDWriteFontFace *fontface;
1830 INT32 baseline;
1831 BOOL exists;
1832 HRESULT hr;
1834 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
1835 ok(hr == S_OK, "got 0x%08x\n", hr);
1837 hr = IDWriteTextAnalyzer_QueryInterface(analyzer, &IID_IDWriteTextAnalyzer1, (void**)&analyzer1);
1838 IDWriteTextAnalyzer_Release(analyzer);
1839 if (hr != S_OK) {
1840 win_skip("GetBaseline() is not supported.\n");
1841 return;
1844 fontface = create_fontface();
1846 /* Tahoma doesn't have BASE table, it doesn't work even with simulation enabled */
1847 exists = TRUE;
1848 baseline = 456;
1849 hr = IDWriteTextAnalyzer1_GetBaseline(analyzer1,
1850 fontface,
1851 DWRITE_BASELINE_DEFAULT,
1852 FALSE,
1853 TRUE,
1855 NULL,
1856 &baseline,
1857 &exists);
1858 todo_wine {
1859 ok(hr == S_OK, "got 0x%08x\n", hr);
1860 ok(baseline == 0, "got %d\n", baseline);
1861 ok(exists == FALSE, "got %d\n", exists);
1863 exists = TRUE;
1864 baseline = 456;
1865 hr = IDWriteTextAnalyzer1_GetBaseline(analyzer1,
1866 fontface,
1867 DWRITE_BASELINE_ROMAN,
1868 FALSE,
1869 TRUE,
1871 NULL,
1872 &baseline,
1873 &exists);
1874 todo_wine {
1875 ok(hr == S_OK, "got 0x%08x\n", hr);
1876 ok(baseline == 0, "got %d\n", baseline);
1877 ok(exists == FALSE, "got %d\n", exists);
1879 IDWriteFontFace_Release(fontface);
1880 IDWriteTextAnalyzer1_Release(analyzer1);
1883 static inline BOOL float_eq(FLOAT left, FLOAT right)
1885 int x = *(int *)&left;
1886 int y = *(int *)&right;
1888 if (x < 0)
1889 x = INT_MIN - x;
1890 if (y < 0)
1891 y = INT_MIN - y;
1893 return abs(x - y) <= 8;
1896 static void test_GetGdiCompatibleGlyphPlacements(void)
1898 static const WCHAR strW[] = {'A',0};
1899 DWRITE_SHAPING_GLYPH_PROPERTIES glyphprops[1];
1900 DWRITE_SHAPING_TEXT_PROPERTIES textprops[1];
1901 DWRITE_SCRIPT_ANALYSIS sa = { 0 };
1902 IDWriteTextAnalyzer *analyzer;
1903 IDWriteFontFace *fontface;
1904 UINT16 clustermap[1];
1905 HRESULT hr;
1906 UINT32 count;
1907 UINT16 glyphs[1];
1908 FLOAT advance;
1909 DWRITE_GLYPH_OFFSET offsets[1];
1910 DWRITE_FONT_METRICS fontmetrics;
1911 FLOAT emsize;
1913 hr = IDWriteFactory_CreateTextAnalyzer(factory, &analyzer);
1914 ok(hr == S_OK, "got 0x%08x\n", hr);
1916 fontface = create_fontface();
1918 IDWriteFontFace_GetMetrics(fontface, &fontmetrics);
1920 count = 0;
1921 hr = IDWriteTextAnalyzer_GetGlyphs(analyzer, strW, 1, fontface,
1922 FALSE, FALSE, &sa, NULL, NULL, NULL, NULL, 0, 1, clustermap,
1923 textprops, glyphs, glyphprops, &count);
1924 ok(hr == S_OK, "got 0x%08x\n", hr);
1925 ok(count == 1, "got %u\n", count);
1927 for (emsize = 12.0; emsize <= 20.0; emsize += 1.0) {
1928 FLOAT compatadvance, expected, ppdip;
1929 DWRITE_GLYPH_METRICS metrics;
1931 hr = IDWriteTextAnalyzer_GetGlyphPlacements(analyzer, strW, clustermap,
1932 textprops, 1, glyphs, glyphprops, count, fontface, emsize, FALSE, FALSE,
1933 &sa, NULL, NULL, NULL, 0, &advance, offsets);
1934 ok(hr == S_OK, "got 0x%08x\n", hr);
1935 ok(advance > 0.0, "got %f\n", advance);
1937 /* 1 ppdip, no transform */
1938 ppdip = 1.0;
1939 hr = IDWriteFontFace_GetGdiCompatibleGlyphMetrics(fontface, emsize, ppdip, NULL, FALSE,
1940 glyphs, 1, &metrics, FALSE);
1941 ok(hr == S_OK, "got 0x%08x\n", hr);
1943 expected = floorf(metrics.advanceWidth * emsize * ppdip / fontmetrics.designUnitsPerEm + 0.5f) / ppdip;
1944 hr = IDWriteTextAnalyzer_GetGdiCompatibleGlyphPlacements(analyzer, strW,
1945 clustermap, textprops, 1, glyphs, glyphprops, count, fontface, emsize,
1946 ppdip, NULL, FALSE, FALSE, FALSE, &sa, NULL, NULL, NULL, 0, &compatadvance, offsets);
1947 ok(hr == S_OK, "got 0x%08x\n", hr);
1948 ok(compatadvance == expected, "%.0f: got advance %f, expected %f, natural %f\n", emsize,
1949 compatadvance, expected, advance);
1951 /* 1.2 ppdip, no transform */
1952 ppdip = 1.2;
1953 hr = IDWriteFontFace_GetGdiCompatibleGlyphMetrics(fontface, emsize, ppdip, NULL, FALSE,
1954 glyphs, 1, &metrics, FALSE);
1955 ok(hr == S_OK, "got 0x%08x\n", hr);
1957 expected = floorf(metrics.advanceWidth * emsize * ppdip / fontmetrics.designUnitsPerEm + 0.5f) / ppdip;
1958 hr = IDWriteTextAnalyzer_GetGdiCompatibleGlyphPlacements(analyzer, strW,
1959 clustermap, textprops, 1, glyphs, glyphprops, count, fontface, emsize,
1960 ppdip, NULL, FALSE, FALSE, FALSE, &sa, NULL, NULL, NULL, 0, &compatadvance, offsets);
1961 ok(hr == S_OK, "got 0x%08x\n", hr);
1962 ok(float_eq(compatadvance, expected), "%.0f: got advance %f, expected %f, natural %f\n", emsize,
1963 compatadvance, expected, advance);
1966 IDWriteFontFace_Release(fontface);
1967 IDWriteTextAnalyzer_Release(analyzer);
1970 START_TEST(analyzer)
1972 HRESULT hr;
1974 hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_ISOLATED, &IID_IDWriteFactory, (IUnknown**)&factory);
1975 ok(hr == S_OK, "got 0x%08x\n", hr);
1976 if (hr != S_OK)
1978 win_skip("failed to create factory\n");
1979 return;
1982 init_call_sequences(sequences, NUM_CALL_SEQUENCES);
1983 init_call_sequences(expected_seq, 1);
1985 test_AnalyzeScript();
1986 test_AnalyzeLineBreakpoints();
1987 test_GetScriptProperties();
1988 test_GetTextComplexity();
1989 test_GetGlyphs();
1990 test_numbersubstitution();
1991 test_GetTypographicFeatures();
1992 test_GetGlyphPlacements();
1993 test_ApplyCharacterSpacing();
1994 test_GetGlyphOrientationTransform();
1995 test_GetBaseline();
1996 test_GetGdiCompatibleGlyphPlacements();
1998 IDWriteFactory_Release(factory);