4 * Copyright 2012-2014 Nikolay Sivov for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
31 #include "wine/test.h"
33 static IDWriteFactory
*factory
;
34 static const WCHAR test_fontfile
[] = {'w','i','n','e','_','t','e','s','t','_','f','o','n','t','.','t','t','f',0};
41 static const char *get_analysis_kind_name(enum analysis_kind kind
)
46 return "ScriptAnalysis";
52 struct script_analysis
{
55 DWRITE_SCRIPT_SHAPES shapes
;
59 enum analysis_kind kind
;
60 struct script_analysis sa
;
64 enum analysis_kind kind
;
75 struct call_entry
*sequence
;
78 #define NUM_CALL_SEQUENCES 1
80 static struct call_sequence
*sequences
[NUM_CALL_SEQUENCES
];
81 static struct call_sequence
*expected_seq
[1];
83 static void add_call(struct call_sequence
**seq
, int sequence_index
, const struct call_entry
*call
)
85 struct call_sequence
*call_seq
= seq
[sequence_index
];
87 if (!call_seq
->sequence
)
90 call_seq
->sequence
= HeapAlloc(GetProcessHeap(), 0,
91 call_seq
->size
* sizeof (struct call_entry
));
94 if (call_seq
->count
== call_seq
->size
)
97 call_seq
->sequence
= HeapReAlloc(GetProcessHeap(), 0,
99 call_seq
->size
* sizeof (struct call_entry
));
102 assert(call_seq
->sequence
);
104 call_seq
->sequence
[call_seq
->count
++] = *call
;
107 static inline void flush_sequence(struct call_sequence
**seg
, int sequence_index
)
109 struct call_sequence
*call_seq
= seg
[sequence_index
];
111 HeapFree(GetProcessHeap(), 0, call_seq
->sequence
);
112 call_seq
->sequence
= NULL
;
113 call_seq
->count
= call_seq
->size
= 0;
116 static inline void flush_sequences(struct call_sequence
**seq
, int n
)
119 for (i
= 0; i
< n
; i
++)
120 flush_sequence(seq
, i
);
123 static void init_call_sequences(struct call_sequence
**seq
, int n
)
127 for (i
= 0; i
< n
; i
++)
128 seq
[i
] = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(struct call_sequence
));
131 static void test_uint(UINT32 actual
, UINT32 expected
, const char *name
, const struct testcontext
*ctxt
)
133 if (expected
!= actual
&& ctxt
->todo
)
135 (*ctxt
->failcount
)++;
136 ok_(ctxt
->file
, ctxt
->line
) (0, "%s: \"%s\" expecting %u, got %u\n", get_analysis_kind_name(ctxt
->kind
), name
, expected
, actual
);
139 ok_(ctxt
->file
, ctxt
->line
) (expected
== actual
, "%s: \"%s\" expecting %u, got %u\n", get_analysis_kind_name(ctxt
->kind
), name
,
143 static void ok_sequence_(struct call_sequence
**seq
, int sequence_index
,
144 const struct call_entry
*expected
, const char *context
, BOOL todo
,
145 const char *file
, int line
)
147 struct call_sequence
*call_seq
= seq
[sequence_index
];
148 static const struct call_entry end_of_sequence
= { LastKind
};
149 const struct call_entry
*actual
, *sequence
;
151 struct testcontext ctxt
;
153 add_call(seq
, sequence_index
, &end_of_sequence
);
155 sequence
= call_seq
->sequence
;
158 ctxt
.failcount
= &failcount
;
163 while (expected
->kind
!= LastKind
&& actual
->kind
!= LastKind
)
165 if (expected
->kind
== actual
->kind
)
167 ctxt
.kind
= expected
->kind
;
169 switch (actual
->kind
)
172 test_uint(actual
->sa
.pos
, expected
->sa
.pos
, "position", &ctxt
);
173 test_uint(actual
->sa
.len
, expected
->sa
.len
, "length", &ctxt
);
174 test_uint(actual
->sa
.shapes
, expected
->sa
.shapes
, "shapes", &ctxt
);
177 ok(0, "%s: callback not handled, %s\n", context
, get_analysis_kind_name(actual
->kind
));
187 ok_(file
, line
) (0, "%s: call %s was expected, but got call %s instead\n",
188 context
, get_analysis_kind_name(expected
->kind
), get_analysis_kind_name(actual
->kind
));
191 flush_sequence(seq
, sequence_index
);
196 ok_(file
, line
) (0, "%s: call %s was expected, but got call %s instead\n",
197 context
, get_analysis_kind_name(expected
->kind
), get_analysis_kind_name(actual
->kind
));
207 if (expected
->kind
!= LastKind
|| actual
->kind
!= LastKind
)
210 ok_(file
, line
) (0, "%s: the call sequence is not complete: expected %s - actual %s\n",
211 context
, get_analysis_kind_name(expected
->kind
), get_analysis_kind_name(actual
->kind
));
215 else if (expected
->kind
!= LastKind
|| actual
->kind
!= LastKind
)
217 ok_(file
, line
) (0, "%s: the call sequence is not complete: expected %s - actual %s\n",
218 context
, get_analysis_kind_name(expected
->kind
), get_analysis_kind_name(actual
->kind
));
221 if (todo
&& !failcount
) /* succeeded yet marked todo */
225 ok_(file
, line
)(1, "%s: marked \"todo_wine\" but succeeds\n", context
);
229 flush_sequence(seq
, sequence_index
);
232 #define ok_sequence(seq, index, exp, contx, todo) \
233 ok_sequence_(seq, index, (exp), (contx), (todo), __FILE__, __LINE__)
235 static HRESULT WINAPI
analysissink_QueryInterface(IDWriteTextAnalysisSink
*iface
, REFIID riid
, void **obj
)
237 if (IsEqualIID(riid
, &IID_IDWriteTextAnalysisSink
) || IsEqualIID(riid
, &IID_IUnknown
))
244 return E_NOINTERFACE
;
247 static ULONG WINAPI
analysissink_AddRef(IDWriteTextAnalysisSink
*iface
)
252 static ULONG WINAPI
analysissink_Release(IDWriteTextAnalysisSink
*iface
)
257 static HRESULT WINAPI
analysissink_SetScriptAnalysis(IDWriteTextAnalysisSink
*iface
,
258 UINT32 position
, UINT32 length
, DWRITE_SCRIPT_ANALYSIS
const* sa
)
260 struct call_entry entry
;
261 entry
.kind
= ScriptAnalysis
;
262 entry
.sa
.pos
= position
;
263 entry
.sa
.len
= length
;
264 entry
.sa
.shapes
= sa
->shapes
;
265 add_call(sequences
, ANALYZER_ID
, &entry
);
269 static DWRITE_SCRIPT_ANALYSIS g_sa
;
270 static HRESULT WINAPI
analysissink_SetScriptAnalysis2(IDWriteTextAnalysisSink
*iface
,
271 UINT32 position
, UINT32 length
, DWRITE_SCRIPT_ANALYSIS
const* sa
)
277 #define BREAKPOINT_COUNT 20
278 static DWRITE_LINE_BREAKPOINT g_actual_bp
[BREAKPOINT_COUNT
];
280 static HRESULT WINAPI
analysissink_SetLineBreakpoints(IDWriteTextAnalysisSink
*iface
,
283 DWRITE_LINE_BREAKPOINT
const* breakpoints
)
285 if (position
+ length
> BREAKPOINT_COUNT
) {
286 ok(0, "SetLineBreakpoints: reported pos=%u, len=%u overflows expected length %d\n", position
, length
, BREAKPOINT_COUNT
);
289 memcpy(&g_actual_bp
[position
], breakpoints
, length
*sizeof(DWRITE_LINE_BREAKPOINT
));
293 static HRESULT WINAPI
analysissink_SetBidiLevel(IDWriteTextAnalysisSink
*iface
,
299 ok(0, "unexpected\n");
303 static HRESULT WINAPI
analysissink_SetNumberSubstitution(IDWriteTextAnalysisSink
*iface
,
306 IDWriteNumberSubstitution
* substitution
)
308 ok(0, "unexpected\n");
312 static IDWriteTextAnalysisSinkVtbl analysissinkvtbl
= {
313 analysissink_QueryInterface
,
315 analysissink_Release
,
316 analysissink_SetScriptAnalysis
,
317 analysissink_SetLineBreakpoints
,
318 analysissink_SetBidiLevel
,
319 analysissink_SetNumberSubstitution
322 static IDWriteTextAnalysisSinkVtbl analysissinkvtbl2
= {
323 analysissink_QueryInterface
,
325 analysissink_Release
,
326 analysissink_SetScriptAnalysis2
,
327 analysissink_SetLineBreakpoints
,
328 analysissink_SetBidiLevel
,
329 analysissink_SetNumberSubstitution
332 static IDWriteTextAnalysisSink analysissink
= { &analysissinkvtbl
};
333 static IDWriteTextAnalysisSink analysissink2
= { &analysissinkvtbl2
};
335 static HRESULT WINAPI
analysissource_QueryInterface(IDWriteTextAnalysisSource
*iface
,
336 REFIID riid
, void **obj
)
338 ok(0, "QueryInterface not expected\n");
342 static ULONG WINAPI
analysissource_AddRef(IDWriteTextAnalysisSource
*iface
)
344 ok(0, "AddRef not expected\n");
348 static ULONG WINAPI
analysissource_Release(IDWriteTextAnalysisSource
*iface
)
350 ok(0, "Release not expected\n");
354 static const WCHAR
*g_source
;
356 static HRESULT WINAPI
analysissource_GetTextAtPosition(IDWriteTextAnalysisSource
*iface
,
357 UINT32 position
, WCHAR
const** text
, UINT32
* text_len
)
359 if (position
>= lstrlenW(g_source
))
366 *text
= &g_source
[position
];
367 *text_len
= lstrlenW(g_source
) - position
;
373 static HRESULT WINAPI
analysissource_GetTextBeforePosition(IDWriteTextAnalysisSource
*iface
,
374 UINT32 position
, WCHAR
const** text
, UINT32
* text_len
)
376 ok(0, "unexpected\n");
380 static DWRITE_READING_DIRECTION WINAPI
analysissource_GetParagraphReadingDirection(
381 IDWriteTextAnalysisSource
*iface
)
383 ok(0, "unexpected\n");
384 return DWRITE_READING_DIRECTION_RIGHT_TO_LEFT
;
387 static HRESULT WINAPI
analysissource_GetLocaleName(IDWriteTextAnalysisSource
*iface
,
388 UINT32 position
, UINT32
* text_len
, WCHAR
const** locale
)
395 static HRESULT WINAPI
analysissource_GetNumberSubstitution(IDWriteTextAnalysisSource
*iface
,
396 UINT32 position
, UINT32
* text_len
, IDWriteNumberSubstitution
**substitution
)
398 ok(0, "unexpected\n");
402 static IDWriteTextAnalysisSourceVtbl analysissourcevtbl
= {
403 analysissource_QueryInterface
,
404 analysissource_AddRef
,
405 analysissource_Release
,
406 analysissource_GetTextAtPosition
,
407 analysissource_GetTextBeforePosition
,
408 analysissource_GetParagraphReadingDirection
,
409 analysissource_GetLocaleName
,
410 analysissource_GetNumberSubstitution
413 static IDWriteTextAnalysisSource analysissource
= { &analysissourcevtbl
};
415 static IDWriteFontFace
*create_fontface(void)
417 static const WCHAR tahomaW
[] = {'T','a','h','o','m','a',0};
418 IDWriteGdiInterop
*interop
;
419 IDWriteFontFace
*fontface
;
424 hr
= IDWriteFactory_GetGdiInterop(factory
, &interop
);
425 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
427 memset(&logfont
, 0, sizeof(logfont
));
428 logfont
.lfHeight
= 12;
429 logfont
.lfWidth
= 12;
430 logfont
.lfWeight
= FW_NORMAL
;
431 logfont
.lfItalic
= 1;
432 lstrcpyW(logfont
.lfFaceName
, tahomaW
);
434 hr
= IDWriteGdiInterop_CreateFontFromLOGFONT(interop
, &logfont
, &font
);
435 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
437 hr
= IDWriteFont_CreateFontFace(font
, &fontface
);
438 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
440 IDWriteFont_Release(font
);
441 IDWriteGdiInterop_Release(interop
);
446 static WCHAR
*create_testfontfile(const WCHAR
*filename
)
448 static WCHAR pathW
[MAX_PATH
];
454 GetTempPathW(sizeof(pathW
)/sizeof(WCHAR
), pathW
);
455 lstrcatW(pathW
, filename
);
457 file
= CreateFileW(pathW
, GENERIC_READ
|GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, 0, 0);
458 ok(file
!= INVALID_HANDLE_VALUE
, "file creation failed, at %s, error %d\n", wine_dbgstr_w(pathW
),
461 res
= FindResourceA(GetModuleHandleA(NULL
), (LPCSTR
)MAKEINTRESOURCE(1), (LPCSTR
)RT_RCDATA
);
462 ok(res
!= 0, "couldn't find resource\n");
463 ptr
= LockResource(LoadResource(GetModuleHandleA(NULL
), res
));
464 WriteFile(file
, ptr
, SizeofResource(GetModuleHandleA(NULL
), res
), &written
, NULL
);
465 ok(written
== SizeofResource(GetModuleHandleA(NULL
), res
), "couldn't write resource\n");
471 #define DELETE_FONTFILE(filename) _delete_testfontfile(filename, __LINE__)
472 static void _delete_testfontfile(const WCHAR
*filename
, int line
)
474 BOOL ret
= DeleteFileW(filename
);
475 ok_(__FILE__
,line
)(ret
, "failed to delete file %s, error %d\n", wine_dbgstr_w(filename
), GetLastError());
478 static IDWriteFontFace
*create_testfontface(const WCHAR
*filename
)
480 IDWriteFontFace
*face
;
481 IDWriteFontFile
*file
;
484 hr
= IDWriteFactory_CreateFontFileReference(factory
, filename
, NULL
, &file
);
485 ok(hr
== S_OK
, "got 0x%08x\n",hr
);
487 hr
= IDWriteFactory_CreateFontFace(factory
, DWRITE_FONT_FACE_TYPE_TRUETYPE
, 1, &file
, 0,
488 DWRITE_FONT_SIMULATIONS_NONE
, &face
);
489 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
490 IDWriteFontFile_Release(file
);
496 const WCHAR string
[50];
498 struct script_analysis sa
[10];
501 static struct sa_test sa_tests
[] = {
503 /* just 1 char string */
505 { { 0, 1, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
508 {'t','e','s','t',0}, 1,
509 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
512 {' ',' ',' ',' ','!','$','[','^','{','~',0}, 1,
513 { { 0, 10, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
516 {' ',' ',' ','1','2',' ',0}, 1,
517 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
522 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
526 {0x064a,0x064f,0x0633,0x0627,0x0648,0x0650,0x064a,0}, 1,
527 { { 0, 7, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
531 {0x0627,0x0644,0x0635,0x0651,0x0650,0x062d,0x0629,0x064f,' ',0x062a,0x064e,
532 0x0627,0x062c,0x064c,' ',0x0639,0x064e,0x0644,0x0649,' ',
533 0x0631,0x064f,0x0624,0x0648,0x0633,0x0650,' ',0x0627,0x0644,
534 0x0623,0x0635,0x0650,0x062d,0x0651,0x064e,0x0627,0x0621,0x0650,0x06f0,0x06f5,0}, 1,
535 { { 0, 40, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
539 {'1','2','3','-','5','2',0x064a,0x064f,0x0633,0x0627,0x0648,0x0650,0x064a,'7','1','.',0}, 1,
540 { { 0, 16, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
543 /* Arabic, English */
544 {'A','B','C','-','D','E','F',' ',0x0621,0x0623,0x0624,0}, 2,
545 { { 0, 8, DWRITE_SCRIPT_SHAPES_DEFAULT
},
546 { 8, 3, DWRITE_SCRIPT_SHAPES_DEFAULT
},
550 /* leading space, Arabic, English */
551 {' ',0x0621,0x0623,0x0624,'A','B','C','-','D','E','F',0}, 2,
552 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT
},
553 { 4, 7, DWRITE_SCRIPT_SHAPES_DEFAULT
},
557 /* English, Arabic, trailing space */
558 {'A','B','C','-','D','E','F',0x0621,0x0623,0x0624,' ',0}, 2,
559 { { 0, 7, DWRITE_SCRIPT_SHAPES_DEFAULT
},
560 { 7, 4, DWRITE_SCRIPT_SHAPES_DEFAULT
},
564 /* C1 Controls, Latin-1 Supplement */
565 {0x80,0x90,0x9f,0xa0,0xc0,0xb8,0xbf,0xc0,0xff,0}, 2,
566 { { 0, 3, DWRITE_SCRIPT_SHAPES_NO_VISUAL
},
567 { 3, 6, DWRITE_SCRIPT_SHAPES_DEFAULT
},
571 /* Latin Extended-A */
572 {0x100,0x120,0x130,0x140,0x150,0x160,0x170,0x17f,0}, 1,
573 { { 0, 8, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
576 /* Latin Extended-B */
577 {0x180,0x190,0x1bf,0x1c0,0x1c3,0x1c4,0x1cc,0x1dc,0x1ff,0x217,0x21b,0x24f,0}, 1,
578 { { 0, 12, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
582 {0x250,0x260,0x270,0x290,0x2af,0}, 1,
583 { { 0, 5, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
586 /* Spacing Modifier Letters */
587 {0x2b0,0x2ba,0x2d7,0x2dd,0x2ef,0x2ff,0}, 1,
588 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
591 /* Combining Diacritical Marks */
592 {0x300,0x320,0x340,0x345,0x350,0x36f,0}, 1,
593 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
596 /* Greek and Coptic */
597 {0x370,0x388,0x3d8,0x3e1,0x3e2,0x3fa,0x3ff,0}, 3,
598 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT
},
599 { 4, 1, DWRITE_SCRIPT_SHAPES_DEFAULT
},
600 { 5, 2, DWRITE_SCRIPT_SHAPES_DEFAULT
}
604 /* Cyrillic and Cyrillic Supplement */
605 {0x400,0x40f,0x410,0x44f,0x450,0x45f,0x460,0x481,0x48a,0x4f0,0x4fa,0x4ff,0x500,0x510,0x520,0}, 1,
606 { { 0, 15, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
610 {0x531,0x540,0x559,0x55f,0x570,0x589,0x58a,0}, 1,
611 { { 0, 7, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
615 {0x5e9,0x5dc,0x5d5,0x5dd,0}, 1,
616 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
619 /* Latin, Hebrew, Latin */
620 {'p','a','r','t',' ','o','n','e',' ',0x5d7,0x5dc,0x5e7,' ',0x5e9,0x5ea,0x5d9,0x5d9,0x5dd,' ','p','a','r','t',' ','t','h','r','e','e',0}, 3,
621 { { 0, 9, DWRITE_SCRIPT_SHAPES_DEFAULT
},
622 { 9, 10, DWRITE_SCRIPT_SHAPES_DEFAULT
},
623 { 19, 10, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
627 {0x710,0x712,0x712,0x714,'.',0}, 1,
628 { { 0, 5, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
631 /* Arabic Supplement */
632 {0x750,0x760,0x76d,'.',0}, 1,
633 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
637 {0x780,0x78e,0x798,0x7a6,0x7b0,'.',0}, 1,
638 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
642 {0x7c0,0x7ca,0x7e8,0x7eb,0x7f6,'.',0}, 1,
643 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
647 {0x780,0x798,0x7a5,0x7a6,0x7b0,'.',0}, 1,
648 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
652 {0x926,0x947,0x935,0x928,0x93e,0x917,0x930,0x940,'.',0}, 1,
653 { { 0, 9, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
657 {0x9ac,0x9be,0x982,0x9b2,0x9be,'.',0}, 1,
658 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
662 {0xa17,0xa41,0xa30,0xa2e,0xa41,0xa16,0xa40,'.',0}, 1,
663 { { 0, 8, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
667 {0xa97,0xac1,0xa9c,0xab0,0xabe,0xaa4,0xac0,'.',0}, 1,
668 { { 0, 8, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
672 {0xb13,0xb21,0xb3c,0xb3f,0xb06,'.',0}, 1,
673 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
677 {0xba4,0xbae,0xbbf,0xbb4,0xbcd,'.',0}, 1,
678 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
682 {0xc24,0xc46,0xc32,0xc41,0xc17,0xc41,'.',0}, 1,
683 { { 0, 7, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
687 {0xc95,0xca8,0xccd,0xca8,0xca1,'.',0}, 1,
688 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
692 {0xd2e,0xd32,0xd2f,0xd3e,0xd33,0xd02,'.',0}, 1,
693 { { 0, 7, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
697 {0xd82,0xd85,0xd9a,0xdcf,'.',0}, 1,
698 { { 0, 5, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
702 {0x0e04,0x0e27,0x0e32,0x0e21,0x0e1e,0x0e22,0x0e32,0x0e22,0x0e32,0x0e21,
703 0x0e2d,0x0e22,0x0e39,0x0e48,0x0e17,0x0e35,0x0e48,0x0e44,0x0e2b,0x0e19,
704 0x0e04,0x0e27,0x0e32,0x0e21,0x0e2a, 0x0e33,0x0e40,0x0e23,0x0e47,0x0e08,
705 0x0e2d,0x0e22,0x0e39,0x0e48,0x0e17,0x0e35,0x0e48,0x0e19,0x0e31,0x0e48,0x0e19,'.',0}, 1,
706 { { 0, 42, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
710 {0xead,0xeb1,0xe81,0xeaa,0xead,0xe99,0xea5,0xeb2,0xea7,'.',0}, 1,
711 { { 0, 10, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
715 {0xf04,0xf05,0xf0e,0x020,0xf51,0xf7c,0xf53,0xf0b,0xf5a,0xf53,0xf0b,
716 0xf51,0xf44,0xf0b,0xf54,0xf7c,0xf0d,'.',0}, 1,
717 { { 0, 18, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
721 {0x1019,0x103c,0x1014,0x103a,0x1019,0x102c,0x1021,0x1000,0x1039,0x1001,0x101b,0x102c,'.',0}, 1,
722 { { 0, 13, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
726 {0x10a0,0x10d0,0x10da,0x10f1,0x10fb,0x2d00,'.',0}, 1,
727 { { 0, 7, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
731 {0x1100,0x1110,0x1160,0x1170,0x11a8,'.',0}, 1,
732 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
736 {0x130d,0x12d5,0x12dd,0}, 1,
737 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
741 {0x13e3,0x13b3,0x13a9,0x0020,0x13a6,0x13ec,0x13c2,0x13af,0x13cd,0x13d7,0}, 1,
742 { { 0, 10, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
746 {0x1403,0x14c4,0x1483,0x144e,0x1450,0x1466,0}, 1,
747 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
751 {0x169b,0x1691,0x168c,0x1690,0x168b,0x169c,0}, 1,
752 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
756 {0x16a0,0x16a1,0x16a2,0x16a3,0x16a4,0x16a5,0}, 1,
757 { { 0, 6, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
761 {0x1781,0x17c1,0x1798,0x179a,0x1797,0x17b6,0x179f,0x17b6,0x19e0,0}, 1,
762 { { 0, 9, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
766 {0x182e,0x1823,0x1829,0x182d,0x1823,0x182f,0x0020,0x182a,0x1822,0x1834,0x1822,0x182d,0x180c,0}, 1,
767 { { 0, 13, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
771 {0x1900,0x1910,0x1920,0x1930,0}, 1,
772 { { 0, 4, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
776 {0x1956,0x196d,0x1970,0x1956,0x196c,0x1973,0x1951,0x1968,0x1952,0x1970,0}, 1,
777 { { 0, 10, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
781 {0x1992,0x19c4,0}, 1,
782 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
786 {0x1a00,0x1a10,0}, 1,
787 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
791 {0x1a20,0x1a40,0x1a50,0}, 1,
792 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
796 {0x1b00,0x1b05,0x1b20,0}, 1,
797 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
801 {0x1b80,0x1b85,0x1ba0,0}, 1,
802 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
806 {0x1bc0,0x1be5,0x1bfc,0}, 1,
807 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
811 {0x1c00,0x1c20,0x1c40,0}, 1,
812 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
816 {0x1c50,0x1c5a,0x1c77,0}, 1,
817 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
820 /* Sundanese Supplement */
821 {0x1cc0,0x1cc5,0x1cc7,0}, 1,
822 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
825 /* Phonetic Extensions */
826 {0x1d00,0x1d40,0x1d70,0}, 1,
827 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
830 /* Combining diacritical marks */
831 {0x1dc0,0x300,0x1ddf,0}, 1,
832 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
835 /* Latin Extended Additional, Extended-C */
836 {0x1e00,0x1d00,0x2c60,0}, 1,
837 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
842 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
845 /* General Punctuation */
846 {0x1dc0,0x2000,0}, 1,
847 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
850 /* Superscripts and Subscripts */
851 {0x2070,0x2086,0x2000,0}, 1,
852 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
855 /* Currency, Combining Diacritical Marks for Symbols. Letterlike Symbols.. */
856 {0x20a0,0x20b8,0x2000,0x20d0,0x2100,0x2150,0x2190,0x2200,0x2300,0x2400,0x2440,0x2460,0x2500,0x2580,0x25a0,0x2600,
857 0x2700,0x27c0,0x27f0,0x2900,0x2980,0x2a00,0x2b00,0}, 1,
858 { { 0, 23, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
862 {0x2800,0x2070,0x2000,0}, 1,
863 { { 0, 3, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
867 {0x2c00,0x2c12,0}, 1,
868 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
872 {0x2c80,0x3e2,0x1f00,0}, 2,
873 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT
},
874 { 2, 1, DWRITE_SCRIPT_SHAPES_DEFAULT
} }
878 {0x2d30,0x2d4a,0}, 1,
879 { { 0, 2, DWRITE_SCRIPT_SHAPES_DEFAULT
}}
883 {0x202a,0x202c,'a','b','c','\r',0}, 3,
884 { { 0, 2, DWRITE_SCRIPT_SHAPES_NO_VISUAL
},
885 { 2, 3, DWRITE_SCRIPT_SHAPES_DEFAULT
},
886 { 5, 1, DWRITE_SCRIPT_SHAPES_NO_VISUAL
} }
889 /* LRE/PDF and other visual and non-visual codes from Common script range */
890 {0x202a,0x202c,'r','!',0x200b,'\r',0}, 3,
891 { { 0, 2, DWRITE_SCRIPT_SHAPES_NO_VISUAL
},
892 { 2, 2, DWRITE_SCRIPT_SHAPES_DEFAULT
},
893 { 4, 2, DWRITE_SCRIPT_SHAPES_NO_VISUAL
} }
895 /* keep this as end marker */
899 static void init_expected_sa(struct call_sequence
**seq
, const struct sa_test
*test
)
901 static const struct call_entry end_of_sequence
= { LastKind
};
904 flush_sequence(seq
, 0);
906 /* add expected calls */
907 for (i
= 0; i
< test
->item_count
; i
++)
909 struct call_entry call
;
911 call
.kind
= ScriptAnalysis
;
912 call
.sa
.pos
= test
->sa
[i
].pos
;
913 call
.sa
.len
= test
->sa
[i
].len
;
914 call
.sa
.shapes
= test
->sa
[i
].shapes
;
915 add_call(seq
, 0, &call
);
918 /* and stop marker */
919 add_call(seq
, 0, &end_of_sequence
);
922 static void get_script_analysis(const WCHAR
*str
, DWRITE_SCRIPT_ANALYSIS
*sa
)
924 IDWriteTextAnalyzer
*analyzer
;
929 hr
= IDWriteFactory_CreateTextAnalyzer(factory
, &analyzer
);
930 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
932 hr
= IDWriteTextAnalyzer_AnalyzeScript(analyzer
, &analysissource
, 0, lstrlenW(g_source
), &analysissink2
);
933 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
938 static void test_AnalyzeScript(void)
940 const struct sa_test
*ptr
= sa_tests
;
941 IDWriteTextAnalyzer
*analyzer
;
944 hr
= IDWriteFactory_CreateTextAnalyzer(factory
, &analyzer
);
945 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
949 g_source
= ptr
->string
;
951 init_expected_sa(expected_seq
, ptr
);
952 hr
= IDWriteTextAnalyzer_AnalyzeScript(analyzer
, &analysissource
, 0, lstrlenW(g_source
), &analysissink
);
953 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
954 ok_sequence(sequences
, ANALYZER_ID
, expected_seq
[0]->sequence
, wine_dbgstr_w(ptr
->string
), FALSE
);
958 IDWriteTextAnalyzer_Release(analyzer
);
961 struct linebreaks_test
{
962 const WCHAR text
[BREAKPOINT_COUNT
+1];
963 DWRITE_LINE_BREAKPOINT bp
[BREAKPOINT_COUNT
];
966 static struct linebreaks_test linebreaks_tests
[] = {
967 { {'A','-','B',' ','C',0x58a,'D',0x2010,'E',0x2012,'F',0x2013,'\t',0},
969 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, FALSE
, FALSE
},
970 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, DWRITE_BREAK_CONDITION_CAN_BREAK
, FALSE
, FALSE
},
971 { DWRITE_BREAK_CONDITION_CAN_BREAK
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, FALSE
, FALSE
},
972 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, DWRITE_BREAK_CONDITION_CAN_BREAK
, TRUE
, FALSE
},
973 { DWRITE_BREAK_CONDITION_CAN_BREAK
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, FALSE
, FALSE
},
974 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, DWRITE_BREAK_CONDITION_CAN_BREAK
, FALSE
, FALSE
},
975 { DWRITE_BREAK_CONDITION_CAN_BREAK
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, FALSE
, FALSE
},
976 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, DWRITE_BREAK_CONDITION_CAN_BREAK
, FALSE
, FALSE
},
977 { DWRITE_BREAK_CONDITION_CAN_BREAK
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, FALSE
, FALSE
},
978 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, DWRITE_BREAK_CONDITION_CAN_BREAK
, FALSE
, FALSE
},
979 { DWRITE_BREAK_CONDITION_CAN_BREAK
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, FALSE
, FALSE
},
980 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, FALSE
, FALSE
},
981 { DWRITE_BREAK_CONDITION_MAY_NOT_BREAK
, DWRITE_BREAK_CONDITION_CAN_BREAK
, TRUE
, FALSE
}
987 static void compare_breakpoints(const struct linebreaks_test
*test
, DWRITE_LINE_BREAKPOINT
*actual
)
989 const WCHAR
*text
= test
->text
;
990 int cmp
= memcmp(test
->bp
, actual
, sizeof(*actual
)*BREAKPOINT_COUNT
);
991 ok(!cmp
, "%s: got wrong breakpoint data\n", wine_dbgstr_w(test
->text
));
995 ok(!memcmp(&test
->bp
[i
], &actual
[i
], sizeof(*actual
)),
996 "%s: got (%d, %d, %d, %d), expected (%d, %d, %d, %d)\n",
997 wine_dbgstr_wn(&test
->text
[i
], 1),
998 g_actual_bp
[i
].breakConditionBefore
,
999 g_actual_bp
[i
].breakConditionAfter
,
1000 g_actual_bp
[i
].isWhitespace
,
1001 g_actual_bp
[i
].isSoftHyphen
,
1002 test
->bp
[i
].breakConditionBefore
,
1003 test
->bp
[i
].breakConditionAfter
,
1004 test
->bp
[i
].isWhitespace
,
1005 test
->bp
[i
].isSoftHyphen
);
1012 static void test_AnalyzeLineBreakpoints(void)
1014 static const WCHAR emptyW
[] = {0};
1015 const struct linebreaks_test
*ptr
= linebreaks_tests
;
1016 IDWriteTextAnalyzer
*analyzer
;
1019 hr
= IDWriteFactory_CreateTextAnalyzer(factory
, &analyzer
);
1020 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
1023 hr
= IDWriteTextAnalyzer_AnalyzeLineBreakpoints(analyzer
, &analysissource
, 0, 0, &analysissink
);
1024 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
1028 g_source
= ptr
->text
;
1030 memset(g_actual_bp
, 0, sizeof(g_actual_bp
));
1031 hr
= IDWriteTextAnalyzer_AnalyzeLineBreakpoints(analyzer
, &analysissource
, 0, lstrlenW(g_source
), &analysissink
);
1032 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
1033 compare_breakpoints(ptr
, g_actual_bp
);
1038 IDWriteTextAnalyzer_Release(analyzer
);
1041 static void test_GetScriptProperties(void)
1043 IDWriteTextAnalyzer1
*analyzer1
;
1044 IDWriteTextAnalyzer
*analyzer
;
1045 DWRITE_SCRIPT_ANALYSIS sa
;
1046 DWRITE_SCRIPT_PROPERTIES props
;
1049 hr
= IDWriteFactory_CreateTextAnalyzer(factory
, &analyzer
);
1050 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
1052 hr
= IDWriteTextAnalyzer_QueryInterface(analyzer
, &IID_IDWriteTextAnalyzer1
, (void**)&analyzer1
);
1053 IDWriteTextAnalyzer_Release(analyzer
);
1055 win_skip("GetScriptProperties() is not supported.\n");
1060 hr
= IDWriteTextAnalyzer1_GetScriptProperties(analyzer1
, sa
, &props
);
1061 ok(hr
== E_INVALIDARG
, "got 0x%08x\n", hr
);
1063 if (0) /* crashes on native */
1064 hr
= IDWriteTextAnalyzer1_GetScriptProperties(analyzer1
, sa
, NULL
);
1067 hr
= IDWriteTextAnalyzer1_GetScriptProperties(analyzer1
, sa
, &props
);
1068 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
1070 IDWriteTextAnalyzer1_Release(analyzer1
);
1073 struct textcomplexity_test
{
1074 const WCHAR text
[5];
1080 static const struct textcomplexity_test textcomplexity_tests
[] = {
1081 { {0}, 1, FALSE
, 1 },
1082 { {0}, 0, TRUE
, 0 },
1083 { {0x610,0}, 0, TRUE
, 0 },
1084 { {'A','B','C','D',0}, 3, TRUE
, 3 },
1085 { {'A','B','C','D',0}, 5, TRUE
, 4 },
1086 { {'A','B','C','D',0}, 10, TRUE
, 4 },
1087 { {'A',0x610,'C','D',0}, 1, TRUE
, 1 },
1088 { {'A',0x610,'C','D',0}, 2, FALSE
, 2 },
1089 { {0x610,'A','C','D',0}, 1, FALSE
, 1 },
1090 { {0x610,'A','C','D',0}, 2, FALSE
, 1 },
1091 { {0x610,0x610,'C','D',0}, 2, FALSE
, 2 },
1092 { {0xd800,'A','B',0}, 1, FALSE
, 1 },
1093 { {0xd800,'A','B',0}, 2, FALSE
, 1 },
1094 { {0xdc00,'A','B',0}, 2, FALSE
, 1 }
1097 static void test_GetTextComplexity(void)
1099 static const WCHAR textW
[] = {'A','B','C',0};
1100 IDWriteTextAnalyzer1
*analyzer1
;
1101 IDWriteTextAnalyzer
*analyzer
;
1102 IDWriteFontFace
*fontface
;
1109 hr
= IDWriteFactory_CreateTextAnalyzer(factory
, &analyzer
);
1110 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
1112 hr
= IDWriteTextAnalyzer_QueryInterface(analyzer
, &IID_IDWriteTextAnalyzer1
, (void**)&analyzer1
);
1113 IDWriteTextAnalyzer_Release(analyzer
);
1115 win_skip("GetTextComplexity() is not supported.\n");
1119 if (0) { /* crashes on native */
1120 hr
= IDWriteTextAnalyzer1_GetTextComplexity(analyzer1
, NULL
, 0, NULL
, NULL
, NULL
, NULL
);
1121 hr
= IDWriteTextAnalyzer1_GetTextComplexity(analyzer1
, NULL
, 0, NULL
, NULL
, &len
, NULL
);
1122 hr
= IDWriteTextAnalyzer1_GetTextComplexity(analyzer1
, textW
, 3, NULL
, NULL
, NULL
, NULL
);
1123 hr
= IDWriteTextAnalyzer1_GetTextComplexity(analyzer1
, textW
, 3, NULL
, NULL
, &len
, NULL
);
1124 hr
= IDWriteTextAnalyzer1_GetTextComplexity(analyzer1
, textW
, 3, NULL
, &simple
, NULL
, NULL
);
1129 hr
= IDWriteTextAnalyzer1_GetTextComplexity(analyzer1
, NULL
, 0, NULL
, &simple
, &len
, NULL
);
1130 ok(hr
== E_INVALIDARG
, "got 0x%08x\n", hr
);
1131 ok(len
== 0, "got %d\n", len
);
1132 ok(simple
== FALSE
, "got %d\n", simple
);
1137 hr
= IDWriteTextAnalyzer1_GetTextComplexity(analyzer1
, textW
, 3, NULL
, &simple
, &len
, NULL
);
1138 ok(hr
== E_INVALIDARG
, "got 0x%08x\n", hr
);
1139 ok(len
== 0, "got %d\n", len
);
1140 ok(simple
== FALSE
, "got %d\n", simple
);
1141 ok(indices
[0] == 1, "got %d\n", indices
[0]);
1143 fontface
= create_fontface();
1145 for (i
= 0; i
< sizeof(textcomplexity_tests
)/sizeof(struct textcomplexity_test
); i
++) {
1146 const struct textcomplexity_test
*ptr
= &textcomplexity_tests
[i
];
1148 simple
= !ptr
->simple
;
1150 hr
= IDWriteTextAnalyzer1_GetTextComplexity(analyzer1
, ptr
->text
, ptr
->length
, fontface
, &simple
, &len
, indices
);
1151 ok(hr
== S_OK
, "%d: got 0x%08x\n", i
, hr
);
1152 ok(len
== ptr
->len_read
, "%d: read length: got %d, expected %d\n", i
, len
, ptr
->len_read
);
1153 ok(simple
== ptr
->simple
, "%d: simple: got %d, expected %d\n", i
, simple
, ptr
->simple
);
1154 if (simple
&& ptr
->length
)
1155 ok(indices
[0] > 0, "%d: got %d\n", i
, indices
[0]);
1157 ok(indices
[0] == 0, "%d: got %d\n", i
, indices
[0]);
1160 IDWriteFontFace_Release(fontface
);
1161 IDWriteTextAnalyzer1_Release(analyzer1
);
1164 static void test_numbersubstitution(void)
1166 static const WCHAR dummyW
[] = {'d','u','m','m','y',0};
1167 IDWriteNumberSubstitution
*substitution
;
1170 hr
= IDWriteFactory_CreateNumberSubstitution(factory
, DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE
, NULL
, FALSE
, &substitution
);
1171 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
1172 IDWriteNumberSubstitution_Release(substitution
);
1174 /* invalid method */
1175 hr
= IDWriteFactory_CreateNumberSubstitution(factory
, DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL
+1, NULL
, FALSE
, &substitution
);
1176 ok(hr
== E_INVALIDARG
, "got 0x%08x\n", hr
);
1178 /* invalid method */
1179 hr
= IDWriteFactory_CreateNumberSubstitution(factory
, -1, NULL
, FALSE
, &substitution
);
1180 ok(hr
== E_INVALIDARG
, "got 0x%08x\n", hr
);
1182 /* invalid locale */
1183 hr
= IDWriteFactory_CreateNumberSubstitution(factory
, DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL
, dummyW
, FALSE
, &substitution
);
1184 ok(hr
== E_INVALIDARG
, "got 0x%08x\n", hr
);
1186 hr
= IDWriteFactory_CreateNumberSubstitution(factory
, DWRITE_NUMBER_SUBSTITUTION_METHOD_CONTEXTUAL
, dummyW
, FALSE
, &substitution
);
1187 ok(hr
== E_INVALIDARG
, "got 0x%08x\n", hr
);
1189 hr
= IDWriteFactory_CreateNumberSubstitution(factory
, DWRITE_NUMBER_SUBSTITUTION_METHOD_NATIONAL
, dummyW
, FALSE
, &substitution
);
1190 ok(hr
== E_INVALIDARG
, "got 0x%08x\n", hr
);
1192 /* invalid locale, but it's not needed for this method */
1193 hr
= IDWriteFactory_CreateNumberSubstitution(factory
, DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE
, dummyW
, FALSE
, &substitution
);
1194 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
1195 IDWriteNumberSubstitution_Release(substitution
);
1198 static void test_GetGlyphs(void)
1200 static const WCHAR test1W
[] = {'<','B',' ','C',0};
1201 static const WCHAR test2W
[] = {'<','B','\t','C',0};
1202 static const WCHAR test3W
[] = {0x202a,0x202c,0};
1203 DWRITE_SHAPING_GLYPH_PROPERTIES shapingprops
[20];
1204 DWRITE_SHAPING_TEXT_PROPERTIES props
[20];
1205 UINT32 maxglyphcount
, actual_count
;
1206 IDWriteTextAnalyzer
*analyzer
;
1207 IDWriteFontFace
*fontface
;
1208 DWRITE_SCRIPT_ANALYSIS sa
;
1209 DWRITE_GLYPH_OFFSET offsets
[10];
1210 UINT16 clustermap
[10];
1216 hr
= IDWriteFactory_CreateTextAnalyzer(factory
, &analyzer
);
1217 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
1219 fontface
= create_fontface();
1223 sa
.shapes
= DWRITE_SCRIPT_SHAPES_DEFAULT
;
1224 hr
= IDWriteTextAnalyzer_GetGlyphs(analyzer
, test1W
, lstrlenW(test1W
), fontface
, FALSE
, FALSE
, &sa
, NULL
,
1225 NULL
, NULL
, NULL
, 0, maxglyphcount
, clustermap
, props
, glyphs1
, shapingprops
, &actual_count
);
1226 ok(hr
== E_NOT_SUFFICIENT_BUFFER
, "got 0x%08x\n", hr
);
1229 /* NULL fontface - crashes on Windows */
1230 hr
= IDWriteTextAnalyzer_GetGlyphs(analyzer
, test1W
, lstrlenW(test1W
), NULL
, FALSE
, FALSE
, &sa
, NULL
,
1231 NULL
, NULL
, NULL
, 0, maxglyphcount
, clustermap
, props
, glyphs1
, shapingprops
, &actual_count
);
1234 /* invalid script id */
1238 sa
.shapes
= DWRITE_SCRIPT_SHAPES_DEFAULT
;
1239 hr
= IDWriteTextAnalyzer_GetGlyphs(analyzer
, test1W
, lstrlenW(test1W
), fontface
, FALSE
, FALSE
, &sa
, NULL
,
1240 NULL
, NULL
, NULL
, 0, maxglyphcount
, clustermap
, props
, glyphs1
, shapingprops
, &actual_count
);
1241 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
1242 ok(actual_count
== 4, "got %d\n", actual_count
);
1243 ok(sa
.script
== 999, "got %u\n", sa
.script
);
1245 /* no '\t' -> ' ' replacement */
1248 hr
= IDWriteTextAnalyzer_GetGlyphs(analyzer
, test1W
, lstrlenW(test1W
), fontface
, FALSE
, FALSE
, &sa
, NULL
,
1249 NULL
, NULL
, NULL
, 0, maxglyphcount
, clustermap
, props
, glyphs1
, shapingprops
, &actual_count
);
1250 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
1251 ok(actual_count
== 4, "got %d\n", actual_count
);
1254 hr
= IDWriteTextAnalyzer_GetGlyphs(analyzer
, test2W
, lstrlenW(test2W
), fontface
, FALSE
, FALSE
, &sa
, NULL
,
1255 NULL
, NULL
, NULL
, 0, maxglyphcount
, clustermap
, props
, glyphs2
, shapingprops
, &actual_count
);
1256 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
1257 ok(actual_count
== 4, "got %d\n", actual_count
);
1258 ok(glyphs1
[2] != glyphs2
[2], "got %d\n", glyphs1
[2]);
1260 /* check that mirroring works */
1263 hr
= IDWriteTextAnalyzer_GetGlyphs(analyzer
, test1W
, lstrlenW(test1W
), fontface
, FALSE
, FALSE
, &sa
, NULL
,
1264 NULL
, NULL
, NULL
, 0, maxglyphcount
, clustermap
, props
, glyphs1
, shapingprops
, &actual_count
);
1265 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
1266 ok(actual_count
== 4, "got %d\n", actual_count
);
1269 hr
= IDWriteTextAnalyzer_GetGlyphs(analyzer
, test1W
, lstrlenW(test1W
), fontface
, FALSE
, TRUE
, &sa
, NULL
,
1270 NULL
, NULL
, NULL
, 0, maxglyphcount
, clustermap
, props
, glyphs2
, shapingprops
, &actual_count
);
1271 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
1272 ok(actual_count
== 4, "got %d\n", actual_count
);
1273 ok(glyphs1
[0] != glyphs2
[0], "got %d\n", glyphs1
[0]);
1275 /* embedded control codes, with unknown script id 0 */
1277 hr
= IDWriteTextAnalyzer_GetGlyphs(analyzer
, test3W
, lstrlenW(test3W
), fontface
, FALSE
, TRUE
, &sa
, NULL
,
1278 NULL
, NULL
, NULL
, 0, maxglyphcount
, clustermap
, props
, glyphs1
, shapingprops
, &actual_count
);
1279 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
1280 ok(actual_count
== 2, "got %d\n", actual_count
);
1281 ok(glyphs1
[0] == 0, "got %d\n", glyphs1
[0]);
1282 ok(glyphs1
[1] == 0, "got %d\n", glyphs1
[1]);
1283 ok(shapingprops
[0].isClusterStart
== 1, "got %d\n", shapingprops
[0].isClusterStart
);
1284 ok(shapingprops
[0].isZeroWidthSpace
== 0, "got %d\n", shapingprops
[0].isZeroWidthSpace
);
1285 ok(shapingprops
[1].isClusterStart
== 1, "got %d\n", shapingprops
[1].isClusterStart
);
1286 ok(shapingprops
[1].isZeroWidthSpace
== 0, "got %d\n", shapingprops
[1].isZeroWidthSpace
);
1287 ok(clustermap
[0] == 0, "got %d\n", clustermap
[0]);
1288 ok(clustermap
[1] == 1, "got %d\n", clustermap
[1]);
1290 memset(advances
, 0, sizeof(advances
));
1291 hr
= IDWriteTextAnalyzer_GetGlyphPlacements(analyzer
, test3W
, clustermap
, props
, lstrlenW(test3W
),
1292 glyphs1
, shapingprops
, actual_count
, fontface
, 10.0, FALSE
, FALSE
, &sa
, NULL
, NULL
,
1293 NULL
, 0, advances
, offsets
);
1294 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
1295 ok(advances
[0] == 10.0, "got %.2f\n", advances
[0]);
1296 ok(advances
[1] == 10.0, "got %.2f\n", advances
[1]);
1298 /* embedded control codes with proper script */
1300 get_script_analysis(test3W
, &sa
);
1301 ok(sa
.script
!= 0, "got %d\n", sa
.script
);
1303 hr
= IDWriteTextAnalyzer_GetGlyphs(analyzer
, test3W
, lstrlenW(test3W
), fontface
, FALSE
, FALSE
, &sa
, NULL
,
1304 NULL
, NULL
, NULL
, 0, maxglyphcount
, clustermap
, props
, glyphs1
, shapingprops
, &actual_count
);
1305 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
1306 ok(actual_count
== 2, "got %d\n", actual_count
);
1307 ok(glyphs1
[0] == 0, "got %d\n", glyphs1
[0]);
1308 ok(glyphs1
[1] == 0, "got %d\n", glyphs1
[1]);
1309 ok(shapingprops
[0].isClusterStart
== 1, "got %d\n", shapingprops
[0].isClusterStart
);
1310 ok(shapingprops
[0].isZeroWidthSpace
== 0, "got %d\n", shapingprops
[0].isZeroWidthSpace
);
1311 ok(shapingprops
[1].isClusterStart
== 1, "got %d\n", shapingprops
[1].isClusterStart
);
1312 ok(shapingprops
[1].isZeroWidthSpace
== 0, "got %d\n", shapingprops
[1].isZeroWidthSpace
);
1313 ok(clustermap
[0] == 0, "got %d\n", clustermap
[0]);
1314 ok(clustermap
[1] == 1, "got %d\n", clustermap
[1]);
1316 memset(advances
, 0, sizeof(advances
));
1317 hr
= IDWriteTextAnalyzer_GetGlyphPlacements(analyzer
, test3W
, clustermap
, props
, lstrlenW(test3W
),
1318 glyphs1
, shapingprops
, actual_count
, fontface
, 10.0, FALSE
, FALSE
, &sa
, NULL
, NULL
,
1319 NULL
, 0, advances
, offsets
);
1320 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
1321 ok(advances
[0] == 10.0, "got %.2f\n", advances
[0]);
1322 ok(advances
[1] == 10.0, "got %.2f\n", advances
[1]);
1324 IDWriteTextAnalyzer_Release(analyzer
);
1325 IDWriteFontFace_Release(fontface
);
1328 static BOOL
has_feature(const DWRITE_FONT_FEATURE_TAG
*tags
, UINT32 count
, DWRITE_FONT_FEATURE_TAG feature
)
1332 for (i
= 0; i
< count
; i
++)
1333 if (tags
[i
] == feature
) return TRUE
;
1337 static void test_GetTypographicFeatures(void)
1339 static const WCHAR localeW
[] = {'c','a','d','a','b','r','a',0};
1340 static const WCHAR arabicW
[] = {0x064a,0x064f,0x0633,0};
1341 static const WCHAR abcW
[] = {'a','b','c',0};
1342 DWRITE_FONT_FEATURE_TAG tags
[20];
1343 IDWriteTextAnalyzer2
*analyzer2
;
1344 IDWriteTextAnalyzer
*analyzer
;
1345 IDWriteFontFace
*fontface
;
1346 DWRITE_SCRIPT_ANALYSIS sa
;
1351 hr
= IDWriteFactory_CreateTextAnalyzer(factory
, &analyzer
);
1352 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
1354 hr
= IDWriteTextAnalyzer_QueryInterface(analyzer
, &IID_IDWriteTextAnalyzer2
, (void**)&analyzer2
);
1355 IDWriteTextAnalyzer_Release(analyzer
);
1357 win_skip("GetTypographicFeatures() is not supported.\n");
1361 fontface
= create_fontface();
1363 get_script_analysis(abcW
, &sa
);
1365 hr
= IDWriteTextAnalyzer2_GetTypographicFeatures(analyzer2
, fontface
, sa
, NULL
, 0, &count
, NULL
);
1367 ok(hr
== E_NOT_SUFFICIENT_BUFFER
, "got 0x%08x\n", hr
);
1368 ok(count
> 0, "got %u\n", count
);
1370 /* invalid locale name is ignored */
1371 get_script_analysis(abcW
, &sa
);
1373 hr
= IDWriteTextAnalyzer2_GetTypographicFeatures(analyzer2
, fontface
, sa
, localeW
, 0, &count
, NULL
);
1375 ok(hr
== E_NOT_SUFFICIENT_BUFFER
, "got 0x%08x\n", hr
);
1376 ok(count
> 0, "got %u\n", count
);
1378 /* both GSUB and GPOS features are reported */
1379 get_script_analysis(arabicW
, &sa
);
1380 memset(tags
, 0, sizeof(tags
));
1382 hr
= IDWriteTextAnalyzer2_GetTypographicFeatures(analyzer2
, fontface
, sa
, NULL
, sizeof(tags
)/sizeof(tags
[0]), &count
, tags
);
1383 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
1385 ok(count
> 0, "got %u\n", count
);
1386 ret
= has_feature(tags
, count
, DWRITE_FONT_FEATURE_TAG_CONTEXTUAL_ALTERNATES
);
1387 ok(ret
, "expected 'calt' feature\n");
1388 ret
= has_feature(tags
, count
, DWRITE_FONT_FEATURE_TAG_MARK_TO_MARK_POSITIONING
);
1389 ok(ret
, "expected 'mkmk' feature\n");
1391 get_script_analysis(abcW
, &sa
);
1392 memset(tags
, 0, sizeof(tags
));
1394 hr
= IDWriteTextAnalyzer2_GetTypographicFeatures(analyzer2
, fontface
, sa
, NULL
, sizeof(tags
)/sizeof(tags
[0]), &count
, tags
);
1395 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
1397 ok(count
> 0, "got %u\n", count
);
1398 ret
= has_feature(tags
, count
, DWRITE_FONT_FEATURE_TAG_GLYPH_COMPOSITION_DECOMPOSITION
);
1399 ok(ret
, "expected 'ccmp' feature\n");
1400 ret
= has_feature(tags
, count
, DWRITE_FONT_FEATURE_TAG_MARK_TO_MARK_POSITIONING
);
1401 ok(ret
, "expected 'mkmk' feature\n");
1403 ret
= has_feature(tags
, count
, DWRITE_FONT_FEATURE_TAG_CONTEXTUAL_ALTERNATES
);
1404 ok(!ret
, "unexpected 'calt' feature\n");
1406 IDWriteFontFace_Release(fontface
);
1407 IDWriteTextAnalyzer2_Release(analyzer2
);
1410 static void test_GetGlyphPlacements(void)
1412 DWRITE_SHAPING_GLYPH_PROPERTIES glyphprops
[2];
1413 DWRITE_SHAPING_TEXT_PROPERTIES textprops
[2];
1414 static const WCHAR aW
[] = {'A','D',0};
1415 UINT16 clustermap
[2], glyphs
[2];
1416 DWRITE_GLYPH_OFFSET offsets
[2];
1417 IDWriteTextAnalyzer
*analyzer
;
1418 IDWriteFontFace
*fontface
;
1419 DWRITE_SCRIPT_ANALYSIS sa
;
1425 hr
= IDWriteFactory_CreateTextAnalyzer(factory
, &analyzer
);
1426 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
1428 path
= create_testfontfile(test_fontfile
);
1429 fontface
= create_testfontface(path
);
1431 get_script_analysis(aW
, &sa
);
1434 hr
= IDWriteTextAnalyzer_GetGlyphs(analyzer
, aW
, len
, fontface
, FALSE
, FALSE
, &sa
, NULL
,
1435 NULL
, NULL
, NULL
, 0, len
, clustermap
, textprops
, glyphs
, glyphprops
, &count
);
1436 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
1437 ok(count
== 2, "got %u\n", count
);
1439 /* just return on zero glyphs */
1440 advances
[0] = advances
[1] = 1.0;
1441 offsets
[0].advanceOffset
= offsets
[0].ascenderOffset
= 2.0;
1442 hr
= IDWriteTextAnalyzer_GetGlyphPlacements(analyzer
, aW
, clustermap
, textprops
,
1443 len
, glyphs
, glyphprops
, 0, fontface
, 0.0, FALSE
, FALSE
, &sa
, NULL
, NULL
,
1444 NULL
, 0, advances
, offsets
);
1445 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
1446 ok(advances
[0] == 1.0, "got %.2f\n", advances
[0]);
1447 ok(offsets
[0].advanceOffset
== 2.0 && offsets
[0].ascenderOffset
== 2.0, "got %.2f,%.2f\n",
1448 offsets
[0].advanceOffset
, offsets
[0].ascenderOffset
);
1450 /* advances/offsets are scaled with provided font emSize and designed eM box size */
1451 advances
[0] = advances
[1] = 1.0;
1452 memset(offsets
, 0xcc, sizeof(offsets
));
1453 hr
= IDWriteTextAnalyzer_GetGlyphPlacements(analyzer
, aW
, clustermap
, textprops
,
1454 len
, glyphs
, glyphprops
, len
, fontface
, 0.0, FALSE
, FALSE
, &sa
, NULL
, NULL
,
1455 NULL
, 0, advances
, offsets
);
1456 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
1457 ok(advances
[0] == 0.0, "got %.2f\n", advances
[0]);
1458 ok(offsets
[0].advanceOffset
== 0.0 && offsets
[0].ascenderOffset
== 0.0, "got %.2f,%.2f\n",
1459 offsets
[0].advanceOffset
, offsets
[0].ascenderOffset
);
1461 advances
[0] = advances
[1] = 1.0;
1462 memset(offsets
, 0xcc, sizeof(offsets
));
1463 hr
= IDWriteTextAnalyzer_GetGlyphPlacements(analyzer
, aW
, clustermap
, textprops
,
1464 len
, glyphs
, glyphprops
, len
, fontface
, 2048.0, FALSE
, FALSE
, &sa
, NULL
, NULL
,
1465 NULL
, 0, advances
, offsets
);
1466 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
1467 ok(advances
[0] == 1000.0, "got %.2f\n", advances
[0]);
1468 ok(offsets
[0].advanceOffset
== 0.0 && offsets
[0].ascenderOffset
== 0.0, "got %.2f,%.2f\n",
1469 offsets
[0].advanceOffset
, offsets
[0].ascenderOffset
);
1471 advances
[0] = advances
[1] = 1.0;
1472 memset(offsets
, 0xcc, sizeof(offsets
));
1473 hr
= IDWriteTextAnalyzer_GetGlyphPlacements(analyzer
, aW
, clustermap
, textprops
,
1474 len
, glyphs
, glyphprops
, len
, fontface
, 1024.0, FALSE
, FALSE
, &sa
, NULL
, NULL
,
1475 NULL
, 0, advances
, offsets
);
1476 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
1477 ok(advances
[0] == 500.0, "got %.2f\n", advances
[0]);
1478 ok(advances
[1] == 500.0, "got %.2f\n", advances
[1]);
1479 ok(offsets
[0].advanceOffset
== 0.0 && offsets
[0].ascenderOffset
== 0.0, "got %.2f,%.2f\n",
1480 offsets
[0].advanceOffset
, offsets
[0].ascenderOffset
);
1482 advances
[0] = advances
[1] = 1.0;
1483 memset(offsets
, 0xcc, sizeof(offsets
));
1484 hr
= IDWriteTextAnalyzer_GetGlyphPlacements(analyzer
, aW
, clustermap
, textprops
,
1485 len
, glyphs
, glyphprops
, len
, fontface
, 20.48, FALSE
, FALSE
, &sa
, NULL
, NULL
,
1486 NULL
, 0, advances
, offsets
);
1487 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
1488 ok(advances
[0] == 10.0, "got %.2f\n", advances
[0]);
1489 ok(advances
[1] == 10.0, "got %.2f\n", advances
[1]);
1490 ok(offsets
[0].advanceOffset
== 0.0 && offsets
[0].ascenderOffset
== 0.0, "got %.2f,%.2f\n",
1491 offsets
[0].advanceOffset
, offsets
[0].ascenderOffset
);
1493 /* without clustermap */
1494 advances
[0] = advances
[1] = 1.0;
1495 hr
= IDWriteTextAnalyzer_GetGlyphPlacements(analyzer
, aW
, NULL
, textprops
,
1496 len
, glyphs
, glyphprops
, len
, fontface
, 1024.0, FALSE
, FALSE
, &sa
, NULL
, NULL
,
1497 NULL
, 0, advances
, offsets
);
1498 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
1499 ok(advances
[0] == 500.0, "got %.2f\n", advances
[0]);
1500 ok(advances
[1] == 500.0, "got %.2f\n", advances
[1]);
1502 /* it's happy to use negative size too */
1503 advances
[0] = advances
[1] = 1.0;
1504 memset(offsets
, 0xcc, sizeof(offsets
));
1505 hr
= IDWriteTextAnalyzer_GetGlyphPlacements(analyzer
, aW
, clustermap
, textprops
,
1506 len
, glyphs
, glyphprops
, len
, fontface
, -10.24, FALSE
, FALSE
, &sa
, NULL
, NULL
,
1507 NULL
, 0, advances
, offsets
);
1508 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
1509 ok(advances
[0] == -5.0, "got %.2f\n", advances
[0]);
1510 ok(offsets
[0].advanceOffset
== 0.0 && offsets
[0].ascenderOffset
== 0.0, "got %.2f,%.2f\n",
1511 offsets
[0].advanceOffset
, offsets
[0].ascenderOffset
);
1513 IDWriteTextAnalyzer_Release(analyzer
);
1514 IDWriteFontFace_Release(fontface
);
1515 DELETE_FONTFILE(path
);
1518 struct spacing_test
{
1524 FLOAT modified_advances
[3];
1525 FLOAT modified_offsets
[3];
1526 BOOL single_cluster
;
1530 static const struct spacing_test spacing_tests
[] = {
1531 { 0.0, 0.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 } }, /* 0 */
1532 { 0.0, 0.0, 2.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 } },
1533 { 1.0, 0.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 11.0, 12.0 }, { 3.0, 4.0 } },
1534 { 1.0, 1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 12.0, 13.0 }, { 3.0, 4.0 } },
1535 { 1.0, -1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 3.0, 4.0 } },
1536 { 0.0, -10.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 0.0, 1.0 }, { 2.0, 3.0 } }, /* 5 */
1537 { -5.0, -4.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 5.0, 5.0 }, { -1.0, -0.5 } },
1538 { -5.0, -5.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 5.0, 5.0 }, { -0.5, 0.0 } },
1539 { 2.0, 0.0, 5.0, { 1.0, 2.0 }, { 2.0, 3.0 }, { 7.0, 7.0 }, { 6.0, 6.5 } },
1540 { 2.0, 1.0, 5.0, { 1.0, 2.0 }, { 2.0, 3.0 }, { 8.0, 8.0 }, { 6.0, 6.5 } },
1541 { 2.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 7.0, 7.0 }, { 4.0, 5.0 } }, /* 10 */
1542 { 1.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 6.0, 6.0 }, { 3.0, 4.0 } },
1543 { -10.0, 1.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 6.0, 6.0 }, { -3.0, -3.0 } },
1544 { 0.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 5.0, 5.0 }, { 2.0, 3.0 } },
1545 { 1.0, -10.0, -5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 0.0, 0.0 }, { 2.0, 3.0 } },
1546 { -10.0, 1.0, 5.0, { 8.0, 11.0 }, { 2.0, 3.0 }, { 6.0, 6.0 }, { -1.0, -3.0 } }, /* 15 */
1547 /* cluster of more than 1 glyph */
1548 { 0.0, 0.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 }, TRUE
},
1549 { 1.0, 0.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.5 }, { 11.0, 11.0 }, { 3.0, 3.5 }, TRUE
},
1550 { 1.0, 1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 11.0, 12.0 }, { 3.0, 3.0 }, TRUE
},
1551 { 1.0, -1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 11.0, 10.0 }, { 3.0, 3.0 }, TRUE
},
1552 { 0.0, -10.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 1.0 }, { 2.0, 3.0 }, TRUE
}, /* 20 */
1553 { 0.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 1.0 }, { 2.0, 3.0 }, TRUE
},
1554 { 1.0, -10.0, -5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 0.0, 0.0 }, { 2.0, 3.0 }, TRUE
},
1555 { -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
},
1556 { -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
},
1557 { -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 */
1558 { -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
},
1559 { 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
},
1560 { -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
},
1561 /* isZeroWidthSpace set */
1562 { 1.0, 0.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 12.0 }, { 2.0, 4.0 }, FALSE
, { TRUE
, FALSE
} },
1563 { 1.0, 1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 13.0 }, { 2.0, 4.0 }, FALSE
, { TRUE
, FALSE
} }, /* 30 */
1564 { 1.0, -1.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 3.0, 3.0 }, FALSE
, { FALSE
, TRUE
} },
1565 { 0.0, -10.0, 0.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 1.0 }, { 2.0, 3.0 }, FALSE
, { TRUE
, FALSE
} },
1566 { -5.0, -4.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 5.0, 11.0 }, { -1.0, 3.0 }, FALSE
, { FALSE
, TRUE
} },
1567 { -5.0, -5.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 }, FALSE
, { TRUE
, TRUE
} },
1568 { 2.0, 0.0, 5.0, { 1.0, 2.0 }, { 2.0, 3.0 }, { 7.0, 2.0 }, { 6.0, 3.0 }, FALSE
, { FALSE
, TRUE
} }, /* 35 */
1569 { 2.0, 1.0, 5.0, { 1.0, 2.0 }, { 2.0, 3.0 }, { 8.0, 2.0 }, { 6.0, 3.0 }, FALSE
, { FALSE
, TRUE
} },
1570 { 2.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 }, FALSE
, { TRUE
, TRUE
} },
1571 { 1.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 6.0, 11.0 }, { 3.0, 3.0 }, FALSE
, { FALSE
, TRUE
} },
1572 { -10.0, 1.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 10.0, 11.0 }, { 2.0, 3.0 }, FALSE
, { TRUE
, TRUE
} },
1573 { 0.0, -10.0, 5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 5.0, 11.0 }, { 2.0, 3.0 }, FALSE
, { FALSE
, TRUE
} }, /* 40 */
1574 { 1.0, -10.0, -5.0, { 10.0, 11.0 }, { 2.0, 3.0 }, { 0.0, 0.0 }, { 2.0, 3.0 }, FALSE
, { TRUE
, FALSE
} },
1575 { -10.0, 1.0, 5.0, { 8.0, 11.0 }, { 2.0, 3.0 }, { 6.0, 11.0 }, { -1.0, 3.0 }, FALSE
, { FALSE
, TRUE
} },
1578 static void test_ApplyCharacterSpacing(void)
1580 DWRITE_SHAPING_GLYPH_PROPERTIES props
[3];
1581 IDWriteTextAnalyzer1
*analyzer1
;
1582 IDWriteTextAnalyzer
*analyzer
;
1583 UINT16 clustermap
[2];
1587 hr
= IDWriteFactory_CreateTextAnalyzer(factory
, &analyzer
);
1588 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
1590 hr
= IDWriteTextAnalyzer_QueryInterface(analyzer
, &IID_IDWriteTextAnalyzer1
, (void**)&analyzer1
);
1591 IDWriteTextAnalyzer_Release(analyzer
);
1593 win_skip("ApplyCharacterSpacing() is not supported.\n");
1597 for (i
= 0; i
< sizeof(spacing_tests
)/sizeof(spacing_tests
[0]); i
++) {
1598 const struct spacing_test
*ptr
= spacing_tests
+ i
;
1599 DWRITE_GLYPH_OFFSET offsets
[3];
1603 offsets
[0].advanceOffset
= ptr
->offsets
[0];
1604 offsets
[1].advanceOffset
= ptr
->offsets
[1];
1605 offsets
[2].advanceOffset
= ptr
->offsets
[2];
1606 /* Ascender offsets are never thouched as spacing applies in reading direction only,
1607 we'll only test them to see if they are not changed */
1608 offsets
[0].ascenderOffset
= 23.0;
1609 offsets
[1].ascenderOffset
= 32.0;
1610 offsets
[2].ascenderOffset
= 31.0;
1612 glyph_count
= ptr
->advances
[2] > 0.0 ? 3 : 2;
1613 if (ptr
->single_cluster
) {
1618 /* trivial case with one glyph per cluster */
1623 advances
[0] = advances
[1] = 123.45;
1624 memset(props
, 0, sizeof(props
));
1625 props
[0].isZeroWidthSpace
= ptr
->is_ZWS
[0];
1626 props
[1].isZeroWidthSpace
= ptr
->is_ZWS
[1];
1627 props
[2].isZeroWidthSpace
= ptr
->is_ZWS
[2];
1629 hr
= IDWriteTextAnalyzer1_ApplyCharacterSpacing(analyzer1
,
1633 sizeof(clustermap
)/sizeof(clustermap
[0]),
1641 /* invalid argument cases */
1642 if (ptr
->min_advance
< 0.0)
1643 ok(hr
== E_INVALIDARG
, "%d: got 0x%08x\n", i
, hr
);
1645 ok(hr
== S_OK
, "%d: got 0x%08x\n", i
, hr
);
1647 ok(ptr
->modified_advances
[0] == advances
[0], "%d: got advance[0] %.2f, expected %.2f\n", i
, advances
[0], ptr
->modified_advances
[0]);
1648 ok(ptr
->modified_advances
[1] == advances
[1], "%d: got advance[1] %.2f, expected %.2f\n", i
, advances
[1], ptr
->modified_advances
[1]);
1649 if (glyph_count
> 2)
1650 ok(ptr
->modified_advances
[2] == advances
[2], "%d: got advance[2] %.2f, expected %.2f\n", i
, advances
[2], ptr
->modified_advances
[2]);
1652 ok(ptr
->modified_offsets
[0] == offsets
[0].advanceOffset
, "%d: got offset[0] %.2f, expected %.2f\n", i
,
1653 offsets
[0].advanceOffset
, ptr
->modified_offsets
[0]);
1654 ok(ptr
->modified_offsets
[1] == offsets
[1].advanceOffset
, "%d: got offset[1] %.2f, expected %.2f\n", i
,
1655 offsets
[1].advanceOffset
, ptr
->modified_offsets
[1]);
1656 if (glyph_count
> 2)
1657 ok(ptr
->modified_offsets
[2] == offsets
[2].advanceOffset
, "%d: got offset[2] %.2f, expected %.2f\n", i
,
1658 offsets
[2].advanceOffset
, ptr
->modified_offsets
[2]);
1660 ok(offsets
[0].ascenderOffset
== 23.0, "%d: unexpected ascenderOffset %.2f\n", i
, offsets
[0].ascenderOffset
);
1661 ok(offsets
[1].ascenderOffset
== 32.0, "%d: unexpected ascenderOffset %.2f\n", i
, offsets
[1].ascenderOffset
);
1662 ok(offsets
[2].ascenderOffset
== 31.0, "%d: unexpected ascenderOffset %.2f\n", i
, offsets
[2].ascenderOffset
);
1665 ok(ptr
->modified_advances
[0] == advances
[0], "%d: got advance[0] %.2f, expected %.2f\n", i
, advances
[0], ptr
->modified_advances
[0]);
1666 ok(ptr
->modified_advances
[1] == advances
[1], "%d: got advance[1] %.2f, expected %.2f\n", i
, advances
[1], ptr
->modified_advances
[1]);
1667 ok(ptr
->offsets
[0] == offsets
[0].advanceOffset
, "%d: got offset[0] %.2f, expected %.2f\n", i
,
1668 offsets
[0].advanceOffset
, ptr
->modified_offsets
[0]);
1669 ok(ptr
->offsets
[1] == offsets
[1].advanceOffset
, "%d: got offset[1] %.2f, expected %.2f\n", i
,
1670 offsets
[1].advanceOffset
, ptr
->modified_offsets
[1]);
1671 ok(offsets
[0].ascenderOffset
== 23.0, "%d: unexpected ascenderOffset %.2f\n", i
, offsets
[0].ascenderOffset
);
1672 ok(offsets
[1].ascenderOffset
== 32.0, "%d: unexpected ascenderOffset %.2f\n", i
, offsets
[1].ascenderOffset
);
1676 IDWriteTextAnalyzer1_Release(analyzer1
);
1679 struct orientation_transf_test
{
1680 DWRITE_GLYPH_ORIENTATION_ANGLE angle
;
1685 static const struct orientation_transf_test ot_tests
[] = {
1686 { DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES
, FALSE
, { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 } },
1687 { DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES
, FALSE
, { 0.0, 1.0, -1.0, 0.0, 0.0, 0.0 } },
1688 { DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES
, FALSE
, { -1.0, 0.0, 0.0, -1.0, 0.0, 0.0 } },
1689 { DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES
, FALSE
, { 0.0, -1.0, 1.0, 0.0, 0.0, 0.0 } },
1690 { DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES
, TRUE
, { 0.0, 1.0, -1.0, 0.0, 0.0, 0.0 } },
1691 { DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES
, TRUE
, { -1.0, 0.0, 0.0, -1.0, 0.0, 0.0 } },
1692 { DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES
, TRUE
, { 0.0, -1.0, 1.0, 0.0, 0.0, 0.0 } },
1693 { DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES
, TRUE
, { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 } }
1696 static inline const char *dbgstr_matrix(const DWRITE_MATRIX
*m
)
1698 static char buff
[64];
1699 sprintf(buff
, "{%.2f, %.2f, %.2f, %.2f, %.2f, %.2f}", m
->m11
, m
->m12
,
1700 m
->m21
, m
->m22
, m
->dx
, m
->dy
);
1704 static void test_GetGlyphOrientationTransform(void)
1706 IDWriteTextAnalyzer2
*analyzer2
;
1707 IDWriteTextAnalyzer1
*analyzer1
;
1708 IDWriteTextAnalyzer
*analyzer
;
1709 FLOAT originx
, originy
;
1714 hr
= IDWriteFactory_CreateTextAnalyzer(factory
, &analyzer
);
1715 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
1717 hr
= IDWriteTextAnalyzer_QueryInterface(analyzer
, &IID_IDWriteTextAnalyzer1
, (void**)&analyzer1
);
1718 IDWriteTextAnalyzer_Release(analyzer
);
1720 win_skip("GetGlyphOrientationTransform() is not supported.\n");
1724 /* invalid angle value */
1725 memset(&m
, 0xcc, sizeof(m
));
1726 hr
= IDWriteTextAnalyzer1_GetGlyphOrientationTransform(analyzer1
,
1727 DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES
+ 1, FALSE
, &m
);
1728 ok(hr
== E_INVALIDARG
, "got 0x%08x\n", hr
);
1729 ok(m
.m11
== 0.0, "got %.2f\n", m
.m11
);
1731 for (i
= 0; i
< sizeof(ot_tests
)/sizeof(ot_tests
[0]); i
++) {
1732 memset(&m
, 0, sizeof(m
));
1733 hr
= IDWriteTextAnalyzer1_GetGlyphOrientationTransform(analyzer1
, ot_tests
[i
].angle
,
1734 ot_tests
[i
].is_sideways
, &m
);
1735 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
1736 ok(!memcmp(&ot_tests
[i
].m
, &m
, sizeof(m
)), "%d: wrong matrix %s\n", i
, dbgstr_matrix(&m
));
1739 hr
= IDWriteTextAnalyzer1_QueryInterface(analyzer1
, &IID_IDWriteTextAnalyzer2
, (void**)&analyzer2
);
1740 IDWriteTextAnalyzer1_Release(analyzer1
);
1742 win_skip("IDWriteTextAnalyzer2::GetGlyphOrientationTransform() is not supported.\n");
1746 /* invalid angle value */
1747 memset(&m
, 0xcc, sizeof(m
));
1748 hr
= IDWriteTextAnalyzer2_GetGlyphOrientationTransform(analyzer2
,
1749 DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES
+ 1, FALSE
, 0.0, 0.0, &m
);
1750 ok(hr
== E_INVALIDARG
, "got 0x%08x\n", hr
);
1751 ok(m
.m11
== 0.0, "got %.2f\n", m
.m11
);
1755 for (i
= 0; i
< sizeof(ot_tests
)/sizeof(ot_tests
[0]); i
++) {
1756 DWRITE_GLYPH_ORIENTATION_ANGLE angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES
;
1757 DWRITE_MATRIX m_exp
;
1759 memset(&m
, 0, sizeof(m
));
1761 /* zero offset gives same result as a call from IDWriteTextAnalyzer1 */
1762 hr
= IDWriteTextAnalyzer2_GetGlyphOrientationTransform(analyzer2
, ot_tests
[i
].angle
,
1763 ot_tests
[i
].is_sideways
, 0.0, 0.0, &m
);
1764 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
1765 ok(!memcmp(&ot_tests
[i
].m
, &m
, sizeof(m
)), "%d: wrong matrix %s\n", i
, dbgstr_matrix(&m
));
1767 m_exp
= ot_tests
[i
].m
;
1768 hr
= IDWriteTextAnalyzer2_GetGlyphOrientationTransform(analyzer2
, ot_tests
[i
].angle
,
1769 ot_tests
[i
].is_sideways
, originx
, originy
, &m
);
1770 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
1772 /* 90 degrees more for sideways */
1773 if (ot_tests
[i
].is_sideways
) {
1774 switch (ot_tests
[i
].angle
)
1776 case DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES
:
1777 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES
;
1779 case DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES
:
1780 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES
;
1782 case DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES
:
1783 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES
;
1785 case DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES
:
1786 angle
= DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES
;
1793 angle
= ot_tests
[i
].angle
;
1795 /* set expected offsets */
1798 case DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES
:
1800 case DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES
:
1801 m_exp
.dx
= originx
+ originy
;
1802 m_exp
.dy
= originy
- originx
;
1804 case DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES
:
1805 m_exp
.dx
= originx
+ originx
;
1806 m_exp
.dy
= originy
+ originy
;
1808 case DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES
:
1809 m_exp
.dx
= originx
- originy
;
1810 m_exp
.dy
= originy
+ originx
;
1816 ok(!memcmp(&m_exp
, &m
, sizeof(m
)), "%d: wrong matrix %s\n", i
, dbgstr_matrix(&m
));
1819 IDWriteTextAnalyzer2_Release(analyzer2
);
1822 START_TEST(analyzer
)
1826 hr
= DWriteCreateFactory(DWRITE_FACTORY_TYPE_ISOLATED
, &IID_IDWriteFactory
, (IUnknown
**)&factory
);
1827 ok(hr
== S_OK
, "got 0x%08x\n", hr
);
1830 win_skip("failed to create factory\n");
1834 init_call_sequences(sequences
, NUM_CALL_SEQUENCES
);
1835 init_call_sequences(expected_seq
, 1);
1837 test_AnalyzeScript();
1838 test_AnalyzeLineBreakpoints();
1839 test_GetScriptProperties();
1840 test_GetTextComplexity();
1842 test_numbersubstitution();
1843 test_GetTypographicFeatures();
1844 test_GetGlyphPlacements();
1845 test_ApplyCharacterSpacing();
1846 test_GetGlyphOrientationTransform();
1848 IDWriteFactory_Release(factory
);