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
34 #include "wine/test.h"
36 static IDWriteFactory
*factory
;
38 static void * create_text_analyzer(REFIID riid
)
40 IDWriteTextAnalyzer
*analyzer
;
43 if (SUCCEEDED(IDWriteFactory_CreateTextAnalyzer(factory
, &analyzer
)))
45 IDWriteTextAnalyzer_QueryInterface(analyzer
, riid
, &ret
);
46 IDWriteTextAnalyzer_Release(analyzer
);
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)
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)
76 struct ot_gdef_classdef_format1
84 struct ot_gdef_class_range
91 struct ot_gdef_classdef_format2
95 struct ot_gdef_class_range ranges
[1];
103 static const char *get_analysis_kind_name(enum analysis_kind kind
)
108 return "ScriptAnalysis";
114 struct script_analysis
{
117 DWRITE_SCRIPT_SHAPES shapes
;
121 enum analysis_kind kind
;
122 struct script_analysis sa
;
126 enum analysis_kind kind
;
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
)
152 call_seq
->sequence
= malloc(call_seq
->size
* sizeof(*call_seq
->sequence
));
155 if (call_seq
->count
== call_seq
->size
)
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
)
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
);
191 ok_(ctxt
->file
, ctxt
->line
) (expected
== actual
, "%s: \"%s\" expecting %u, got %u\n", get_analysis_kind_name(ctxt
->kind
), name
,
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
;
203 struct testcontext ctxt
;
205 add_call(seq
, sequence_index
, &end_of_sequence
);
207 sequence
= call_seq
->sequence
;
210 ctxt
.failcount
= &failcount
;
215 while (expected
->kind
!= LastKind
&& actual
->kind
!= LastKind
)
217 if (expected
->kind
== actual
->kind
)
219 ctxt
.kind
= expected
->kind
;
221 switch (actual
->kind
)
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
);
229 ok(0, "%s: callback not handled, %s\n", context
, get_analysis_kind_name(actual
->kind
));
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
);
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
));
259 if (expected
->kind
!= LastKind
|| actual
->kind
!= LastKind
)
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 */
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
))
296 return E_NOINTERFACE
;
299 static ULONG WINAPI
analysissink_AddRef(IDWriteTextAnalysisSink
*iface
)
304 static ULONG WINAPI
analysissink_Release(IDWriteTextAnalysisSink
*iface
)
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
);
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
)
329 #define BREAKPOINT_COUNT 20
330 static DWRITE_LINE_BREAKPOINT g_actual_bp
[BREAKPOINT_COUNT
];
332 static HRESULT WINAPI
analysissink_SetLineBreakpoints(IDWriteTextAnalysisSink
*iface
,
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
);
341 memcpy(&g_actual_bp
[position
], breakpoints
, length
*sizeof(DWRITE_LINE_BREAKPOINT
));
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
,
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
);
358 memset(g_explicit_levels
+ position
, explicitLevel
, length
);
359 memset(g_resolved_levels
+ position
, resolvedLevel
, length
);
363 static HRESULT WINAPI
analysissink_SetNumberSubstitution(IDWriteTextAnalysisSink
*iface
,
366 IDWriteNumberSubstitution
* substitution
)
368 ok(0, "unexpected\n");
372 static IDWriteTextAnalysisSinkVtbl analysissinkvtbl
= {
373 analysissink_QueryInterface
,
375 analysissink_Release
,
376 analysissink_SetScriptAnalysis
,
377 analysissink_SetLineBreakpoints
,
378 analysissink_SetBidiLevel
,
379 analysissink_SetNumberSubstitution
382 static IDWriteTextAnalysisSinkVtbl analysissinkvtbl2
= {
383 analysissink_QueryInterface
,
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");
402 static ULONG WINAPI
analysissource_AddRef(IDWriteTextAnalysisSource
*iface
)
404 ok(0, "AddRef not expected\n");
408 static ULONG WINAPI
analysissource_Release(IDWriteTextAnalysisSource
*iface
)
410 ok(0, "Release not expected\n");
414 struct testanalysissource
416 IDWriteTextAnalysisSource IDWriteTextAnalysisSource_iface
;
419 DWRITE_READING_DIRECTION direction
;
422 static void init_textsource(struct testanalysissource
*source
, const WCHAR
*text
,
423 INT text_length
, DWRITE_READING_DIRECTION direction
)
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
)
447 *text
= source
->text
+ position
;
448 *text_len
= source
->text_length
- position
;
454 static HRESULT WINAPI
analysissource_GetTextBeforePosition(IDWriteTextAnalysisSource
*iface
,
455 UINT32 position
, WCHAR
const** text
, UINT32
* text_len
)
457 ok(0, "unexpected\n");
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
)
476 static HRESULT WINAPI
analysissource_GetNumberSubstitution(IDWriteTextAnalysisSource
*iface
,
477 UINT32 position
, UINT32
* text_len
, IDWriteNumberSubstitution
**substitution
)
479 ok(0, "unexpected\n");
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
;
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
);
526 static WCHAR
*create_testfontfile(const WCHAR
*filename
)
528 static WCHAR pathW
[MAX_PATH
];
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
),
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");
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
;
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
);
576 const WCHAR string
[50];
579 struct script_analysis sa
[10];
582 static struct sa_test sa_tests
[] = {
584 /* just 1 char string */
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
}}
603 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
607 {0x064a,0x064f,0x0633,0x0627,0x0648,0x0650,0x064a,0x0661,0}, -1, 1,
608 { { 0, 8, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
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
}}
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
}}
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
}}
691 {0x531,0x540,0x559,0x55f,0x570,0x589,0x58a,0}, -1, 1,
692 { { 0, 7, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
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
}}
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
}}
718 {0x780,0x78e,0x798,0x7a6,0x7b0,'.',0}, -1, 1,
719 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
723 {0x7c0,0x7ca,0x7e8,0x7eb,0x7f6,'.',0}, -1, 1,
724 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
728 {0x780,0x798,0x7a5,0x7a6,0x7b0,'.',0}, -1, 1,
729 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
733 {0x926,0x947,0x935,0x928,0x93e,0x917,0x930,0x940,'.',0}, -1, 1,
734 { { 0, 9, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
738 {0x9ac,0x9be,0x982,0x9b2,0x9be,'.',0}, -1, 1,
739 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
743 {0xa17,0xa41,0xa30,0xa2e,0xa41,0xa16,0xa40,'.',0}, -1, 1,
744 { { 0, 8, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
748 {0xa97,0xac1,0xa9c,0xab0,0xabe,0xaa4,0xac0,'.',0}, -1, 1,
749 { { 0, 8, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
753 {0xb13,0xb21,0xb3c,0xb3f,0xb06,'.',0}, -1, 1,
754 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
758 {0xba4,0xbae,0xbbf,0xbb4,0xbcd,'.',0}, -1, 1,
759 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
763 {0xc24,0xc46,0xc32,0xc41,0xc17,0xc41,'.',0}, -1, 1,
764 { { 0, 7, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
768 {0xc95,0xca8,0xccd,0xca8,0xca1,'.',0}, -1, 1,
769 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
773 {0xd2e,0xd32,0xd2f,0xd3e,0xd33,0xd02,'.',0}, -1, 1,
774 { { 0, 7, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
778 {0xd82,0xd85,0xd9a,0xdcf,'.',0}, -1, 1,
779 { { 0, 5, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
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
}}
791 {0xead,0xeb1,0xe81,0xeaa,0xead,0xe99,0xea5,0xeb2,0xea7,'.',0}, -1, 1,
792 { { 0, 10, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
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
}}
802 {0x1019,0x103c,0x1014,0x103a,0x1019,0x102c,0x1021,0x1000,0x1039,0x1001,0x101b,0x102c,'.',0}, -1, 1,
803 { { 0, 13, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
807 {0x10a0,0x10d0,0x10da,0x10f1,0x10fb,0x2d00,'.',0}, -1, 1,
808 { { 0, 7, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
812 {0x1100,0x1110,0x1160,0x1170,0x11a8,'.',0}, -1, 1,
813 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
817 {0x130d,0x12d5,0x12dd,0}, -1, 1,
818 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
822 {0x13e3,0x13b3,0x13a9,0x0020,0x13a6,0x13ec,0x13c2,0x13af,0x13cd,0x13d7,0}, -1, 1,
823 { { 0, 10, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
827 {0x1403,0x14c4,0x1483,0x144e,0x1450,0x1466,0}, -1, 1,
828 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
832 {0x169b,0x1691,0x168c,0x1690,0x168b,0x169c,0}, -1, 1,
833 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
837 {0x16a0,0x16a1,0x16a2,0x16a3,0x16a4,0x16a5,0}, -1, 1,
838 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
842 {0x1781,0x17c1,0x1798,0x179a,0x1797,0x17b6,0x179f,0x17b6,0x19e0,0}, -1, 1,
843 { { 0, 9, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
847 {0x182e,0x1823,0x1829,0x182d,0x1823,0x182f,0x0020,0x182a,0x1822,0x1834,0x1822,0x182d,0x180c,0}, -1, 1,
848 { { 0, 13, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
852 {0x1900,0x1910,0x1920,0x1930,0}, -1, 1,
853 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
857 {0x1956,0x196d,0x1970,0x1956,0x196c,0x1973,0x1951,0x1968,0x1952,0x1970,0}, -1, 1,
858 { { 0, 10, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
862 {0x1992,0x19c4,0}, -1, 1,
863 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
867 {0x1a00,0x1a10,0}, -1, 1,
868 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
872 {0x1a20,0x1a40,0x1a50,0}, -1, 1,
873 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
877 {0x1b00,0x1b05,0x1b20,0}, -1, 1,
878 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
882 {0x1b80,0x1b85,0x1ba0,0}, -1, 1,
883 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
887 {0x1bc0,0x1be5,0x1bfc,0}, -1, 1,
888 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
892 {0x1c00,0x1c20,0x1c40,0}, -1, 1,
893 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
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
}}
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
}}
943 {0x2800,0x2070,0x2000,0}, -1, 1,
944 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
948 {0x2c00,0x2c12,0}, -1, 1,
949 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
953 {0x2c80,0x3e2,0x1f00,0}, -1, 2,
954 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT
},
955 { 2, 1, DWRITE_SCRIPT_SHAPES_DEFAULT
} }
959 {0x2d30,0x2d4a,0}, -1, 1,
960 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
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
} }
1038 {0xdb40,0xdc20,0}, -1, 1,
1039 { { 0, 2, DWRITE_SCRIPT_SHAPES_NO_VISUAL
} }
1042 /* Null at start of string */
1044 { { 0, 1, DWRITE_SCRIPT_SHAPES_NO_VISUAL
},
1045 { 1, 4, DWRITE_SCRIPT_SHAPES_DEFAULT
} }
1048 /* Null embedded in string */
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 */
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
};
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
;
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
);
1101 IDWriteTextAnalyzer_Release(analyzer
);
1104 static void test_AnalyzeScript(void)
1106 IDWriteTextAnalyzer
*analyzer
;
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
},
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
));
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));
1220 static void test_AnalyzeLineBreakpoints(void)
1222 const struct linebreaks_test
*ptr
= linebreaks_tests
;
1223 IDWriteTextAnalyzer
*analyzer
;
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,
1233 ok(hr
== S_OK
, "Unexpected hr %#lx.\n", hr
);
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
);
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
);
1259 IDWriteTextAnalyzer_Release(analyzer
);
1262 static void test_GetScriptProperties(void)
1264 IDWriteTextAnalyzer1
*analyzer
;
1265 DWRITE_SCRIPT_PROPERTIES props
;
1266 DWRITE_SCRIPT_ANALYSIS sa
;
1269 analyzer
= create_text_analyzer(&IID_IDWriteTextAnalyzer1
);
1272 win_skip("GetScriptProperties() is not supported.\n");
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
);
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];
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
;
1330 analyzer
= create_text_analyzer(&IID_IDWriteTextAnalyzer1
);
1333 win_skip("GetTextComplexity() is not supported.\n");
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
);
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
);
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
];
1367 simple
= !ptr
->simple
;
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]);
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
;
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
,
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
)
1433 UINT32 codepoint
= *str
;
1436 hr
= IDWriteFontFace_GetGlyphIndices(fontface
, &codepoint
, 1, glyphs
++);
1437 ok(hr
== S_OK
, "Unexpected hr %#lx.\n", hr
);
1442 static void get_fontface_advances(IDWriteFontFace
*fontface
, FLOAT emsize
, const UINT16
*glyphs
, FLOAT
*advances
, UINT32 count
)
1444 DWRITE_FONT_METRICS fontmetrics
;
1447 IDWriteFontFace_GetMetrics(fontface
, &fontmetrics
);
1448 for (i
= 0; i
< count
; i
++) {
1449 DWRITE_GLYPH_METRICS metrics
;
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
;
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
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
)
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
))
1497 else if (glyph
> GET_BE_WORD(range
->end_glyph
))
1503 static unsigned int get_glyph_class(const struct dwrite_fonttable
*table
, UINT16 glyph
)
1505 unsigned int glyph_class
= GDEF_CLASS_UNCLASSIFIED
, offset
;
1508 offset
= table_read_be_word(table
, 4);
1510 format
= table_read_be_word(table
, offset
);
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
]));
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
]));
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
;
1549 static void get_enus_string(IDWriteLocalizedStrings
*strings
, WCHAR
*buff
, unsigned int size
)
1551 BOOL exists
= FALSE
;
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! */
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
;
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
);
1583 offset
= table_read_be_word(&gdef
, 4);
1586 IDWriteFontFace_ReleaseFontTable(fontface
, gdef
.context
);
1590 if (FAILED(IDWriteFontFace_QueryInterface(fontface
, &IID_IDWriteFontFace1
, (void **)&fontface1
)))
1592 IDWriteFontFace_ReleaseFontTable(fontface
, gdef
.context
);
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)
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
;
1618 hr
= IDWriteFontFace1_GetGlyphIndices(fontface1
, &ch
, 1, &glyph
);
1619 ok(hr
== S_OK
, "Failed to get glyph index, hr %#lx.\n", hr
);
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)
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
));
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
));
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];
1682 analyzer
= create_text_analyzer(&IID_IDWriteTextAnalyzer
);
1683 ok(!!analyzer
, "Failed to create analyzer instance.\n");
1685 fontface
= create_fontface();
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
);
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 */
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 */
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
);
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 */
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
);
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);
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 */
1769 get_script_analysis(test3W
, &sa
);
1770 ok(sa
.script
!= 0, "got %d\n", sa
.script
);
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 */
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
;
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
)
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
;
1865 analyzer
= create_text_analyzer(&IID_IDWriteTextAnalyzer2
);
1868 win_skip("GetTypographicFeatures() is not supported.\n");
1872 fontface
= create_fontface();
1874 get_script_analysis(L
"abc", &sa
);
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
);
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
));
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
));
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
;
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
);
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
{
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. */
2058 /* isZeroWidthSpace */
2059 #define P_Z { 0, 0, 0, 1, 0 }
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
} },
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
} },
2198 static void test_ApplyCharacterSpacing(void)
2200 DWRITE_SHAPING_GLYPH_PROPERTIES props
[3];
2201 IDWriteTextAnalyzer1
*analyzer
;
2202 UINT16 clustermap
[2];
2206 analyzer
= create_text_analyzer(&IID_IDWriteTextAnalyzer1
);
2209 win_skip("ApplyCharacterSpacing() is not supported.\n");
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];
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
)
2236 props
[0].isClusterStart
= 1;
2240 /* trivial case with one glyph per cluster */
2243 props
[0].isClusterStart
= props
[1].isClusterStart
= 1;
2246 winetest_push_context("Test %u", i
);
2248 hr
= IDWriteTextAnalyzer1_ApplyCharacterSpacing(analyzer
,
2252 ARRAY_SIZE(clustermap
),
2260 ok(hr
== (ptr
->min_advance
< 0.0f
? E_INVALIDARG
: S_OK
), "Unexpected hr %#lx.\n", hr
);
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
);
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
,
2306 ARRAY_SIZE(clustermap
),
2314 ok(hr
== (ptr
->min_advance
< 0.0f
? E_INVALIDARG
: S_OK
), "Unexpected hr %#lx.\n", hr
);
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
);
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
;
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
);
2379 static void test_GetGlyphOrientationTransform(void)
2381 IDWriteTextAnalyzer2
*analyzer2
;
2382 IDWriteTextAnalyzer1
*analyzer
;
2383 FLOAT originx
, originy
;
2388 analyzer
= create_text_analyzer(&IID_IDWriteTextAnalyzer1
);
2391 win_skip("GetGlyphOrientationTransform() is not supported.\n");
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
);
2416 win_skip("IDWriteTextAnalyzer2::GetGlyphOrientationTransform() is not supported.\n");
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
);
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
;
2453 case DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES
:
2454 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES
;
2456 case DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES
:
2457 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES
;
2459 case DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES
:
2460 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES
;
2467 angle
= ot_tests
[i
].angle
;
2469 /* set expected offsets */
2472 case DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES
:
2474 case DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES
:
2475 m_exp
.dx
= originx
+ originy
;
2476 m_exp
.dy
= originy
- originx
;
2478 case DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES
:
2479 m_exp
.dx
= originx
+ originx
;
2480 m_exp
.dy
= originy
+ originy
;
2482 case DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES
:
2483 m_exp
.dx
= originx
- originy
;
2484 m_exp
.dy
= originy
+ originx
;
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
;
2505 analyzer
= create_text_analyzer(&IID_IDWriteTextAnalyzer1
);
2508 win_skip("GetBaseline() is not supported.\n");
2512 fontface
= create_fontface();
2514 /* Tahoma does not have a BASE table. */
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
);
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
);
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
);
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
);
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
;
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];
2585 DWRITE_GLYPH_OFFSET offsets
[1];
2586 DWRITE_FONT_METRICS fontmetrics
;
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
);
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 */
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 */
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
);
2647 const WCHAR text
[BIDI_LEVELS_COUNT
];
2648 DWRITE_READING_DIRECTION direction
;
2649 UINT8
explicit[BIDI_LEVELS_COUNT
];
2650 UINT8 resolved
[BIDI_LEVELS_COUNT
];
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
,
2686 { 'A', 0x200c, 'B', 0 },
2687 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT
,
2692 { LRE
, PDF
, 'a', 'b', 0 },
2693 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT
,
2698 { 'a', LRE
, PDF
, 'b', 0 },
2699 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT
,
2704 { RLE
, PDF
, 'a', 'b', 0 },
2705 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT
,
2710 { 'a', RLE
, PDF
, 'b', 0 },
2711 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT
,
2716 { 'a', RLE
, PDF
, 'b', 0 },
2717 DWRITE_READING_DIRECTION_RIGHT_TO_LEFT
,
2722 { LRE
, PDF
, 'a', 'b', 0 },
2723 DWRITE_READING_DIRECTION_RIGHT_TO_LEFT
,
2728 { PDF
, 'a', 'b', 0 },
2729 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT
,
2734 { LRE
, 'a', 'b', PDF
, 0 },
2735 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT
,
2741 { LRI
, 'a', 'b', PDI
, 0 },
2742 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT
,
2748 { RLI
, 'a', 'b', PDI
, 0 },
2749 DWRITE_READING_DIRECTION_LEFT_TO_RIGHT
,
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;
2764 match
= !memcmp(explicit, test
->explicit, len
);
2769 ok(0, "test %u: %s wrong explicit levels:\n", seq
, wine_dbgstr_w(test
->text
));
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
]) {
2779 ok(0, "\tat %u, explicit level %u, expected %u\n", i
, explicit[i
], test
->explicit[i
]);
2782 ok(0, "\tat %u, explicit level %u, expected %u\n", i
, explicit[i
], test
->explicit[i
]);
2787 match
= !memcmp(resolved
, test
->resolved
, len
);
2792 ok(0, "test %u: %s wrong resolved levels:\n", seq
, wine_dbgstr_w(test
->text
));
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
]) {
2802 ok(0, "\tat %u, resolved level %u, expected %u\n", i
, resolved
[i
], test
->resolved
[i
]);
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
;
2821 analyzer
= create_text_analyzer(&IID_IDWriteTextAnalyzer
);
2822 ok(!!analyzer
, "Failed to create analyzer instance.\n");
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
);
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
);
2849 IDWriteTextAnalyzer_Release(analyzer
);
2859 static void test_glyph_justification_property(void)
2863 enum script_id script
;
2865 unsigned short justification
[10];
2872 SCRIPT_JUSTIFY_CHARACTER
,
2873 SCRIPT_JUSTIFY_BLANK
,
2874 SCRIPT_JUSTIFY_CHARACTER
,
2875 SCRIPT_JUSTIFY_BLANK
,
2876 SCRIPT_JUSTIFY_CHARACTER
,
2883 SCRIPT_JUSTIFY_BLANK
,
2884 SCRIPT_JUSTIFY_CHARACTER
,
2885 SCRIPT_JUSTIFY_BLANK
,
2886 SCRIPT_JUSTIFY_CHARACTER
,
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
;
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"))
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
)
2965 hr
= DWriteCreateFactory(DWRITE_FACTORY_TYPE_ISOLATED
, &IID_IDWriteFactory
, (IUnknown
**)&factory
);
2966 ok(hr
== S_OK
, "Unexpected hr %#lx.\n", hr
);
2969 win_skip("failed to create factory\n");
2973 init_call_sequences(sequences
, NUM_CALL_SEQUENCES
);
2974 init_call_sequences(expected_seq
, 1);
2976 test_AnalyzeScript();
2977 test_AnalyzeLineBreakpoints();
2979 test_GetScriptProperties();
2980 test_GetTextComplexity();
2982 test_numbersubstitution();
2983 test_GetTypographicFeatures();
2984 test_GetGlyphPlacements();
2985 test_ApplyCharacterSpacing();
2986 test_GetGlyphOrientationTransform();
2988 test_GetGdiCompatibleGlyphPlacements();
2989 test_glyph_justification_property();
2991 IDWriteFactory_Release(factory
);