reg/tests: Test backslashes with whitespace variations and comma placement.
[wine.git] / dlls / dwrite / analyzer.c
blobede2e6dc0a2162a184bbf078745341df4853a3c3
1 /*
2 * Text analyzer
4 * Copyright 2011 Aric Stewart for CodeWeavers
5 * Copyright 2012, 2014 Nikolay Sivov for CodeWeavers
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #define COBJMACROS
24 #include <math.h>
26 #include "dwrite_private.h"
27 #include "scripts.h"
29 WINE_DEFAULT_DEBUG_CHANNEL(dwrite);
31 extern const unsigned short wine_linebreak_table[] DECLSPEC_HIDDEN;
32 extern const unsigned short wine_scripts_table[] DECLSPEC_HIDDEN;
34 /* Number of characters needed for LOCALE_SNATIVEDIGITS */
35 #define NATIVE_DIGITS_LEN 11
37 struct dwritescript_properties {
38 DWRITE_SCRIPT_PROPERTIES props;
39 UINT32 scripttag; /* OpenType script tag */
40 UINT32 scriptalttag; /* Version 2 tag, 0 if not defined */
41 BOOL is_complex;
42 const struct scriptshaping_ops *ops;
45 #define _OT(a,b,c,d) DWRITE_MAKE_OPENTYPE_TAG(a,b,c,d)
47 /* NOTE: keep this array synced with script ids from scripts.h */
48 static const struct dwritescript_properties dwritescripts_properties[Script_LastId+1] = {
49 { /* Zzzz */ { 0x7a7a7a5a, 999, 15, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
50 { /* Zyyy */ { 0x7979795a, 998, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
51 { /* Zinh */ { 0x686e695a, 994, 15, 0x0020, 1, 0, 0, 0, 0, 0, 0 } },
52 { /* Arab */ { 0x62617241, 160, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, _OT('a','r','a','b'), 0, TRUE },
53 { /* Armn */ { 0x6e6d7241, 230, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('a','r','m','n') },
54 { /* Avst */ { 0x74737641, 134, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('a','v','s','t') },
55 { /* Bali */ { 0x696c6142, 360, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('b','a','l','i') },
56 { /* Bamu */ { 0x756d6142, 435, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('b','a','m','u') },
57 { /* Batk */ { 0x6b746142, 365, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','a','t','k') },
58 { /* Beng */ { 0x676e6542, 325, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('b','e','n','g'), _OT('b','n','g','2'), TRUE },
59 { /* Bopo */ { 0x6f706f42, 285, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('b','o','p','o') },
60 { /* Brah */ { 0x68617242, 300, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','r','a','h') },
61 { /* Brai */ { 0x69617242, 570, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','r','a','i'), 0, TRUE },
62 { /* Bugi */ { 0x69677542, 367, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','u','g','i') },
63 { /* Buhd */ { 0x64687542, 372, 8, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('b','u','h','d') },
64 { /* Cans */ { 0x736e6143, 440, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('c','a','n','s'), 0, TRUE },
65 { /* Cari */ { 0x69726143, 201, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('c','a','r','i') },
66 { /* Cham */ { 0x6d616843, 358, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('c','h','a','m') },
67 { /* Cher */ { 0x72656843, 445, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('c','h','e','r'), 0, TRUE },
68 { /* Copt */ { 0x74706f43, 204, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('c','o','p','t') },
69 { /* Xsux */ { 0x78757358, 20, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('x','s','u','x') },
70 { /* Cprt */ { 0x74727043, 403, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('c','p','r','t') },
71 { /* Cyrl */ { 0x6c727943, 220, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('c','y','r','l') },
72 { /* Dsrt */ { 0x74727344, 250, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('d','s','r','t'), 0, TRUE },
73 { /* Deva */ { 0x61766544, 315, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('d','e','v','a'), _OT('d','e','v','2'), TRUE },
74 { /* Egyp */ { 0x70796745, 50, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('e','g','y','p') },
75 { /* Ethi */ { 0x69687445, 430, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('e','t','h','i'), 0, TRUE },
76 { /* Geor */ { 0x726f6547, 240, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('g','e','o','r') },
77 { /* Glag */ { 0x67616c47, 225, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('g','l','a','g') },
78 { /* Goth */ { 0x68746f47, 206, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('g','o','t','h') },
79 { /* Grek */ { 0x6b657247, 200, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('g','r','e','k') },
80 { /* Gujr */ { 0x726a7547, 320, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('g','u','j','r'), _OT('g','j','r','2'), TRUE },
81 { /* Guru */ { 0x75727547, 310, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('g','u','r','u'), _OT('g','u','r','2'), TRUE },
82 { /* Hani */ { 0x696e6148, 500, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('h','a','n','i') },
83 { /* Hang */ { 0x676e6148, 286, 8, 0x0020, 1, 1, 1, 1, 0, 0, 0 }, _OT('h','a','n','g'), 0, TRUE },
84 { /* Hano */ { 0x6f6e6148, 371, 8, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('h','a','n','o') },
85 { /* Hebr */ { 0x72626548, 125, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('h','e','b','r'), 0, TRUE },
86 { /* Hira */ { 0x61726948, 410, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('k','a','n','a') },
87 { /* Armi */ { 0x696d7241, 124, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('a','r','m','i') },
88 { /* Phli */ { 0x696c6850, 131, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('p','h','l','i') },
89 { /* Prti */ { 0x69747250, 130, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('p','r','t','i') },
90 { /* Java */ { 0x6176614a, 361, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('j','a','v','a') },
91 { /* Kthi */ { 0x6968744b, 317, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('k','t','h','i') },
92 { /* Knda */ { 0x61646e4b, 345, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('k','n','d','a'), _OT('k','n','d','2'), TRUE },
93 { /* Kana */ { 0x616e614b, 411, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('k','a','n','a') },
94 { /* Kali */ { 0x696c614b, 357, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('k','a','l','i') },
95 { /* Khar */ { 0x7261684b, 305, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('k','h','a','r') },
96 { /* Khmr */ { 0x726d684b, 355, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, _OT('k','h','m','r'), 0, TRUE },
97 { /* Laoo */ { 0x6f6f614c, 356, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, _OT('l','a','o',' '), 0, TRUE },
98 { /* Latn */ { 0x6e74614c, 215, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('l','a','t','n'), 0, FALSE, &latn_shaping_ops },
99 { /* Lepc */ { 0x6370654c, 335, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('l','e','p','c') },
100 { /* Limb */ { 0x626d694c, 336, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('l','i','m','b') },
101 { /* Linb */ { 0x626e694c, 401, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('l','i','n','b') },
102 { /* Lisu */ { 0x7573694c, 399, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('l','i','s','u') },
103 { /* Lyci */ { 0x6963794c, 202, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('l','y','c','i') },
104 { /* Lydi */ { 0x6964794c, 116, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('l','y','d','i') },
105 { /* Mlym */ { 0x6d796c4d, 347, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('m','l','y','m'), _OT('m','l','m','2'), TRUE },
106 { /* Mand */ { 0x646e614d, 140, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, _OT('m','a','n','d') },
107 { /* Mtei */ { 0x6965744d, 337, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('m','t','e','i') },
108 { /* Mong */ { 0x676e6f4d, 145, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, _OT('m','o','n','g'), 0, TRUE },
109 { /* Mymr */ { 0x726d794d, 350, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('m','y','m','r'), 0, TRUE },
110 { /* Talu */ { 0x756c6154, 354, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','a','l','u'), 0, TRUE },
111 { /* Nkoo */ { 0x6f6f6b4e, 165, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, _OT('n','k','o',' '), 0, TRUE },
112 { /* Ogam */ { 0x6d61674f, 212, 1, 0x1680, 0, 1, 0, 0, 0, 1, 0 }, _OT('o','g','a','m'), 0, TRUE },
113 { /* Olck */ { 0x6b636c4f, 261, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('o','l','c','k') },
114 { /* Ital */ { 0x6c617449, 210, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('i','t','a','l') },
115 { /* Xpeo */ { 0x6f657058, 30, 1, 0x0020, 0, 1, 1, 1, 0, 0, 0 }, _OT('x','p','e','o'), 0, TRUE },
116 { /* Sarb */ { 0x62726153, 105, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('s','a','r','b') },
117 { /* Orkh */ { 0x686b724f, 175, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('o','r','k','h') },
118 { /* Orya */ { 0x6179724f, 327, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('o','r','y','a'), _OT('o','r','y','2'), TRUE },
119 { /* Osma */ { 0x616d734f, 260, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('o','s','m','a'), 0, TRUE },
120 { /* Phag */ { 0x67616850, 331, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, _OT('p','h','a','g'), 0, TRUE },
121 { /* Phnx */ { 0x786e6850, 115, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('p','h','n','x') },
122 { /* Rjng */ { 0x676e6a52, 363, 8, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('r','j','n','g') },
123 { /* Runr */ { 0x726e7552, 211, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, _OT('r','u','n','r'), 0, TRUE },
124 { /* Samr */ { 0x726d6153, 123, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('s','a','m','r') },
125 { /* Saur */ { 0x72756153, 344, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','a','u','r') },
126 { /* Shaw */ { 0x77616853, 281, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('s','h','a','w') },
127 { /* Sinh */ { 0x686e6953, 348, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','i','n','h'), 0, TRUE },
128 { /* Sund */ { 0x646e7553, 362, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','u','n','d') },
129 { /* Sylo */ { 0x6f6c7953, 316, 8, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('s','y','l','o') },
130 { /* Syrc */ { 0x63727953, 135, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, _OT('s','y','r','c'), 0, TRUE },
131 { /* Tglg */ { 0x676c6754, 370, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('t','g','l','g') },
132 { /* Tagb */ { 0x62676154, 373, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('t','a','g','b') },
133 { /* Tale */ { 0x656c6154, 353, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('t','a','l','e'), 0, TRUE },
134 { /* Lana */ { 0x616e614c, 351, 8, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('l','a','n','a') },
135 { /* Tavt */ { 0x74766154, 359, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, _OT('t','a','v','t') },
136 { /* Taml */ { 0x6c6d6154, 346, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','a','m','l'), _OT('t','m','l','2'), TRUE },
137 { /* Telu */ { 0x756c6554, 340, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','e','l','u'), _OT('t','e','l','2'), TRUE },
138 { /* Thaa */ { 0x61616854, 170, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','h','a','a'), 0, TRUE },
139 { /* Thai */ { 0x69616854, 352, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, _OT('t','h','a','i'), 0, TRUE },
140 { /* Tibt */ { 0x74626954, 330, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','i','b','t'), 0, TRUE },
141 { /* Tfng */ { 0x676e6654, 120, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','f','n','g'), 0, TRUE },
142 { /* Ugar */ { 0x72616755, 40, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('u','g','a','r') },
143 { /* Vaii */ { 0x69696156, 470, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('v','a','i',' '), 0, TRUE },
144 { /* Yiii */ { 0x69696959, 460, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('y','i',' ',' '), 0, TRUE },
145 { /* Cakm */ { 0x6d6b6143, 349, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('c','a','k','m') },
146 { /* Merc */ { 0x6372654d, 101, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('m','e','r','c') },
147 { /* Mero */ { 0x6f72654d, 100, 1, 0x0020, 0, 1, 1, 1, 0, 0, 0 }, _OT('m','e','r','o') },
148 { /* Plrd */ { 0x64726c50, 282, 8, 0x0020, 1, 0, 1, 0, 0, 0, 0 }, _OT('p','l','r','d') },
149 { /* Shrd */ { 0x64726853, 319, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','h','r','d') },
150 { /* Sora */ { 0x61726f53, 398, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','o','r','a') },
151 { /* Takr */ { 0x726b6154, 321, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('t','a','k','r') },
152 { /* Bass */ { 0x73736142, 259, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','a','s','s') },
153 { /* Aghb */ { 0x62686741, 239, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('a','g','h','b') },
154 { /* Dupl */ { 0x6c707544, 755, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, _OT('d','u','p','l') },
155 { /* Elba */ { 0x61626c45, 226, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('e','l','b','a') },
156 { /* Gran */ { 0x6e617247, 343, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('g','r','a','n') },
157 { /* Khoj */ { 0x6a6f684b, 322, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('k','h','o','j') },
158 { /* Sind */ { 0x646e6953, 318, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('s','i','n','d') },
159 { /* Lina */ { 0x616e694c, 400, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, _OT('l','i','n','a') },
160 { /* Mahj */ { 0x6a68614d, 314, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('m','a','h','j') },
161 { /* Mani */ { 0x696e614d, 139, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, _OT('m','a','n','i') },
162 { /* Mend */ { 0x646e654d, 438, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('m','e','n','d') },
163 { /* Modi */ { 0x69646f4d, 324, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('m','o','d','i') },
164 { /* Mroo */ { 0x6f6f724d, 199, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('m','r','o','o') },
165 { /* Nbat */ { 0x7461624e, 159, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('n','b','a','t') },
166 { /* Narb */ { 0x6272614e, 106, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('n','a','r','b') },
167 { /* Perm */ { 0x6d726550, 227, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('p','e','r','m') },
168 { /* Hmng */ { 0x676e6d48, 450, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('h','m','n','g') },
169 { /* Palm */ { 0x6d6c6150, 126, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, _OT('p','a','l','m') },
170 { /* Pauc */ { 0x63756150, 263, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('p','a','u','c') },
171 { /* Phlp */ { 0x706c6850, 132, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, _OT('p','h','l','p') },
172 { /* Sidd */ { 0x64646953, 302, 8, 0x0020, 1, 0, 1, 1, 0, 0, 0 }, _OT('s','i','d','d') },
173 { /* Tirh */ { 0x68726954, 326, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, _OT('t','i','r','h') },
174 { /* Wara */ { 0x61726157, 262, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('w','a','r','a') },
175 { /* Adlm */ { 0x6d6c6441, 166, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('a','d','l','m') },
176 { /* Ahom */ { 0x6d6f6841, 338, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('a','h','o','m') },
177 { /* Hluw */ { 0x77756c48, 80, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('h','l','u','w') },
178 { /* Bhks */ { 0x736b6842, 334, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('b','h','k','s') },
179 { /* Hatr */ { 0x72746148, 127, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('h','a','t','r') },
180 { /* Marc */ { 0x6372614d, 332, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('m','a','r','c') },
181 { /* Mult */ { 0x746c754d, 323, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('m','u','l','t') },
182 { /* Newa */ { 0x6177654e, 333, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('n','e','w','a') },
183 { /* Hung */ { 0x676e7548, 176, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('h','u','n','g') },
184 { /* Osge */ { 0x6567734f, 219, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('o','s','g','e') },
185 { /* Sgnw */ { 0x776e6753, 95, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('s','g','n','w') },
186 { /* Tang */ { 0x676e6154, 520, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, _OT('t','a','n','g') },
188 #undef _OT
190 const char *debugstr_sa_script(UINT16 script)
192 return script < Script_LastId ? debugstr_an((char*)&dwritescripts_properties[script].props.isoScriptCode, 4): "not defined";
195 /* system font falback configuration */
196 static const WCHAR meiryoW[] = {'M','e','i','r','y','o',0};
198 struct fallback_mapping {
199 DWRITE_UNICODE_RANGE range;
200 const WCHAR *family;
203 static const struct fallback_mapping fontfallback_neutral_data[] = {
204 { { 0x3000, 0x30ff }, meiryoW }, /* CJK Symbols and Punctuation, Hiragana, Katakana */
205 { { 0x31f0, 0x31ff }, meiryoW }, /* Katakana Phonetic Extensions */
206 { { 0x4e00, 0x9fff }, meiryoW }, /* CJK Unified Ideographs */
209 struct dwrite_fontfallback {
210 IDWriteFontFallback IDWriteFontFallback_iface;
211 IDWriteFactory5 *factory;
212 IDWriteFontCollection1 *systemcollection;
213 const struct fallback_mapping *mappings;
214 UINT32 count;
217 struct dwrite_numbersubstitution {
218 IDWriteNumberSubstitution IDWriteNumberSubstitution_iface;
219 LONG ref;
221 DWRITE_NUMBER_SUBSTITUTION_METHOD method;
222 WCHAR *locale;
223 BOOL ignore_user_override;
226 static inline struct dwrite_numbersubstitution *impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution *iface)
228 return CONTAINING_RECORD(iface, struct dwrite_numbersubstitution, IDWriteNumberSubstitution_iface);
231 static struct dwrite_numbersubstitution *unsafe_impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution *iface);
233 static inline struct dwrite_fontfallback *impl_from_IDWriteFontFallback(IDWriteFontFallback *iface)
235 return CONTAINING_RECORD(iface, struct dwrite_fontfallback, IDWriteFontFallback_iface);
238 static inline UINT32 decode_surrogate_pair(const WCHAR *str, UINT32 index, UINT32 end)
240 if (index < end-1 && IS_SURROGATE_PAIR(str[index], str[index+1])) {
241 UINT32 ch = 0x10000 + ((str[index] - 0xd800) << 10) + (str[index+1] - 0xdc00);
242 TRACE("surrogate pair (%x %x) => %x\n", str[index], str[index+1], ch);
243 return ch;
245 return 0;
248 static inline UINT16 get_char_script(WCHAR c)
250 UINT16 script = get_table_entry(wine_scripts_table, c);
251 return script == Script_Inherited ? Script_Unknown : script;
254 static DWRITE_SCRIPT_ANALYSIS get_char_sa(WCHAR c)
256 DWRITE_SCRIPT_ANALYSIS sa;
258 sa.script = get_char_script(c);
259 sa.shapes = iscntrlW(c) || c == 0x2028 /* LINE SEPARATOR */ || c == 0x2029 /* PARAGRAPH SEPARATOR */ ?
260 DWRITE_SCRIPT_SHAPES_NO_VISUAL : DWRITE_SCRIPT_SHAPES_DEFAULT;
261 return sa;
264 static HRESULT analyze_script(const WCHAR *text, UINT32 position, UINT32 length, IDWriteTextAnalysisSink *sink)
266 DWRITE_SCRIPT_ANALYSIS sa;
267 UINT32 pos, i, seq_length;
269 if (!length)
270 return S_OK;
272 sa = get_char_sa(*text);
274 pos = position;
275 seq_length = 1;
277 for (i = 1; i < length; i++)
279 DWRITE_SCRIPT_ANALYSIS cur_sa = get_char_sa(text[i]);
281 /* Unknown type is ignored when preceded or followed by another script */
282 switch (sa.script) {
283 case Script_Unknown:
284 sa.script = cur_sa.script;
285 break;
286 case Script_Common:
287 if (cur_sa.script == Script_Unknown)
288 cur_sa.script = sa.script;
289 else if ((cur_sa.script != Script_Common) && sa.shapes == DWRITE_SCRIPT_SHAPES_DEFAULT)
290 sa.script = cur_sa.script;
291 break;
292 default:
293 if ((cur_sa.script == Script_Common && cur_sa.shapes == DWRITE_SCRIPT_SHAPES_DEFAULT) || cur_sa.script == Script_Unknown)
294 cur_sa.script = sa.script;
297 /* this is a length of a sequence to be reported next */
298 if (sa.script == cur_sa.script && sa.shapes == cur_sa.shapes)
299 seq_length++;
300 else {
301 HRESULT hr;
303 hr = IDWriteTextAnalysisSink_SetScriptAnalysis(sink, pos, seq_length, &sa);
304 if (FAILED(hr)) return hr;
305 pos = position + i;
306 seq_length = 1;
307 sa = cur_sa;
311 /* one char length case or normal completion call */
312 return IDWriteTextAnalysisSink_SetScriptAnalysis(sink, pos, seq_length, &sa);
315 struct linebreaking_state {
316 DWRITE_LINE_BREAKPOINT *breakpoints;
317 UINT32 count;
320 enum BreakConditionLocation {
321 BreakConditionBefore,
322 BreakConditionAfter
325 enum linebreaking_classes {
326 b_BK = 1,
327 b_CR,
328 b_LF,
329 b_CM,
330 b_SG,
331 b_GL,
332 b_CB,
333 b_SP,
334 b_ZW,
335 b_NL,
336 b_WJ,
337 b_JL,
338 b_JV,
339 b_JT,
340 b_H2,
341 b_H3,
342 b_XX,
343 b_OP,
344 b_CL,
345 b_CP,
346 b_QU,
347 b_NS,
348 b_EX,
349 b_SY,
350 b_IS,
351 b_PR,
352 b_PO,
353 b_NU,
354 b_AL,
355 b_ID,
356 b_IN,
357 b_HY,
358 b_BB,
359 b_BA,
360 b_SA,
361 b_AI,
362 b_B2,
363 b_HL,
364 b_CJ,
365 b_RI,
366 b_EB,
367 b_EM,
368 b_ZWJ,
371 static BOOL has_strong_condition(DWRITE_BREAK_CONDITION old_condition, DWRITE_BREAK_CONDITION new_condition)
373 if (old_condition == DWRITE_BREAK_CONDITION_MAY_NOT_BREAK || old_condition == DWRITE_BREAK_CONDITION_MUST_BREAK)
374 return TRUE;
376 if (old_condition == DWRITE_BREAK_CONDITION_CAN_BREAK && new_condition != DWRITE_BREAK_CONDITION_MUST_BREAK)
377 return TRUE;
379 return FALSE;
382 /* "Can break" is a weak condition, stronger "may not break" and "must break" override it. Initially all conditions are
383 set to "can break" and could only be changed once. */
384 static inline void set_break_condition(UINT32 pos, enum BreakConditionLocation location, DWRITE_BREAK_CONDITION condition,
385 struct linebreaking_state *state)
387 if (location == BreakConditionBefore) {
388 if (has_strong_condition(state->breakpoints[pos].breakConditionBefore, condition))
389 return;
390 state->breakpoints[pos].breakConditionBefore = condition;
391 if (pos > 0)
392 state->breakpoints[pos-1].breakConditionAfter = condition;
394 else {
395 if (has_strong_condition(state->breakpoints[pos].breakConditionAfter, condition))
396 return;
397 state->breakpoints[pos].breakConditionAfter = condition;
398 if (pos + 1 < state->count)
399 state->breakpoints[pos+1].breakConditionBefore = condition;
403 BOOL lb_is_newline_char(WCHAR ch)
405 short c = get_table_entry(wine_linebreak_table, ch);
406 return c == b_LF || c == b_NL || c == b_CR || c == b_BK;
409 static HRESULT analyze_linebreaks(const WCHAR *text, UINT32 count, DWRITE_LINE_BREAKPOINT *breakpoints)
411 struct linebreaking_state state;
412 short *break_class;
413 int i, j;
415 break_class = heap_alloc(count*sizeof(short));
416 if (!break_class)
417 return E_OUTOFMEMORY;
419 state.breakpoints = breakpoints;
420 state.count = count;
422 for (i = 0; i < count; i++)
424 break_class[i] = get_table_entry(wine_linebreak_table, text[i]);
426 breakpoints[i].breakConditionBefore = DWRITE_BREAK_CONDITION_NEUTRAL;
427 breakpoints[i].breakConditionAfter = DWRITE_BREAK_CONDITION_NEUTRAL;
428 breakpoints[i].isWhitespace = !!isspaceW(text[i]);
429 breakpoints[i].isSoftHyphen = text[i] == 0x00ad /* Unicode Soft Hyphen */;
430 breakpoints[i].padding = 0;
432 /* LB1 - resolve some classes. TODO: use external algorithms for these classes. */
433 switch (break_class[i])
435 case b_AI:
436 case b_SA:
437 case b_SG:
438 case b_XX:
439 break_class[i] = b_AL;
440 break;
441 case b_CJ:
442 break_class[i] = b_NS;
443 break;
447 /* LB2 - never break at the start */
448 set_break_condition(0, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
449 /* LB3 - always break at the end. */
450 set_break_condition(count - 1, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
452 /* LB4 - LB6 - mandatory breaks. */
453 for (i = 0; i < count; i++)
455 switch (break_class[i])
457 /* LB4 - LB6 */
458 case b_CR:
459 /* LB5 - don't break CR x LF */
460 if (i < count-1 && break_class[i+1] == b_LF)
462 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
463 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
464 break;
466 case b_LF:
467 case b_NL:
468 case b_BK:
469 /* LB4 - LB5 - always break after hard breaks */
470 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MUST_BREAK, &state);
471 /* LB6 - do not break before hard breaks */
472 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
473 break;
477 /* LB7 - LB8 - explicit breaks and non-breaks */
478 for (i = 0; i < count; i++)
480 switch (break_class[i])
482 /* LB7 - do not break before spaces */
483 case b_SP:
484 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
485 break;
486 case b_ZW:
487 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
489 /* LB8 - break before character after zero-width space, skip spaces in-between */
490 j = i;
491 while (j < count-1 && break_class[j+1] == b_SP)
492 j++;
493 if (j < count-1 && break_class[j+1] != b_ZW)
494 set_break_condition(j, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
495 break;
496 /* LB8a - do not break between ZWJ and an ideograph, emoji base or emoji modifier */
497 case b_ZWJ:
498 if (i < count-1 && (break_class[i+1] == b_ID || break_class[i+1] == b_EB || break_class[i+1] == b_EM))
499 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
500 break;
504 /* LB9 - LB10 - combining marks */
505 for (i = 0; i < count; i++)
507 if (break_class[i] == b_CM || break_class[i] == b_ZWJ)
509 if (i > 0)
511 switch (break_class[i-1])
513 case b_SP:
514 case b_BK:
515 case b_CR:
516 case b_LF:
517 case b_NL:
518 case b_ZW:
519 break_class[i] = b_AL;
520 break;
521 default:
523 break_class[i] = break_class[i-1];
524 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
528 else break_class[i] = b_AL;
532 for (i = 0; i < count; i++)
534 switch (break_class[i])
536 /* LB11 - don't break before and after word joiner */
537 case b_WJ:
538 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
539 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
540 break;
541 /* LB12 - don't break after glue */
542 case b_GL:
543 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
544 /* LB12a */
545 if (i > 0)
547 if (break_class[i-1] != b_SP && break_class[i-1] != b_BA && break_class[i-1] != b_HY)
548 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
550 break;
551 /* LB13 */
552 case b_CL:
553 case b_CP:
554 case b_EX:
555 case b_IS:
556 case b_SY:
557 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
558 break;
559 /* LB14 - do not break after OP, even after spaces */
560 case b_OP:
561 j = i;
562 while (j < count-1 && break_class[j+1] == b_SP)
563 j++;
564 set_break_condition(j, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
565 break;
566 /* LB15 - do not break within QU-OP, even with intervening spaces */
567 case b_QU:
568 j = i;
569 while (j < count-1 && break_class[j+1] == b_SP)
570 j++;
571 if (j < count - 1 && break_class[j+1] == b_OP)
572 set_break_condition(j, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
573 break;
574 /* LB16 */
575 case b_NS:
576 j = i-1;
577 while(j > 0 && break_class[j] == b_SP)
578 j--;
579 if (break_class[j] == b_CL || break_class[j] == b_CP)
580 for (j++; j <= i; j++)
581 set_break_condition(j, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
582 break;
583 /* LB17 - do not break within B2, even with intervening spaces */
584 case b_B2:
585 j = i;
586 while (j < count && break_class[j+1] == b_SP)
587 j++;
588 if (j < count - 1 && break_class[j+1] == b_B2)
589 set_break_condition(j, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
590 break;
594 for (i = 0; i < count; i++)
596 switch(break_class[i])
598 /* LB18 - break is allowed after space */
599 case b_SP:
600 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
601 break;
602 /* LB19 - don't break before or after quotation mark */
603 case b_QU:
604 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
605 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
606 break;
607 /* LB20 */
608 case b_CB:
609 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
610 if (i < count - 1 && break_class[i+1] != b_QU)
611 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
612 break;
613 /* LB21 */
614 case b_BA:
615 case b_HY:
616 case b_NS:
617 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
618 break;
619 case b_BB:
620 if (i < count - 1 && break_class[i+1] != b_CB)
621 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
622 break;
623 /* LB21a, LB21b */
624 case b_HL:
625 /* LB21a */
626 if (i < count-1)
627 switch (break_class[i+1])
629 case b_HY:
630 case b_BA:
631 set_break_condition(i+1, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
633 /* LB21b */
634 if (i > 0 && break_class[i-1] == b_SY)
635 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
636 break;
637 /* LB22 */
638 case b_IN:
639 if (i > 0)
641 switch (break_class[i-1])
643 case b_AL:
644 case b_HL:
645 case b_EX:
646 case b_ID:
647 case b_EB:
648 case b_EM:
649 case b_IN:
650 case b_NU:
651 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
654 break;
657 if (i < count-1)
659 /* LB23 - do not break between digits and letters */
660 if ((break_class[i] == b_AL && break_class[i+1] == b_NU) ||
661 (break_class[i] == b_HL && break_class[i+1] == b_NU) ||
662 (break_class[i] == b_NU && break_class[i+1] == b_AL) ||
663 (break_class[i] == b_NU && break_class[i+1] == b_HL))
664 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
666 /* LB23a - do not break between numeric prefixes and ideographs, or between ideographs and numeric postfixes */
667 if ((break_class[i] == b_PR && break_class[i+1] == b_ID) ||
668 (break_class[i] == b_PR && break_class[i+1] == b_EB) ||
669 (break_class[i] == b_PR && break_class[i+1] == b_EM) ||
670 (break_class[i] == b_ID && break_class[i+1] == b_PO) ||
671 (break_class[i] == b_EM && break_class[i+1] == b_PO) ||
672 (break_class[i] == b_EB && break_class[i+1] == b_PO))
673 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
675 /* LB24 - do not break between numeric prefix/postfix and letters, or letters and prefix/postfix */
676 if ((break_class[i] == b_PR && break_class[i+1] == b_AL) ||
677 (break_class[i] == b_PR && break_class[i+1] == b_HL) ||
678 (break_class[i] == b_PO && break_class[i+1] == b_AL) ||
679 (break_class[i] == b_PO && break_class[i+1] == b_HL) ||
680 (break_class[i] == b_AL && break_class[i+1] == b_PR) ||
681 (break_class[i] == b_HL && break_class[i+1] == b_PR) ||
682 (break_class[i] == b_AL && break_class[i+1] == b_PO) ||
683 (break_class[i] == b_HL && break_class[i+1] == b_PO))
684 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
686 /* LB25 */
687 if ((break_class[i] == b_CL && break_class[i+1] == b_PO) ||
688 (break_class[i] == b_CP && break_class[i+1] == b_PO) ||
689 (break_class[i] == b_CL && break_class[i+1] == b_PR) ||
690 (break_class[i] == b_CP && break_class[i+1] == b_PR) ||
691 (break_class[i] == b_NU && break_class[i+1] == b_PO) ||
692 (break_class[i] == b_NU && break_class[i+1] == b_PR) ||
693 (break_class[i] == b_PO && break_class[i+1] == b_OP) ||
694 (break_class[i] == b_PO && break_class[i+1] == b_NU) ||
695 (break_class[i] == b_PR && break_class[i+1] == b_OP) ||
696 (break_class[i] == b_PR && break_class[i+1] == b_NU) ||
697 (break_class[i] == b_HY && break_class[i+1] == b_NU) ||
698 (break_class[i] == b_IS && break_class[i+1] == b_NU) ||
699 (break_class[i] == b_NU && break_class[i+1] == b_NU) ||
700 (break_class[i] == b_SY && break_class[i+1] == b_NU))
701 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
703 /* LB26 */
704 if (break_class[i] == b_JL)
706 switch (break_class[i+1])
708 case b_JL:
709 case b_JV:
710 case b_H2:
711 case b_H3:
712 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
715 if ((break_class[i] == b_JV || break_class[i] == b_H2) &&
716 (break_class[i+1] == b_JV || break_class[i+1] == b_JT))
717 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
718 if ((break_class[i] == b_JT || break_class[i] == b_H3) &&
719 break_class[i+1] == b_JT)
720 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
722 /* LB27 */
723 switch (break_class[i])
725 case b_JL:
726 case b_JV:
727 case b_JT:
728 case b_H2:
729 case b_H3:
730 if (break_class[i+1] == b_IN || break_class[i+1] == b_PO)
731 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
733 if (break_class[i] == b_PR)
735 switch (break_class[i+1])
737 case b_JL:
738 case b_JV:
739 case b_JT:
740 case b_H2:
741 case b_H3:
742 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
746 /* LB28 */
747 if ((break_class[i] == b_AL && break_class[i+1] == b_AL) ||
748 (break_class[i] == b_AL && break_class[i+1] == b_HL) ||
749 (break_class[i] == b_HL && break_class[i+1] == b_AL) ||
750 (break_class[i] == b_HL && break_class[i+1] == b_HL))
751 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
753 /* LB29 */
754 if ((break_class[i] == b_IS && break_class[i+1] == b_AL) ||
755 (break_class[i] == b_IS && break_class[i+1] == b_HL))
756 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
758 /* LB30 */
759 if ((break_class[i] == b_AL || break_class[i] == b_HL || break_class[i] == b_NU) &&
760 break_class[i+1] == b_OP)
761 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
762 if (break_class[i] == b_CP &&
763 (break_class[i+1] == b_AL || break_class[i+1] == b_HL || break_class[i+1] == b_NU))
764 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
766 /* LB30a - break between two RIs if and only if there are an even number of RIs preceding position of the break */
767 if (break_class[i] == b_RI && break_class[i+1] == b_RI) {
768 unsigned int c = 0;
770 j = i + 1;
771 while (j > 0 && break_class[--j] == b_RI)
772 c++;
774 if ((c & 1) == 0)
775 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
778 /* LB30b - do not break between an emoji base and an emoji modifier */
779 if (break_class[i] == b_EB && break_class[i+1] == b_EM)
780 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
784 /* LB31 - allow breaks everywhere else. */
785 for (i = 0; i < count; i++)
787 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
788 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
791 heap_free(break_class);
792 return S_OK;
795 static HRESULT WINAPI dwritetextanalyzer_QueryInterface(IDWriteTextAnalyzer2 *iface, REFIID riid, void **obj)
797 TRACE("(%s %p)\n", debugstr_guid(riid), obj);
799 if (IsEqualIID(riid, &IID_IDWriteTextAnalyzer2) ||
800 IsEqualIID(riid, &IID_IDWriteTextAnalyzer1) ||
801 IsEqualIID(riid, &IID_IDWriteTextAnalyzer) ||
802 IsEqualIID(riid, &IID_IUnknown))
804 *obj = iface;
805 return S_OK;
808 *obj = NULL;
809 return E_NOINTERFACE;
812 static ULONG WINAPI dwritetextanalyzer_AddRef(IDWriteTextAnalyzer2 *iface)
814 return 2;
817 static ULONG WINAPI dwritetextanalyzer_Release(IDWriteTextAnalyzer2 *iface)
819 return 1;
822 /* This helper tries to get 'length' chars from a source, allocating a buffer only if source failed to provide enough
823 data after a first request. */
824 static HRESULT get_text_source_ptr(IDWriteTextAnalysisSource *source, UINT32 position, UINT32 length, const WCHAR **text, WCHAR **buff)
826 HRESULT hr;
827 UINT32 len;
829 *buff = NULL;
830 *text = NULL;
831 len = 0;
832 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, position, text, &len);
833 if (FAILED(hr)) return hr;
835 if (len < length) {
836 UINT32 read;
838 *buff = heap_alloc(length*sizeof(WCHAR));
839 if (!*buff)
840 return E_OUTOFMEMORY;
841 memcpy(*buff, *text, len*sizeof(WCHAR));
842 read = len;
844 while (read < length && *text) {
845 *text = NULL;
846 len = 0;
847 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, read, text, &len);
848 if (FAILED(hr)) {
849 heap_free(*buff);
850 return hr;
852 memcpy(*buff + read, *text, min(len, length-read)*sizeof(WCHAR));
853 read += len;
856 *text = *buff;
859 return hr;
862 static HRESULT WINAPI dwritetextanalyzer_AnalyzeScript(IDWriteTextAnalyzer2 *iface,
863 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
865 WCHAR *buff = NULL;
866 const WCHAR *text;
867 HRESULT hr;
869 TRACE("(%p %u %u %p)\n", source, position, length, sink);
871 if (length == 0)
872 return S_OK;
874 hr = get_text_source_ptr(source, position, length, &text, &buff);
875 if (FAILED(hr))
876 return hr;
878 hr = analyze_script(text, position, length, sink);
879 heap_free(buff);
881 return hr;
884 static HRESULT WINAPI dwritetextanalyzer_AnalyzeBidi(IDWriteTextAnalyzer2 *iface,
885 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
887 UINT8 *levels = NULL, *explicit = NULL;
888 UINT8 baselevel, level, explicit_level;
889 UINT32 pos, i, seq_length;
890 WCHAR *buff = NULL;
891 const WCHAR *text;
892 HRESULT hr;
894 TRACE("(%p %u %u %p)\n", source, position, length, sink);
896 if (!length)
897 return S_OK;
899 hr = get_text_source_ptr(source, position, length, &text, &buff);
900 if (FAILED(hr))
901 return hr;
903 levels = heap_alloc(length*sizeof(*levels));
904 explicit = heap_alloc(length*sizeof(*explicit));
906 if (!levels || !explicit) {
907 hr = E_OUTOFMEMORY;
908 goto done;
911 baselevel = IDWriteTextAnalysisSource_GetParagraphReadingDirection(source);
912 hr = bidi_computelevels(text, length, baselevel, explicit, levels);
913 if (FAILED(hr))
914 goto done;
916 level = levels[0];
917 explicit_level = explicit[0];
918 pos = position;
919 seq_length = 1;
921 for (i = 1; i < length; i++) {
922 if (levels[i] == level && explicit[i] == explicit_level)
923 seq_length++;
924 else {
925 hr = IDWriteTextAnalysisSink_SetBidiLevel(sink, pos, seq_length, explicit_level, level);
926 if (FAILED(hr))
927 goto done;
929 pos += seq_length;
930 seq_length = 1;
931 level = levels[i];
932 explicit_level = explicit[i];
935 /* one char length case or normal completion call */
936 hr = IDWriteTextAnalysisSink_SetBidiLevel(sink, pos, seq_length, explicit_level, level);
938 done:
939 heap_free(explicit);
940 heap_free(levels);
941 heap_free(buff);
943 return hr;
946 static HRESULT WINAPI dwritetextanalyzer_AnalyzeNumberSubstitution(IDWriteTextAnalyzer2 *iface,
947 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
949 static int once;
951 if (!once++)
952 FIXME("(%p %u %u %p): stub\n", source, position, length, sink);
953 return S_OK;
956 static HRESULT WINAPI dwritetextanalyzer_AnalyzeLineBreakpoints(IDWriteTextAnalyzer2 *iface,
957 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
959 DWRITE_LINE_BREAKPOINT *breakpoints = NULL;
960 WCHAR *buff = NULL;
961 const WCHAR *text;
962 HRESULT hr;
963 UINT32 len;
965 TRACE("(%p %u %u %p)\n", source, position, length, sink);
967 if (length == 0)
968 return S_OK;
970 /* get some, check for length */
971 text = NULL;
972 len = 0;
973 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, position, &text, &len);
974 if (FAILED(hr)) return hr;
976 if (len < length) {
977 UINT32 read;
979 buff = heap_alloc(length*sizeof(WCHAR));
980 if (!buff)
981 return E_OUTOFMEMORY;
982 memcpy(buff, text, len*sizeof(WCHAR));
983 read = len;
985 while (read < length && text) {
986 text = NULL;
987 len = 0;
988 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, read, &text, &len);
989 if (FAILED(hr))
990 goto done;
991 memcpy(&buff[read], text, min(len, length-read)*sizeof(WCHAR));
992 read += len;
995 text = buff;
998 breakpoints = heap_alloc(length*sizeof(*breakpoints));
999 if (!breakpoints) {
1000 hr = E_OUTOFMEMORY;
1001 goto done;
1004 hr = analyze_linebreaks(text, length, breakpoints);
1005 if (FAILED(hr))
1006 goto done;
1008 hr = IDWriteTextAnalysisSink_SetLineBreakpoints(sink, position, length, breakpoints);
1010 done:
1011 heap_free(breakpoints);
1012 heap_free(buff);
1014 return hr;
1017 static UINT32 get_opentype_language(const WCHAR *locale)
1019 UINT32 language = DWRITE_FONT_FEATURE_TAG_DEFAULT;
1021 if (locale) {
1022 WCHAR tag[5];
1023 if (GetLocaleInfoEx(locale, LOCALE_SOPENTYPELANGUAGETAG, tag, sizeof(tag)/sizeof(WCHAR)))
1024 language = DWRITE_MAKE_OPENTYPE_TAG(tag[0],tag[1],tag[2],tag[3]);
1027 return language;
1030 static DWRITE_NUMBER_SUBSTITUTION_METHOD get_number_substitutes(IDWriteNumberSubstitution *substitution, WCHAR *digits)
1032 struct dwrite_numbersubstitution *numbersubst = unsafe_impl_from_IDWriteNumberSubstitution(substitution);
1033 DWRITE_NUMBER_SUBSTITUTION_METHOD method;
1034 WCHAR isolang[9];
1035 DWORD lctype;
1037 if (!numbersubst)
1038 return DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE;
1040 lctype = numbersubst->ignore_user_override ? LOCALE_NOUSEROVERRIDE : 0;
1042 if (numbersubst->method == DWRITE_NUMBER_SUBSTITUTION_METHOD_FROM_CULTURE) {
1043 DWORD value;
1045 method = DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE;
1046 if (GetLocaleInfoEx(numbersubst->locale, lctype | LOCALE_IDIGITSUBSTITUTION | LOCALE_RETURN_NUMBER, (WCHAR *)&value, 2)) {
1047 switch (value)
1049 case 0:
1050 method = DWRITE_NUMBER_SUBSTITUTION_METHOD_CONTEXTUAL;
1051 break;
1052 case 2:
1053 method = DWRITE_NUMBER_SUBSTITUTION_METHOD_NATIONAL;
1054 break;
1055 case 1:
1056 default:
1057 if (value != 1)
1058 WARN("Unknown IDIGITSUBSTITUTION value %u, locale %s.\n", value, debugstr_w(numbersubst->locale));
1061 else
1062 WARN("Failed to get IDIGITSUBSTITUTION for locale %s\n", debugstr_w(numbersubst->locale));
1064 else
1065 method = numbersubst->method;
1067 digits[0] = 0;
1068 switch (method)
1070 case DWRITE_NUMBER_SUBSTITUTION_METHOD_NATIONAL:
1071 GetLocaleInfoEx(numbersubst->locale, lctype | LOCALE_SNATIVEDIGITS, digits, NATIVE_DIGITS_LEN);
1072 break;
1073 case DWRITE_NUMBER_SUBSTITUTION_METHOD_CONTEXTUAL:
1074 case DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL:
1075 if (GetLocaleInfoEx(numbersubst->locale, LOCALE_SISO639LANGNAME, isolang, sizeof(isolang)/sizeof(isolang[0]))) {
1076 static const WCHAR arW[] = {'a','r',0};
1077 static const WCHAR arabicW[] = {0x640,0x641,0x642,0x643,0x644,0x645,0x646,0x647,0x648,0x649,0};
1079 /* For some Arabic locales Latin digits are returned for SNATIVEDIGITS */
1080 if (!strcmpW(arW, isolang)) {
1081 strcpyW(digits, arabicW);
1082 break;
1085 GetLocaleInfoEx(numbersubst->locale, lctype | LOCALE_SNATIVEDIGITS, digits, NATIVE_DIGITS_LEN);
1086 break;
1087 default:
1091 if (method != DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE && !*digits) {
1092 WARN("Failed to get number substitutes for locale %s, method %d\n", debugstr_w(numbersubst->locale), method);
1093 method = DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE;
1096 return method;
1099 static HRESULT WINAPI dwritetextanalyzer_GetGlyphs(IDWriteTextAnalyzer2 *iface,
1100 WCHAR const* text, UINT32 length, IDWriteFontFace* fontface, BOOL is_sideways,
1101 BOOL is_rtl, DWRITE_SCRIPT_ANALYSIS const* analysis, WCHAR const* locale,
1102 IDWriteNumberSubstitution* substitution, DWRITE_TYPOGRAPHIC_FEATURES const** features,
1103 UINT32 const* feature_range_len, UINT32 feature_ranges, UINT32 max_glyph_count,
1104 UINT16* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES* text_props, UINT16* glyph_indices,
1105 DWRITE_SHAPING_GLYPH_PROPERTIES* glyph_props, UINT32* actual_glyph_count)
1107 const struct dwritescript_properties *scriptprops;
1108 DWRITE_NUMBER_SUBSTITUTION_METHOD method;
1109 struct scriptshaping_context context;
1110 struct scriptshaping_cache *cache = NULL;
1111 BOOL update_cluster, need_vertical;
1112 WCHAR digits[NATIVE_DIGITS_LEN];
1113 IDWriteFontFace1 *fontface1;
1114 WCHAR *string;
1115 UINT32 i, g;
1116 HRESULT hr = S_OK;
1117 UINT16 script;
1119 TRACE("(%s:%u %p %d %d %s %s %p %p %p %u %u %p %p %p %p %p)\n", debugstr_wn(text, length),
1120 length, fontface, is_sideways, is_rtl, debugstr_sa_script(analysis->script), debugstr_w(locale), substitution,
1121 features, feature_range_len, feature_ranges, max_glyph_count, clustermap, text_props, glyph_indices,
1122 glyph_props, actual_glyph_count);
1124 script = analysis->script > Script_LastId ? Script_Unknown : analysis->script;
1126 if (max_glyph_count < length)
1127 return E_NOT_SUFFICIENT_BUFFER;
1129 string = heap_alloc(sizeof(WCHAR)*length);
1130 if (!string)
1131 return E_OUTOFMEMORY;
1133 method = get_number_substitutes(substitution, digits);
1135 for (i = 0; i < length; i++) {
1136 /* FIXME: set to better values */
1137 glyph_props[i].justification = text[i] == ' ' ? SCRIPT_JUSTIFY_BLANK : SCRIPT_JUSTIFY_CHARACTER;
1138 glyph_props[i].isClusterStart = 1;
1139 glyph_props[i].isDiacritic = 0;
1140 glyph_props[i].isZeroWidthSpace = 0;
1141 glyph_props[i].reserved = 0;
1143 /* FIXME: have the shaping engine set this */
1144 text_props[i].isShapedAlone = 0;
1145 text_props[i].reserved = 0;
1147 clustermap[i] = i;
1149 string[i] = text[i];
1150 switch (method)
1152 case DWRITE_NUMBER_SUBSTITUTION_METHOD_CONTEXTUAL:
1153 if (!is_rtl)
1154 break;
1155 /* fallthrough */
1156 default:
1157 if (string[i] >= '0' && string[i] <= '9')
1158 string[i] = digits[string[i] - '0'];
1159 break;
1160 case DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE:
1165 for (; i < max_glyph_count; i++) {
1166 glyph_props[i].justification = SCRIPT_JUSTIFY_NONE;
1167 glyph_props[i].isClusterStart = 0;
1168 glyph_props[i].isDiacritic = 0;
1169 glyph_props[i].isZeroWidthSpace = 0;
1170 glyph_props[i].reserved = 0;
1173 hr = IDWriteFontFace_QueryInterface(fontface, &IID_IDWriteFontFace1, (void**)&fontface1);
1174 if (FAILED(hr))
1175 WARN("failed to get IDWriteFontFace1\n");
1177 need_vertical = is_sideways && fontface1 && IDWriteFontFace1_HasVerticalGlyphVariants(fontface1);
1179 for (i = 0, g = 0, update_cluster = FALSE; i < length; i++) {
1180 UINT32 codepoint;
1182 if (!update_cluster) {
1183 codepoint = decode_surrogate_pair(string, i, length);
1184 if (!codepoint)
1185 codepoint = is_rtl ? bidi_get_mirrored_char(string[i]) : string[i];
1186 else
1187 update_cluster = TRUE;
1189 hr = IDWriteFontFace_GetGlyphIndices(fontface, &codepoint, 1, &glyph_indices[g]);
1190 if (FAILED(hr))
1191 goto done;
1193 if (need_vertical) {
1194 UINT16 vertical;
1196 hr = IDWriteFontFace1_GetVerticalGlyphVariants(fontface1, 1, &glyph_indices[g], &vertical);
1197 if (hr == S_OK)
1198 glyph_indices[g] = vertical;
1201 g++;
1203 else {
1204 INT32 k;
1206 update_cluster = FALSE;
1207 /* mark surrogate halves with same cluster */
1208 clustermap[i] = clustermap[i-1];
1209 /* update following clusters */
1210 for (k = i + 1; k >= 0 && k < length; k++)
1211 clustermap[k]--;
1214 *actual_glyph_count = g;
1216 hr = create_scriptshaping_cache(fontface, &cache);
1217 if (FAILED(hr))
1218 goto done;
1220 context.cache = cache;
1221 context.text = text;
1222 context.length = length;
1223 context.is_rtl = is_rtl;
1224 context.max_glyph_count = max_glyph_count;
1225 context.language_tag = get_opentype_language(locale);
1227 scriptprops = &dwritescripts_properties[script];
1228 if (scriptprops->ops && scriptprops->ops->contextual_shaping) {
1229 hr = scriptprops->ops->contextual_shaping(&context, clustermap, glyph_indices, actual_glyph_count);
1230 if (FAILED(hr))
1231 goto done;
1234 /* FIXME: apply default features */
1236 if (scriptprops->ops && scriptprops->ops->set_text_glyphs_props)
1237 hr = scriptprops->ops->set_text_glyphs_props(&context, clustermap, glyph_indices, *actual_glyph_count, text_props, glyph_props);
1238 else
1239 hr = default_shaping_ops.set_text_glyphs_props(&context, clustermap, glyph_indices, *actual_glyph_count, text_props, glyph_props);
1241 done:
1242 if (fontface1)
1243 IDWriteFontFace1_Release(fontface1);
1244 release_scriptshaping_cache(cache);
1245 heap_free(string);
1247 return hr;
1250 static HRESULT WINAPI dwritetextanalyzer_GetGlyphPlacements(IDWriteTextAnalyzer2 *iface,
1251 WCHAR const* text, UINT16 const* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES* props,
1252 UINT32 text_len, UINT16 const* glyphs, DWRITE_SHAPING_GLYPH_PROPERTIES const* glyph_props,
1253 UINT32 glyph_count, IDWriteFontFace *fontface, FLOAT emSize, BOOL is_sideways, BOOL is_rtl,
1254 DWRITE_SCRIPT_ANALYSIS const* analysis, WCHAR const* locale, DWRITE_TYPOGRAPHIC_FEATURES const** features,
1255 UINT32 const* feature_range_len, UINT32 feature_ranges, FLOAT *advances, DWRITE_GLYPH_OFFSET *offsets)
1257 DWRITE_FONT_METRICS metrics;
1258 IDWriteFontFace1 *fontface1;
1259 HRESULT hr;
1260 UINT32 i;
1262 TRACE("(%s %p %p %u %p %p %u %p %.2f %d %d %s %s %p %p %u %p %p)\n", debugstr_wn(text, text_len),
1263 clustermap, props, text_len, glyphs, glyph_props, glyph_count, fontface, emSize, is_sideways,
1264 is_rtl, debugstr_sa_script(analysis->script), debugstr_w(locale), features, feature_range_len,
1265 feature_ranges, advances, offsets);
1267 if (glyph_count == 0)
1268 return S_OK;
1270 hr = IDWriteFontFace_QueryInterface(fontface, &IID_IDWriteFontFace1, (void**)&fontface1);
1271 if (FAILED(hr)) {
1272 WARN("failed to get IDWriteFontFace1.\n");
1273 return hr;
1276 IDWriteFontFace_GetMetrics(fontface, &metrics);
1277 for (i = 0; i < glyph_count; i++) {
1278 if (glyph_props[i].isZeroWidthSpace)
1279 advances[i] = 0.0f;
1280 else {
1281 INT32 a;
1283 hr = IDWriteFontFace1_GetDesignGlyphAdvances(fontface1, 1, &glyphs[i], &a, is_sideways);
1284 if (FAILED(hr))
1285 a = 0;
1286 advances[i] = get_scaled_advance_width(a, emSize, &metrics);
1288 offsets[i].advanceOffset = 0.0f;
1289 offsets[i].ascenderOffset = 0.0f;
1292 /* FIXME: actually apply features */
1294 IDWriteFontFace1_Release(fontface1);
1295 return S_OK;
1298 static HRESULT WINAPI dwritetextanalyzer_GetGdiCompatibleGlyphPlacements(IDWriteTextAnalyzer2 *iface,
1299 WCHAR const* text, UINT16 const* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES* props,
1300 UINT32 text_len, UINT16 const* glyphs, DWRITE_SHAPING_GLYPH_PROPERTIES const* glyph_props,
1301 UINT32 glyph_count, IDWriteFontFace *fontface, FLOAT emSize, FLOAT ppdip,
1302 DWRITE_MATRIX const* transform, BOOL use_gdi_natural, BOOL is_sideways, BOOL is_rtl,
1303 DWRITE_SCRIPT_ANALYSIS const* analysis, WCHAR const* locale, DWRITE_TYPOGRAPHIC_FEATURES const** features,
1304 UINT32 const* feature_range_lengths, UINT32 feature_ranges, FLOAT *advances, DWRITE_GLYPH_OFFSET *offsets)
1306 DWRITE_FONT_METRICS metrics;
1307 IDWriteFontFace1 *fontface1;
1308 HRESULT hr;
1309 UINT32 i;
1311 TRACE("(%s %p %p %u %p %p %u %p %.2f %.2f %p %d %d %d %s %s %p %p %u %p %p)\n", debugstr_wn(text, text_len),
1312 clustermap, props, text_len, glyphs, glyph_props, glyph_count, fontface, emSize, ppdip,
1313 transform, use_gdi_natural, is_sideways, is_rtl, debugstr_sa_script(analysis->script), debugstr_w(locale),
1314 features, feature_range_lengths, feature_ranges, advances, offsets);
1316 if (glyph_count == 0)
1317 return S_OK;
1319 hr = IDWriteFontFace_QueryInterface(fontface, &IID_IDWriteFontFace1, (void**)&fontface1);
1320 if (FAILED(hr)) {
1321 WARN("failed to get IDWriteFontFace1.\n");
1322 return hr;
1325 hr = IDWriteFontFace_GetGdiCompatibleMetrics(fontface, emSize, ppdip, transform, &metrics);
1326 if (FAILED(hr)) {
1327 IDWriteFontFace1_Release(fontface1);
1328 WARN("failed to get compat metrics, 0x%08x\n", hr);
1329 return hr;
1331 for (i = 0; i < glyph_count; i++) {
1332 INT32 a;
1334 hr = IDWriteFontFace1_GetGdiCompatibleGlyphAdvances(fontface1, emSize, ppdip,
1335 transform, use_gdi_natural, is_sideways, 1, &glyphs[i], &a);
1336 if (FAILED(hr))
1337 advances[i] = 0.0f;
1338 else
1339 advances[i] = floorf(a * emSize * ppdip / metrics.designUnitsPerEm + 0.5f) / ppdip;
1340 offsets[i].advanceOffset = 0.0f;
1341 offsets[i].ascenderOffset = 0.0f;
1344 /* FIXME: actually apply features */
1346 IDWriteFontFace1_Release(fontface1);
1347 return S_OK;
1350 static inline FLOAT get_cluster_advance(const FLOAT *advances, UINT32 start, UINT32 end)
1352 FLOAT advance = 0.0f;
1353 for (; start < end; start++)
1354 advance += advances[start];
1355 return advance;
1358 static void apply_single_glyph_spacing(FLOAT leading_spacing, FLOAT trailing_spacing,
1359 FLOAT min_advance_width, UINT32 g, FLOAT const *advances, DWRITE_GLYPH_OFFSET const *offsets,
1360 DWRITE_SHAPING_GLYPH_PROPERTIES const *props, FLOAT *modified_advances, DWRITE_GLYPH_OFFSET *modified_offsets)
1362 BOOL reduced = leading_spacing < 0.0f || trailing_spacing < 0.0f;
1363 FLOAT advance = advances[g];
1364 FLOAT origin = 0.0f;
1366 if (props[g].isZeroWidthSpace) {
1367 modified_advances[g] = advances[g];
1368 modified_offsets[g] = offsets[g];
1369 return;
1372 /* first apply negative spacing and check if we hit minimum width */
1373 if (leading_spacing < 0.0f) {
1374 advance += leading_spacing;
1375 origin -= leading_spacing;
1377 if (trailing_spacing < 0.0f)
1378 advance += trailing_spacing;
1380 if (advance < min_advance_width) {
1381 FLOAT half = (min_advance_width - advance) / 2.0f;
1383 if (!reduced)
1384 origin -= half;
1385 else if (leading_spacing < 0.0f && trailing_spacing < 0.0f)
1386 origin -= half;
1387 else if (leading_spacing < 0.0f)
1388 origin -= min_advance_width - advance;
1390 advance = min_advance_width;
1393 /* now apply positive spacing adjustments */
1394 if (leading_spacing > 0.0f) {
1395 advance += leading_spacing;
1396 origin -= leading_spacing;
1398 if (trailing_spacing > 0.0f)
1399 advance += trailing_spacing;
1401 modified_advances[g] = advance;
1402 modified_offsets[g].advanceOffset = offsets[g].advanceOffset - origin;
1403 /* ascender is never touched, it's orthogonal to reading direction and is not
1404 affected by advance adjustments */
1405 modified_offsets[g].ascenderOffset = offsets[g].ascenderOffset;
1408 static void apply_cluster_spacing(FLOAT leading_spacing, FLOAT trailing_spacing, FLOAT min_advance_width,
1409 UINT32 start, UINT32 end, FLOAT const *advances, DWRITE_GLYPH_OFFSET const *offsets,
1410 FLOAT *modified_advances, DWRITE_GLYPH_OFFSET *modified_offsets)
1412 BOOL reduced = leading_spacing < 0.0f || trailing_spacing < 0.0f;
1413 FLOAT advance = get_cluster_advance(advances, start, end);
1414 FLOAT origin = 0.0f;
1415 UINT16 g;
1417 modified_advances[start] = advances[start];
1418 modified_advances[end-1] = advances[end-1];
1420 /* first apply negative spacing and check if we hit minimum width */
1421 if (leading_spacing < 0.0f) {
1422 advance += leading_spacing;
1423 modified_advances[start] += leading_spacing;
1424 origin -= leading_spacing;
1426 if (trailing_spacing < 0.0f) {
1427 advance += trailing_spacing;
1428 modified_advances[end-1] += trailing_spacing;
1431 advance = min_advance_width - advance;
1432 if (advance > 0.0f) {
1433 /* additional spacing is only applied to leading and trailing glyph */
1434 FLOAT half = advance / 2.0f;
1436 if (!reduced) {
1437 origin -= half;
1438 modified_advances[start] += half;
1439 modified_advances[end-1] += half;
1441 else if (leading_spacing < 0.0f && trailing_spacing < 0.0f) {
1442 origin -= half;
1443 modified_advances[start] += half;
1444 modified_advances[end-1] += half;
1446 else if (leading_spacing < 0.0f) {
1447 origin -= advance;
1448 modified_advances[start] += advance;
1450 else
1451 modified_advances[end-1] += advance;
1454 /* now apply positive spacing adjustments */
1455 if (leading_spacing > 0.0f) {
1456 modified_advances[start] += leading_spacing;
1457 origin -= leading_spacing;
1459 if (trailing_spacing > 0.0f)
1460 modified_advances[end-1] += trailing_spacing;
1462 for (g = start; g < end; g++) {
1463 if (g == start) {
1464 modified_offsets[g].advanceOffset = offsets[g].advanceOffset - origin;
1465 modified_offsets[g].ascenderOffset = offsets[g].ascenderOffset;
1467 else if (g == end - 1)
1468 /* trailing glyph offset is not adjusted */
1469 modified_offsets[g] = offsets[g];
1470 else {
1471 /* for all glyphs within a cluster use original advances and offsets */
1472 modified_advances[g] = advances[g];
1473 modified_offsets[g] = offsets[g];
1478 static inline UINT32 get_cluster_length(UINT16 const *clustermap, UINT32 start, UINT32 text_len)
1480 UINT16 g = clustermap[start];
1481 UINT32 length = 1;
1483 while (start < text_len && clustermap[++start] == g)
1484 length++;
1485 return length;
1488 /* Applies spacing adjustments to clusters.
1490 Adjustments are applied in the following order:
1492 1. Negative adjustments
1494 Leading and trailing spacing could be negative, at this step
1495 only negative ones are actually applied. Leading spacing is only
1496 applied to leading glyph, trailing - to trailing glyph.
1498 2. Minimum advance width
1500 Advances could only be reduced at this point or unchanged. In any
1501 case it's checked if cluster advance width is less than minimum width.
1502 If it's the case advance width is incremented up to minimum value.
1504 Important part is the direction in which this increment is applied;
1505 it depends on direction from which total cluster advance was trimmed
1506 at step 1. So it could be incremented from leading, trailing, or both
1507 sides. When applied to both sides, each side gets half of difference
1508 that brings advance to minimum width.
1510 3. Positive adjustments
1512 After minimum width rule was applied, positive spacing is applied in the same
1513 way as negative one on step 1.
1515 Glyph offset for leading glyph is adjusted too in a way that glyph origin
1516 keeps its position in coordinate system where initial advance width is counted
1517 from 0.
1519 Glyph properties
1521 It's known that isZeroWidthSpace property keeps initial advance from changing.
1523 TODO: test other properties; make isZeroWidthSpace work properly for clusters
1524 with more than one glyph.
1527 static HRESULT WINAPI dwritetextanalyzer1_ApplyCharacterSpacing(IDWriteTextAnalyzer2 *iface,
1528 FLOAT leading_spacing, FLOAT trailing_spacing, FLOAT min_advance_width, UINT32 len,
1529 UINT32 glyph_count, UINT16 const *clustermap, FLOAT const *advances, DWRITE_GLYPH_OFFSET const *offsets,
1530 DWRITE_SHAPING_GLYPH_PROPERTIES const *props, FLOAT *modified_advances, DWRITE_GLYPH_OFFSET *modified_offsets)
1532 UINT16 start;
1534 TRACE("(%.2f %.2f %.2f %u %u %p %p %p %p %p %p)\n", leading_spacing, trailing_spacing, min_advance_width,
1535 len, glyph_count, clustermap, advances, offsets, props, modified_advances, modified_offsets);
1537 if (min_advance_width < 0.0f) {
1538 if (modified_advances != advances)
1539 memset(modified_advances, 0, glyph_count*sizeof(*modified_advances));
1540 return E_INVALIDARG;
1543 /* minimum advance is not applied if no adjustments were made */
1544 if (leading_spacing == 0.0f && trailing_spacing == 0.0f) {
1545 memmove(modified_advances, advances, glyph_count*sizeof(*advances));
1546 memmove(modified_offsets, offsets, glyph_count*sizeof(*offsets));
1547 return S_OK;
1550 for (start = 0; start < len;) {
1551 UINT32 length = get_cluster_length(clustermap, start, len);
1553 if (length == 1) {
1554 UINT32 g = clustermap[start];
1556 apply_single_glyph_spacing(leading_spacing, trailing_spacing, min_advance_width,
1557 g, advances, offsets, props, modified_advances, modified_offsets);
1559 else {
1560 UINT32 g_start, g_end;
1562 g_start = clustermap[start];
1563 g_end = (start + length < len) ? clustermap[start + length] : glyph_count;
1565 apply_cluster_spacing(leading_spacing, trailing_spacing, min_advance_width,
1566 g_start, g_end, advances, offsets, modified_advances, modified_offsets);
1569 start += length;
1572 return S_OK;
1575 static HRESULT WINAPI dwritetextanalyzer1_GetBaseline(IDWriteTextAnalyzer2 *iface, IDWriteFontFace *face,
1576 DWRITE_BASELINE baseline, BOOL vertical, BOOL is_simulation_allowed, DWRITE_SCRIPT_ANALYSIS sa,
1577 const WCHAR *localeName, INT32 *baseline_coord, BOOL *exists)
1579 FIXME("(%p %d %d %u %s %p %p): stub\n", face, vertical, is_simulation_allowed, sa.script, debugstr_w(localeName),
1580 baseline_coord, exists);
1581 return E_NOTIMPL;
1584 static HRESULT WINAPI dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation(IDWriteTextAnalyzer2 *iface,
1585 IDWriteTextAnalysisSource1* source, UINT32 text_pos, UINT32 len, IDWriteTextAnalysisSink1 *sink)
1587 FIXME("(%p %u %u %p): stub\n", source, text_pos, len, sink);
1588 return E_NOTIMPL;
1591 static HRESULT WINAPI dwritetextanalyzer1_GetGlyphOrientationTransform(IDWriteTextAnalyzer2 *iface,
1592 DWRITE_GLYPH_ORIENTATION_ANGLE angle, BOOL is_sideways, DWRITE_MATRIX *transform)
1594 TRACE("(%d %d %p)\n", angle, is_sideways, transform);
1595 return IDWriteTextAnalyzer2_GetGlyphOrientationTransform(iface, angle, is_sideways, 0.0, 0.0, transform);
1598 static HRESULT WINAPI dwritetextanalyzer1_GetScriptProperties(IDWriteTextAnalyzer2 *iface, DWRITE_SCRIPT_ANALYSIS sa,
1599 DWRITE_SCRIPT_PROPERTIES *props)
1601 TRACE("(%u %p)\n", sa.script, props);
1603 if (sa.script > Script_LastId)
1604 return E_INVALIDARG;
1606 *props = dwritescripts_properties[sa.script].props;
1607 return S_OK;
1610 static inline BOOL is_char_from_simple_script(WCHAR c)
1612 if (IS_HIGH_SURROGATE(c) || IS_LOW_SURROGATE(c) ||
1613 /* LRM, RLM, LRE, RLE, PDF, LRO, RLO */
1614 c == 0x200e || c == 0x200f || (c >= 0x202a && c <= 0x202e))
1615 return FALSE;
1616 else {
1617 UINT16 script = get_char_script(c);
1618 return !dwritescripts_properties[script].is_complex;
1622 static HRESULT WINAPI dwritetextanalyzer1_GetTextComplexity(IDWriteTextAnalyzer2 *iface, const WCHAR *text,
1623 UINT32 len, IDWriteFontFace *face, BOOL *is_simple, UINT32 *len_read, UINT16 *indices)
1625 HRESULT hr = S_OK;
1626 int i;
1628 TRACE("(%s:%u %p %p %p %p)\n", debugstr_wn(text, len), len, face, is_simple, len_read, indices);
1630 *is_simple = FALSE;
1631 *len_read = 0;
1633 if (!face)
1634 return E_INVALIDARG;
1636 if (len == 0) {
1637 *is_simple = TRUE;
1638 return S_OK;
1641 *is_simple = text[0] && is_char_from_simple_script(text[0]);
1642 for (i = 1; i < len && text[i]; i++) {
1643 if (is_char_from_simple_script(text[i])) {
1644 if (!*is_simple)
1645 break;
1647 else
1648 *is_simple = FALSE;
1651 *len_read = i;
1653 /* fetch indices */
1654 if (*is_simple && indices) {
1655 UINT32 *codepoints = heap_alloc(*len_read*sizeof(UINT32));
1656 if (!codepoints)
1657 return E_OUTOFMEMORY;
1659 for (i = 0; i < *len_read; i++)
1660 codepoints[i] = text[i];
1662 hr = IDWriteFontFace_GetGlyphIndices(face, codepoints, *len_read, indices);
1663 heap_free(codepoints);
1666 return hr;
1669 static HRESULT WINAPI dwritetextanalyzer1_GetJustificationOpportunities(IDWriteTextAnalyzer2 *iface,
1670 IDWriteFontFace *face, FLOAT font_em_size, DWRITE_SCRIPT_ANALYSIS sa, UINT32 length, UINT32 glyph_count,
1671 const WCHAR *text, const UINT16 *clustermap, const DWRITE_SHAPING_GLYPH_PROPERTIES *prop, DWRITE_JUSTIFICATION_OPPORTUNITY *jo)
1673 FIXME("(%p %.2f %u %u %u %s %p %p %p): stub\n", face, font_em_size, sa.script, length, glyph_count,
1674 debugstr_wn(text, length), clustermap, prop, jo);
1675 return E_NOTIMPL;
1678 static HRESULT WINAPI dwritetextanalyzer1_JustifyGlyphAdvances(IDWriteTextAnalyzer2 *iface,
1679 FLOAT width, UINT32 glyph_count, const DWRITE_JUSTIFICATION_OPPORTUNITY *jo, const FLOAT *advances,
1680 const DWRITE_GLYPH_OFFSET *offsets, FLOAT *justifiedadvances, DWRITE_GLYPH_OFFSET *justifiedoffsets)
1682 FIXME("(%.2f %u %p %p %p %p %p): stub\n", width, glyph_count, jo, advances, offsets, justifiedadvances,
1683 justifiedoffsets);
1684 return E_NOTIMPL;
1687 static HRESULT WINAPI dwritetextanalyzer1_GetJustifiedGlyphs(IDWriteTextAnalyzer2 *iface,
1688 IDWriteFontFace *face, FLOAT font_em_size, DWRITE_SCRIPT_ANALYSIS sa, UINT32 length,
1689 UINT32 glyph_count, UINT32 max_glyphcount, const UINT16 *clustermap, const UINT16 *indices,
1690 const FLOAT *advances, const FLOAT *justifiedadvances, const DWRITE_GLYPH_OFFSET *justifiedoffsets,
1691 const DWRITE_SHAPING_GLYPH_PROPERTIES *prop, UINT32 *actual_count, UINT16 *modified_clustermap,
1692 UINT16 *modified_indices, FLOAT *modified_advances, DWRITE_GLYPH_OFFSET *modified_offsets)
1694 FIXME("(%p %.2f %u %u %u %u %p %p %p %p %p %p %p %p %p %p %p): stub\n", face, font_em_size, sa.script,
1695 length, glyph_count, max_glyphcount, clustermap, indices, advances, justifiedadvances, justifiedoffsets,
1696 prop, actual_count, modified_clustermap, modified_indices, modified_advances, modified_offsets);
1697 return E_NOTIMPL;
1700 static HRESULT WINAPI dwritetextanalyzer2_GetGlyphOrientationTransform(IDWriteTextAnalyzer2 *iface,
1701 DWRITE_GLYPH_ORIENTATION_ANGLE angle, BOOL is_sideways, FLOAT originX, FLOAT originY, DWRITE_MATRIX *m)
1703 static const DWRITE_MATRIX transforms[] = {
1704 { 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f },
1705 { 0.0f, 1.0f, -1.0f, 0.0f, 0.0f, 0.0f },
1706 { -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f },
1707 { 0.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f }
1710 TRACE("(%d %d %.2f %.2f %p)\n", angle, is_sideways, originX, originY, m);
1712 if ((UINT32)angle > DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES) {
1713 memset(m, 0, sizeof(*m));
1714 return E_INVALIDARG;
1717 /* for sideways case simply rotate 90 degrees more */
1718 if (is_sideways) {
1719 switch (angle) {
1720 case DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES:
1721 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES;
1722 break;
1723 case DWRITE_GLYPH_ORIENTATION_ANGLE_90_DEGREES:
1724 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES;
1725 break;
1726 case DWRITE_GLYPH_ORIENTATION_ANGLE_180_DEGREES:
1727 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES;
1728 break;
1729 case DWRITE_GLYPH_ORIENTATION_ANGLE_270_DEGREES:
1730 angle = DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES;
1731 break;
1732 default:
1737 *m = transforms[angle];
1739 /* shift components represent transform necessary to get from original point to
1740 rotated one in new coordinate system */
1741 if ((originX != 0.0f || originY != 0.0f) && angle != DWRITE_GLYPH_ORIENTATION_ANGLE_0_DEGREES) {
1742 m->dx = originX - (m->m11 * originX + m->m21 * originY);
1743 m->dy = originY - (m->m12 * originX + m->m22 * originY);
1746 return S_OK;
1749 static HRESULT WINAPI dwritetextanalyzer2_GetTypographicFeatures(IDWriteTextAnalyzer2 *iface,
1750 IDWriteFontFace *fontface, DWRITE_SCRIPT_ANALYSIS sa, const WCHAR *locale,
1751 UINT32 max_tagcount, UINT32 *actual_tagcount, DWRITE_FONT_FEATURE_TAG *tags)
1753 const struct dwritescript_properties *props;
1754 HRESULT hr = S_OK;
1755 UINT32 language;
1757 TRACE("(%p %u %s %u %p %p)\n", fontface, sa.script, debugstr_w(locale), max_tagcount, actual_tagcount,
1758 tags);
1760 if (sa.script > Script_LastId)
1761 return E_INVALIDARG;
1763 language = get_opentype_language(locale);
1764 props = &dwritescripts_properties[sa.script];
1765 *actual_tagcount = 0;
1767 if (props->scriptalttag)
1768 hr = opentype_get_typographic_features(fontface, props->scriptalttag, language, max_tagcount, actual_tagcount, tags);
1770 if (*actual_tagcount == 0)
1771 hr = opentype_get_typographic_features(fontface, props->scripttag, language, max_tagcount, actual_tagcount, tags);
1773 return hr;
1776 static HRESULT WINAPI dwritetextanalyzer2_CheckTypographicFeature(IDWriteTextAnalyzer2 *iface,
1777 IDWriteFontFace *face, DWRITE_SCRIPT_ANALYSIS sa, const WCHAR *localeName,
1778 DWRITE_FONT_FEATURE_TAG feature, UINT32 glyph_count, const UINT16 *indices, UINT8 *feature_applies)
1780 FIXME("(%p %u %s %x %u %p %p): stub\n", face, sa.script, debugstr_w(localeName), feature, glyph_count, indices,
1781 feature_applies);
1782 return E_NOTIMPL;
1785 static const struct IDWriteTextAnalyzer2Vtbl textanalyzervtbl = {
1786 dwritetextanalyzer_QueryInterface,
1787 dwritetextanalyzer_AddRef,
1788 dwritetextanalyzer_Release,
1789 dwritetextanalyzer_AnalyzeScript,
1790 dwritetextanalyzer_AnalyzeBidi,
1791 dwritetextanalyzer_AnalyzeNumberSubstitution,
1792 dwritetextanalyzer_AnalyzeLineBreakpoints,
1793 dwritetextanalyzer_GetGlyphs,
1794 dwritetextanalyzer_GetGlyphPlacements,
1795 dwritetextanalyzer_GetGdiCompatibleGlyphPlacements,
1796 dwritetextanalyzer1_ApplyCharacterSpacing,
1797 dwritetextanalyzer1_GetBaseline,
1798 dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation,
1799 dwritetextanalyzer1_GetGlyphOrientationTransform,
1800 dwritetextanalyzer1_GetScriptProperties,
1801 dwritetextanalyzer1_GetTextComplexity,
1802 dwritetextanalyzer1_GetJustificationOpportunities,
1803 dwritetextanalyzer1_JustifyGlyphAdvances,
1804 dwritetextanalyzer1_GetJustifiedGlyphs,
1805 dwritetextanalyzer2_GetGlyphOrientationTransform,
1806 dwritetextanalyzer2_GetTypographicFeatures,
1807 dwritetextanalyzer2_CheckTypographicFeature
1810 static IDWriteTextAnalyzer2 textanalyzer = { &textanalyzervtbl };
1812 HRESULT get_textanalyzer(IDWriteTextAnalyzer **ret)
1814 *ret = (IDWriteTextAnalyzer*)&textanalyzer;
1815 return S_OK;
1818 static HRESULT WINAPI dwritenumbersubstitution_QueryInterface(IDWriteNumberSubstitution *iface, REFIID riid, void **obj)
1820 struct dwrite_numbersubstitution *This = impl_from_IDWriteNumberSubstitution(iface);
1822 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
1824 if (IsEqualIID(riid, &IID_IDWriteNumberSubstitution) ||
1825 IsEqualIID(riid, &IID_IUnknown))
1827 *obj = iface;
1828 IDWriteNumberSubstitution_AddRef(iface);
1829 return S_OK;
1832 *obj = NULL;
1834 return E_NOINTERFACE;
1837 static ULONG WINAPI dwritenumbersubstitution_AddRef(IDWriteNumberSubstitution *iface)
1839 struct dwrite_numbersubstitution *This = impl_from_IDWriteNumberSubstitution(iface);
1840 ULONG ref = InterlockedIncrement(&This->ref);
1841 TRACE("(%p)->(%d)\n", This, ref);
1842 return ref;
1845 static ULONG WINAPI dwritenumbersubstitution_Release(IDWriteNumberSubstitution *iface)
1847 struct dwrite_numbersubstitution *This = impl_from_IDWriteNumberSubstitution(iface);
1848 ULONG ref = InterlockedDecrement(&This->ref);
1850 TRACE("(%p)->(%d)\n", This, ref);
1852 if (!ref) {
1853 heap_free(This->locale);
1854 heap_free(This);
1857 return ref;
1860 static const struct IDWriteNumberSubstitutionVtbl numbersubstitutionvtbl = {
1861 dwritenumbersubstitution_QueryInterface,
1862 dwritenumbersubstitution_AddRef,
1863 dwritenumbersubstitution_Release
1866 struct dwrite_numbersubstitution *unsafe_impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution *iface)
1868 if (!iface || iface->lpVtbl != &numbersubstitutionvtbl)
1869 return NULL;
1870 return CONTAINING_RECORD(iface, struct dwrite_numbersubstitution, IDWriteNumberSubstitution_iface);
1873 HRESULT create_numbersubstitution(DWRITE_NUMBER_SUBSTITUTION_METHOD method, const WCHAR *locale,
1874 BOOL ignore_user_override, IDWriteNumberSubstitution **ret)
1876 struct dwrite_numbersubstitution *substitution;
1878 *ret = NULL;
1880 if ((UINT32)method > DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL)
1881 return E_INVALIDARG;
1883 if (method != DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE && !IsValidLocaleName(locale))
1884 return E_INVALIDARG;
1886 substitution = heap_alloc(sizeof(*substitution));
1887 if (!substitution)
1888 return E_OUTOFMEMORY;
1890 substitution->IDWriteNumberSubstitution_iface.lpVtbl = &numbersubstitutionvtbl;
1891 substitution->ref = 1;
1892 substitution->ignore_user_override = ignore_user_override;
1893 substitution->method = method;
1894 substitution->locale = heap_strdupW(locale);
1895 if (locale && !substitution->locale) {
1896 heap_free(substitution);
1897 return E_OUTOFMEMORY;
1900 *ret = &substitution->IDWriteNumberSubstitution_iface;
1901 return S_OK;
1904 /* IDWriteFontFallback */
1905 static HRESULT WINAPI fontfallback_QueryInterface(IDWriteFontFallback *iface, REFIID riid, void **obj)
1907 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback(iface);
1909 TRACE("(%p)->(%s %p)\n", fallback, debugstr_guid(riid), obj);
1911 if (IsEqualIID(riid, &IID_IDWriteFontFallback) || IsEqualIID(riid, &IID_IUnknown)) {
1912 *obj = iface;
1913 IDWriteFontFallback_AddRef(iface);
1914 return S_OK;
1917 *obj = NULL;
1918 return E_NOINTERFACE;
1921 static ULONG WINAPI fontfallback_AddRef(IDWriteFontFallback *iface)
1923 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback(iface);
1924 TRACE("(%p)\n", fallback);
1925 return IDWriteFactory5_AddRef(fallback->factory);
1928 static ULONG WINAPI fontfallback_Release(IDWriteFontFallback *iface)
1930 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback(iface);
1931 TRACE("(%p)\n", fallback);
1932 return IDWriteFactory5_Release(fallback->factory);
1935 static int compare_fallback_mapping(const void *a, const void *b)
1937 UINT32 ch = *(UINT32*)a;
1938 struct fallback_mapping *mapping = (struct fallback_mapping*)b;
1940 if (ch > mapping->range.last)
1941 return 1;
1942 else if (ch < mapping->range.first)
1943 return -1;
1944 else
1945 return 0;
1948 static const struct fallback_mapping *find_fallback_mapping(struct dwrite_fontfallback *fallback, UINT32 ch)
1950 return bsearch(&ch, fallback->mappings, fallback->count, sizeof(*fallback->mappings),
1951 compare_fallback_mapping);
1954 HRESULT create_matching_font(IDWriteFontCollection *collection, const WCHAR *name,
1955 DWRITE_FONT_WEIGHT weight, DWRITE_FONT_STYLE style, DWRITE_FONT_STRETCH stretch, IDWriteFont **font)
1957 IDWriteFontFamily *family;
1958 BOOL exists = FALSE;
1959 HRESULT hr;
1960 UINT32 i;
1962 *font = NULL;
1964 hr = IDWriteFontCollection_FindFamilyName(collection, name, &i, &exists);
1965 if (FAILED(hr))
1966 return hr;
1968 if (!exists)
1969 return E_FAIL;
1971 hr = IDWriteFontCollection_GetFontFamily(collection, i, &family);
1972 if (FAILED(hr))
1973 return hr;
1975 hr = IDWriteFontFamily_GetFirstMatchingFont(family, weight, stretch, style, font);
1976 IDWriteFontFamily_Release(family);
1977 return hr;
1980 static HRESULT fallback_map_characters(IDWriteFont *font, const WCHAR *text, UINT32 length, UINT32 *mapped_length)
1982 HRESULT hr = S_OK;
1983 UINT32 i;
1985 for (i = 0; i < length; i++) {
1986 UINT16 script = get_char_script(text[i]);
1987 BOOL exists;
1989 if (script == Script_Unknown || script == Script_Common) {
1990 ++*mapped_length;
1991 continue;
1994 /* stop on first unsupported character */
1995 exists = FALSE;
1996 hr = IDWriteFont_HasCharacter(font, text[i], &exists);
1997 if (hr == S_OK && exists)
1998 ++*mapped_length;
1999 else
2000 break;
2003 return hr;
2006 static HRESULT fallback_get_fallback_font(struct dwrite_fontfallback *fallback, const WCHAR *text, UINT32 length,
2007 DWRITE_FONT_WEIGHT weight, DWRITE_FONT_STYLE style, DWRITE_FONT_STRETCH stretch, UINT32 *mapped_length, IDWriteFont **mapped_font)
2009 const struct fallback_mapping *mapping;
2010 HRESULT hr;
2012 mapping = find_fallback_mapping(fallback, text[0]);
2013 if (!mapping) {
2014 WARN("no mapping for 0x%x\n", text[0]);
2015 return E_FAIL;
2018 /* now let's see what fallback can handle */
2019 hr = create_matching_font((IDWriteFontCollection*)fallback->systemcollection, mapping->family, weight, style, stretch, mapped_font);
2020 if (FAILED(hr)) {
2021 WARN("failed to create fallback font %s for range [0x%x,0x%x], 0x%08x\n", debugstr_w(mapping->family),
2022 mapping->range.first, mapping->range.last, hr);
2023 return hr;
2026 hr = fallback_map_characters(*mapped_font, text, length, mapped_length);
2027 if (FAILED(hr))
2028 WARN("mapping with fallback font %s failed, 0x%08x\n", debugstr_w(mapping->family), hr);
2030 if (!*mapped_length) {
2031 IDWriteFont_Release(*mapped_font);
2032 *mapped_font = NULL;
2035 return *mapped_length ? S_OK : E_FAIL;
2038 static HRESULT WINAPI fontfallback_MapCharacters(IDWriteFontFallback *iface, IDWriteTextAnalysisSource *source,
2039 UINT32 position, UINT32 length, IDWriteFontCollection *basecollection, const WCHAR *basefamily,
2040 DWRITE_FONT_WEIGHT weight, DWRITE_FONT_STYLE style, DWRITE_FONT_STRETCH stretch, UINT32 *mapped_length,
2041 IDWriteFont **ret_font, FLOAT *scale)
2043 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback(iface);
2044 WCHAR *buff = NULL;
2045 const WCHAR *text;
2046 HRESULT hr;
2048 TRACE("(%p)->(%p %u %u %p, %s, %u, %u, %u, %p, %p, %p)\n", fallback, source, position, length,
2049 basecollection, debugstr_w(basefamily), weight, style, stretch, mapped_length, ret_font, scale);
2051 *mapped_length = 0;
2052 *scale = 1.0f;
2053 *ret_font = NULL;
2055 if (!source)
2056 return E_INVALIDARG;
2058 if (length == 0)
2059 return S_OK;
2061 if (!basecollection)
2062 basecollection = (IDWriteFontCollection*)fallback->systemcollection;
2064 hr = get_text_source_ptr(source, position, length, &text, &buff);
2065 if (FAILED(hr))
2066 goto done;
2068 if (basefamily && *basefamily) {
2069 hr = create_matching_font(basecollection, basefamily, weight, style, stretch, ret_font);
2070 if (FAILED(hr))
2071 goto done;
2073 hr = fallback_map_characters(*ret_font, text, length, mapped_length);
2074 if (FAILED(hr))
2075 goto done;
2078 if (!*mapped_length) {
2079 IDWriteFont *mapped_font;
2081 hr = fallback_get_fallback_font(fallback, text, length, weight, style, stretch, mapped_length, &mapped_font);
2082 if (FAILED(hr)) {
2083 /* fallback wasn't found, keep base font if any, so we can get at least some visual output */
2084 if (*ret_font) {
2085 *mapped_length = length;
2086 hr = S_OK;
2089 else {
2090 if (*ret_font)
2091 IDWriteFont_Release(*ret_font);
2092 *ret_font = mapped_font;
2096 done:
2097 heap_free(buff);
2098 return hr;
2101 static const IDWriteFontFallbackVtbl fontfallbackvtbl = {
2102 fontfallback_QueryInterface,
2103 fontfallback_AddRef,
2104 fontfallback_Release,
2105 fontfallback_MapCharacters
2108 HRESULT create_system_fontfallback(IDWriteFactory5 *factory, IDWriteFontFallback **ret)
2110 struct dwrite_fontfallback *fallback;
2112 *ret = NULL;
2114 fallback = heap_alloc(sizeof(*fallback));
2115 if (!fallback)
2116 return E_OUTOFMEMORY;
2118 fallback->IDWriteFontFallback_iface.lpVtbl = &fontfallbackvtbl;
2119 fallback->factory = factory;
2120 fallback->mappings = fontfallback_neutral_data;
2121 fallback->count = sizeof(fontfallback_neutral_data)/sizeof(fontfallback_neutral_data[0]);
2122 IDWriteFactory5_GetSystemFontCollection(fallback->factory, FALSE, &fallback->systemcollection, FALSE);
2124 *ret = &fallback->IDWriteFontFallback_iface;
2125 return S_OK;
2128 void release_system_fontfallback(IDWriteFontFallback *iface)
2130 struct dwrite_fontfallback *fallback = impl_from_IDWriteFontFallback(iface);
2131 IDWriteFontCollection1_Release(fallback->systemcollection);
2132 heap_free(fallback);