kernel32/tests: Add UTF-7 encoded surrounding characters tests.
[wine/multimedia.git] / dlls / dwrite / analyzer.c
blob82e9fa5b4eb8ca0a8f71f37904261c1fbea63161
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 "dwrite_private.h"
25 #include "scripts.h"
27 WINE_DEFAULT_DEBUG_CHANNEL(dwrite);
29 extern const unsigned short wine_linebreak_table[];
30 extern const unsigned short wine_scripts_table[];
32 struct dwritescript_properties {
33 DWRITE_SCRIPT_PROPERTIES props;
34 BOOL is_complex;
35 const struct scriptshaping_ops *ops;
38 /* NOTE: keep this array synced with script ids from scripts.h */
39 static const struct dwritescript_properties dwritescripts_properties[Script_LastId+1] = {
40 { /* Zzzz */ { 0x7a7a7a5a, 999, 15, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
41 { /* Zyyy */ { 0x7979795a, 998, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
42 { /* Arab */ { 0x62617241, 160, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, TRUE },
43 { /* Armn */ { 0x6e6d7241, 230, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
44 { /* Avst */ { 0x74737641, 134, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
45 { /* Bali */ { 0x696c6142, 360, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 } },
46 { /* Bamu */ { 0x756d6142, 435, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 } },
47 { /* Bass */ { 0x73736142, 259, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
48 { /* Batk */ { 0x6b746142, 365, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
49 { /* Beng */ { 0x676e6542, 325, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, TRUE },
50 { /* Bopo */ { 0x6f706f42, 285, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 } },
51 { /* Brah */ { 0x68617242, 300, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
52 { /* Brai */ { 0x69617242, 570, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, TRUE },
53 { /* Bugi */ { 0x69677542, 367, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
54 { /* Buhd */ { 0x64687542, 372, 8, 0x0020, 0, 0, 1, 0, 0, 0, 0 } },
55 { /* Cans */ { 0x736e6143, 440, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, TRUE },
56 { /* Cari */ { 0x69726143, 201, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 } },
57 { /* Aghb */ { 0x62686741, 239, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
58 { /* Cakm */ { 0x6d6b6143, 349, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
59 { /* Cham */ { 0x6d616843, 358, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 } },
60 { /* Cher */ { 0x72656843, 445, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, TRUE },
61 { /* Copt */ { 0x74706f43, 204, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
62 { /* Xsux */ { 0x78757358, 20, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 } },
63 { /* Cprt */ { 0x74727043, 403, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 } },
64 { /* Cyrl */ { 0x6c727943, 220, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
65 { /* Dsrt */ { 0x74727344, 250, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, TRUE },
66 { /* Deva */ { 0x61766544, 315, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, TRUE },
67 { /* Dupl */ { 0x6c707544, 755, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
68 { /* Egyp */ { 0x70796745, 50, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 } },
69 { /* Elba */ { 0x61626c45, 226, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
70 { /* Ethi */ { 0x69687445, 430, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, TRUE },
71 { /* Geor */ { 0x726f6547, 240, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
72 { /* Glag */ { 0x67616c47, 225, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
73 { /* Goth */ { 0x68746f47, 206, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
74 { /* Gran */ { 0x6e617247, 343, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
75 { /* Grek */ { 0x6b657247, 200, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
76 { /* Gujr */ { 0x726a7547, 320, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, TRUE },
77 { /* Guru */ { 0x75727547, 310, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, TRUE },
78 { /* Hani */ { 0x696e6148, 500, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 } },
79 { /* Hang */ { 0x676e6148, 286, 8, 0x0020, 1, 1, 1, 1, 0, 0, 0 }, TRUE },
80 { /* Hano */ { 0x6f6e6148, 371, 8, 0x0020, 0, 0, 1, 0, 0, 0, 0 } },
81 { /* Hebr */ { 0x72626548, 125, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, TRUE },
82 { /* Hira */ { 0x61726948, 410, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 } },
83 { /* Armi */ { 0x696d7241, 124, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
84 { /* Phli */ { 0x696c6850, 131, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
85 { /* Prti */ { 0x69747250, 130, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
86 { /* Java */ { 0x6176614a, 361, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 } },
87 { /* Kthi */ { 0x6968744b, 317, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 } },
88 { /* Knda */ { 0x61646e4b, 345, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, TRUE },
89 { /* Kana */ { 0x616e614b, 411, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 } },
90 { /* Kali */ { 0x696c614b, 357, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
91 { /* Khar */ { 0x7261684b, 305, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 } },
92 { /* Khmr */ { 0x726d684b, 355, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, TRUE },
93 { /* Khoj */ { 0x6a6f684b, 322, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
94 { /* Sind */ { 0x646e6953, 318, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
95 { /* Laoo */ { 0x6f6f614c, 356, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, TRUE },
96 { /* Latn */ { 0x6e74614c, 215, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, FALSE, &latn_shaping_ops },
97 { /* Lepc */ { 0x6370654c, 335, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 } },
98 { /* Limb */ { 0x626d694c, 336, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 } },
99 { /* Lina */ { 0x616e694c, 400, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
100 { /* Linb */ { 0x626e694c, 401, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 } },
101 { /* Lisu */ { 0x7573694c, 399, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
102 { /* Lyci */ { 0x6963794c, 202, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
103 { /* Lydi */ { 0x6964794c, 116, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
104 { /* Mahj */ { 0x6a68614d, 314, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
105 { /* Mlym */ { 0x6d796c4d, 347, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, TRUE },
106 { /* Mand */ { 0x646e614d, 140, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 } },
107 { /* Mani */ { 0x696e614d, 139, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
108 { /* Mtei */ { 0x6965744d, 337, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 } },
109 { /* Mend */ { 0x646e654d, 438, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
110 { /* Merc */ { 0x6372654d, 101, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
111 { /* Mero */ { 0x6f72654d, 100, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
112 { /* Plrd */ { 0x64726c50, 282, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
113 { /* Modi */ { 0x69646f4d, 324, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
114 { /* Mong */ { 0x676e6f4d, 145, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, TRUE },
115 { /* Mroo */ { 0x6f6f724d, 199, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
116 { /* Mymr */ { 0x726d794d, 350, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, TRUE },
117 { /* Nbat */ { 0x7461624e, 159, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
118 { /* Talu */ { 0x756c6154, 354, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, TRUE },
119 { /* Nkoo */ { 0x6f6f6b4e, 165, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, TRUE },
120 { /* Ogam */ { 0x6d61674f, 212, 1, 0x1680, 0, 1, 0, 0, 0, 1, 0 }, TRUE },
121 { /* Olck */ { 0x6b636c4f, 261, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
122 { /* Ital */ { 0x6c617449, 210, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
123 { /* Narb */ { 0x6272614e, 106, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
124 { /* Perm */ { 0x6d726550, 227, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
125 { /* Xpeo */ { 0x6f657058, 30, 1, 0x0020, 0, 1, 1, 1, 0, 0, 0 }, TRUE },
126 { /* Sarb */ { 0x62726153, 105, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
127 { /* Orkh */ { 0x686b724f, 175, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
128 { /* Orya */ { 0x6179724f, 327, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, TRUE },
129 { /* Osma */ { 0x616d734f, 260, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, TRUE },
130 { /* Hmng */ { 0x676e6d48, 450, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
131 { /* Palm */ { 0x6d6c6150, 126, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
132 { /* Pauc */ { 0x63756150, 263, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
133 { /* Phag */ { 0x67616850, 331, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, TRUE },
134 { /* Phnx */ { 0x786e6850, 115, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 } },
135 { /* Phlp */ { 0x706c6850, 132, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
136 { /* Rjng */ { 0x676e6a52, 363, 8, 0x0020, 1, 0, 1, 0, 0, 0, 0 } },
137 { /* Runr */ { 0x726e7552, 211, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, TRUE },
138 { /* Samr */ { 0x726d6153, 123, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
139 { /* Saur */ { 0x72756153, 344, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 } },
140 { /* Shrd */ { 0x64726853, 319, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
141 { /* Shaw */ { 0x77616853, 281, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
142 { /* Sidd */ { 0x64646953, 302, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
143 { /* Sinh */ { 0x686e6953, 348, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, TRUE },
144 { /* Sora */ { 0x61726f53, 398, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
145 { /* Sund */ { 0x646e7553, 362, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 } },
146 { /* Sylo */ { 0x6f6c7953, 316, 8, 0x0020, 1, 1, 0, 0, 0, 1, 0 } },
147 { /* Syrc */ { 0x63727953, 135, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, TRUE },
148 { /* Tglg */ { 0x676c6754, 370, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
149 { /* Tagb */ { 0x62676154, 373, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
150 { /* Tale */ { 0x656c6154, 353, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, TRUE },
151 { /* Lana */ { 0x616e614c, 351, 8, 0x0020, 1, 0, 1, 0, 0, 0, 0 } },
152 { /* Tavt */ { 0x74766154, 359, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 } },
153 { /* Takr */ { 0x726b6154, 321, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
154 { /* Taml */ { 0x6c6d6154, 346, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, TRUE },
155 { /* Telu */ { 0x756c6554, 340, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, TRUE },
156 { /* Thaa */ { 0x61616854, 170, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, TRUE },
157 { /* Thai */ { 0x69616854, 352, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, TRUE },
158 { /* Tibt */ { 0x74626954, 330, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, TRUE },
159 { /* Tfng */ { 0x676e6654, 120, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, TRUE },
160 { /* Tirh */ { 0x68726954, 326, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
161 { /* Ugar */ { 0x72616755, 40, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 } },
162 { /* Vaii */ { 0x69696156, 470, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, TRUE },
163 { /* Wara */ { 0x61726157, 262, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
164 { /* Yiii */ { 0x69696959, 460, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, TRUE }
167 struct dwrite_numbersubstitution {
168 IDWriteNumberSubstitution IDWriteNumberSubstitution_iface;
169 LONG ref;
171 DWRITE_NUMBER_SUBSTITUTION_METHOD method;
172 WCHAR *locale;
173 BOOL ignore_user_override;
176 static inline struct dwrite_numbersubstitution *impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution *iface)
178 return CONTAINING_RECORD(iface, struct dwrite_numbersubstitution, IDWriteNumberSubstitution_iface);
181 static inline UINT32 decode_surrogate_pair(const WCHAR *str, UINT32 index, UINT32 end)
183 if (index < end-1 && IS_SURROGATE_PAIR(str[index], str[index+1])) {
184 UINT32 ch = 0x10000 + ((str[index] - 0xd800) << 10) + (str[index+1] - 0xdc00);
185 TRACE("surrogate pair (%x %x) => %x\n", str[index], str[index+1], ch);
186 return ch;
188 return 0;
191 static inline UINT16 get_char_script(WCHAR c)
193 UINT16 script = get_table_entry(wine_scripts_table, c);
194 if (script == Script_Unknown) {
195 WORD type;
196 if (GetStringTypeW(CT_CTYPE1, &c, 1, &type) && (type & C1_CNTRL))
197 script = Script_Common;
199 return script;
202 static HRESULT analyze_script(const WCHAR *text, UINT32 len, IDWriteTextAnalysisSink *sink)
204 DWRITE_SCRIPT_ANALYSIS sa;
205 UINT32 pos, i, length;
207 if (!len) return S_OK;
209 sa.script = get_char_script(*text);
211 pos = 0;
212 length = 1;
214 for (i = 1; i < len; i++)
216 UINT16 script = get_char_script(text[i]);
218 /* Unknown type is ignored when preceded or followed by another script */
219 if (sa.script == Script_Unknown) sa.script = script;
220 if (script == Script_Unknown && sa.script != Script_Common) script = sa.script;
221 /* this is a length of a sequence to be reported next */
222 if (sa.script == script) length++;
224 if (sa.script != script)
226 HRESULT hr;
228 sa.shapes = sa.script != Script_Common ? DWRITE_SCRIPT_SHAPES_DEFAULT : DWRITE_SCRIPT_SHAPES_NO_VISUAL;
229 hr = IDWriteTextAnalysisSink_SetScriptAnalysis(sink, pos, length, &sa);
230 if (FAILED(hr)) return hr;
231 pos = i;
232 length = 1;
233 sa.script = script;
237 /* 1 length case or normal completion call */
238 sa.shapes = sa.script != Script_Common ? DWRITE_SCRIPT_SHAPES_DEFAULT : DWRITE_SCRIPT_SHAPES_NO_VISUAL;
239 return IDWriteTextAnalysisSink_SetScriptAnalysis(sink, pos, length, &sa);
242 struct linebreaking_state {
243 DWRITE_LINE_BREAKPOINT *breakpoints;
244 UINT32 count;
247 enum BreakConditionLocation {
248 BreakConditionBefore,
249 BreakConditionAfter
252 enum linebreaking_classes {
253 b_BK = 1,
254 b_CR,
255 b_LF,
256 b_CM,
257 b_SG,
258 b_GL,
259 b_CB,
260 b_SP,
261 b_ZW,
262 b_NL,
263 b_WJ,
264 b_JL,
265 b_JV,
266 b_JT,
267 b_H2,
268 b_H3,
269 b_XX,
270 b_OP,
271 b_CL,
272 b_CP,
273 b_QU,
274 b_NS,
275 b_EX,
276 b_SY,
277 b_IS,
278 b_PR,
279 b_PO,
280 b_NU,
281 b_AL,
282 b_ID,
283 b_IN,
284 b_HY,
285 b_BB,
286 b_BA,
287 b_SA,
288 b_AI,
289 b_B2,
290 b_HL,
291 b_CJ,
292 b_RI
295 /* "Can break" is a weak condition, stronger "may not break" and "must break" override it. Initially all conditions are
296 set to "can break" and could only be changed once. */
297 static inline void set_break_condition(UINT32 pos, enum BreakConditionLocation location, DWRITE_BREAK_CONDITION condition,
298 struct linebreaking_state *state)
300 if (location == BreakConditionBefore) {
301 if (state->breakpoints[pos].breakConditionBefore != DWRITE_BREAK_CONDITION_CAN_BREAK)
302 return;
303 state->breakpoints[pos].breakConditionBefore = condition;
304 if (pos > 0)
305 state->breakpoints[pos-1].breakConditionAfter = condition;
307 else {
308 if (state->breakpoints[pos].breakConditionAfter != DWRITE_BREAK_CONDITION_CAN_BREAK)
309 return;
310 state->breakpoints[pos].breakConditionAfter = condition;
311 if (pos + 1 < state->count)
312 state->breakpoints[pos+1].breakConditionBefore = condition;
316 static HRESULT analyze_linebreaks(const WCHAR *text, UINT32 count, DWRITE_LINE_BREAKPOINT *breakpoints)
318 struct linebreaking_state state;
319 short *break_class;
320 int i, j;
322 break_class = heap_alloc(count*sizeof(short));
323 if (!break_class)
324 return E_OUTOFMEMORY;
326 state.breakpoints = breakpoints;
327 state.count = count;
329 /* LB31 - allow breaks everywhere. It will be overridden if needed as
330 other rules dictate. */
331 for (i = 0; i < count; i++)
333 break_class[i] = get_table_entry(wine_linebreak_table, text[i]);
335 breakpoints[i].breakConditionBefore = DWRITE_BREAK_CONDITION_CAN_BREAK;
336 breakpoints[i].breakConditionAfter = DWRITE_BREAK_CONDITION_CAN_BREAK;
337 breakpoints[i].isWhitespace = break_class[i] == b_BK || break_class[i] == b_ZW || break_class[i] == b_SP || isspaceW(text[i]);
338 breakpoints[i].isSoftHyphen = FALSE;
339 breakpoints[i].padding = 0;
341 /* LB1 - resolve some classes. TODO: use external algorithms for these classes. */
342 switch (break_class[i])
344 case b_AI:
345 case b_SA:
346 case b_SG:
347 case b_XX:
348 break_class[i] = b_AL;
349 break;
350 case b_CJ:
351 break_class[i] = b_NS;
352 break;
356 /* LB2 - never break at the start */
357 set_break_condition(0, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
358 /* LB3 - always break at the end. This one is ignored. */
360 for (i = 0; i < count; i++)
362 switch (break_class[i])
364 /* LB4 - LB6 */
365 case b_CR:
366 /* LB5 - don't break CR x LF */
367 if (i < count-1 && break_class[i+1] == b_LF)
369 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
370 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
371 break;
373 case b_LF:
374 case b_NL:
375 case b_BK:
376 /* LB4 - LB5 - always break after hard breaks */
377 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MUST_BREAK, &state);
378 /* LB6 - do not break before hard breaks */
379 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
380 break;
381 /* LB7 - do not break before spaces */
382 case b_SP:
383 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
384 break;
385 case b_ZW:
386 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
387 /* LB8 - break before character after zero-width space, skip spaces in-between */
388 while (i < count-1 && break_class[i+1] == b_SP)
389 i++;
390 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
391 break;
395 /* LB9 - LB10 */
396 for (i = 0; i < count; i++)
398 if (break_class[i] == b_CM)
400 if (i > 0)
402 switch (break_class[i-1])
404 case b_SP:
405 case b_BK:
406 case b_CR:
407 case b_LF:
408 case b_NL:
409 case b_ZW:
410 break_class[i] = b_AL;
411 break;
412 default:
413 break_class[i] = break_class[i-1];
416 else break_class[i] = b_AL;
420 for (i = 0; i < count; i++)
422 switch (break_class[i])
424 /* LB11 - don't break before and after word joiner */
425 case b_WJ:
426 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
427 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
428 break;
429 /* LB12 - don't break after glue */
430 case b_GL:
431 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
432 /* LB12a */
433 if (i > 0)
435 if (break_class[i-1] != b_SP && break_class[i-1] != b_BA && break_class[i-1] != b_HY)
436 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
438 break;
439 /* LB13 */
440 case b_CL:
441 case b_CP:
442 case b_EX:
443 case b_IS:
444 case b_SY:
445 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
446 break;
447 /* LB14 */
448 case b_OP:
449 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
450 while (i < count-1 && break_class[i+1] == b_SP) {
451 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
452 i++;
454 break;
455 /* LB15 */
456 case b_QU:
457 j = i+1;
458 while (j < count-1 && break_class[j] == b_SP)
459 j++;
460 if (break_class[j] == b_OP)
461 for (; j > i; j--)
462 set_break_condition(j, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
463 break;
464 /* LB16 */
465 case b_NS:
466 j = i-1;
467 while(j > 0 && break_class[j] == b_SP)
468 j--;
469 if (break_class[j] == b_CL || break_class[j] == b_CP)
470 for (j++; j <= i; j++)
471 set_break_condition(j, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
472 break;
473 /* LB17 */
474 case b_B2:
475 j = i+1;
476 while (j < count && break_class[j] == b_SP)
477 j++;
478 if (break_class[j] == b_B2)
479 for (; j > i; j--)
480 set_break_condition(j, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
481 break;
485 for (i = 0; i < count; i++)
487 switch(break_class[i])
489 /* LB18 - break is allowed after space */
490 case b_SP:
491 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
492 break;
493 /* LB19 - don't break before or after quotation mark */
494 case b_QU:
495 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
496 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
497 break;
498 /* LB20 */
499 case b_CB:
500 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
501 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
502 break;
503 /* LB21 */
504 case b_BA:
505 case b_HY:
506 case b_NS:
507 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
508 break;
509 case b_BB:
510 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
511 break;
512 /* LB21a */
513 case b_HL:
514 if (i < count-2)
515 switch (break_class[i+1])
517 case b_HY:
518 case b_BA:
519 set_break_condition(i+1, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
521 break;
522 /* LB22 */
523 case b_IN:
524 if (i > 0)
526 switch (break_class[i-1])
528 case b_AL:
529 case b_HL:
530 case b_ID:
531 case b_IN:
532 case b_NU:
533 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
536 break;
539 if (i < count-1)
541 /* LB23 */
542 if ((break_class[i] == b_ID && break_class[i+1] == b_PO) ||
543 (break_class[i] == b_AL && break_class[i+1] == b_NU) ||
544 (break_class[i] == b_HL && break_class[i+1] == b_NU) ||
545 (break_class[i] == b_NU && break_class[i+1] == b_AL) ||
546 (break_class[i] == b_NU && break_class[i+1] == b_HL))
547 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
548 /* LB24 */
549 if ((break_class[i] == b_PR && break_class[i+1] == b_ID) ||
550 (break_class[i] == b_PR && break_class[i+1] == b_AL) ||
551 (break_class[i] == b_PR && break_class[i+1] == b_HL) ||
552 (break_class[i] == b_PO && break_class[i+1] == b_AL) ||
553 (break_class[i] == b_PO && break_class[i+1] == b_HL))
554 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
556 /* LB25 */
557 if ((break_class[i] == b_CL && break_class[i+1] == b_PO) ||
558 (break_class[i] == b_CP && break_class[i+1] == b_PO) ||
559 (break_class[i] == b_CL && break_class[i+1] == b_PR) ||
560 (break_class[i] == b_CP && break_class[i+1] == b_PR) ||
561 (break_class[i] == b_NU && break_class[i+1] == b_PO) ||
562 (break_class[i] == b_NU && break_class[i+1] == b_PR) ||
563 (break_class[i] == b_PO && break_class[i+1] == b_OP) ||
564 (break_class[i] == b_PO && break_class[i+1] == b_NU) ||
565 (break_class[i] == b_PR && break_class[i+1] == b_OP) ||
566 (break_class[i] == b_PR && break_class[i+1] == b_NU) ||
567 (break_class[i] == b_HY && break_class[i+1] == b_NU) ||
568 (break_class[i] == b_IS && break_class[i+1] == b_NU) ||
569 (break_class[i] == b_NU && break_class[i+1] == b_NU) ||
570 (break_class[i] == b_SY && break_class[i+1] == b_NU))
571 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
573 /* LB26 */
574 if (break_class[i] == b_JL)
576 switch (break_class[i+1])
578 case b_JL:
579 case b_JV:
580 case b_H2:
581 case b_H3:
582 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
585 if ((break_class[i] == b_JV || break_class[i] == b_H2) &&
586 (break_class[i+1] == b_JV || break_class[i+1] == b_JT))
587 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
588 if ((break_class[i] == b_JT || break_class[i] == b_H3) &&
589 break_class[i+1] == b_JT)
590 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
592 /* LB27 */
593 switch (break_class[i])
595 case b_JL:
596 case b_JV:
597 case b_JT:
598 case b_H2:
599 case b_H3:
600 if (break_class[i+1] == b_IN || break_class[i+1] == b_PO)
601 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
603 if (break_class[i] == b_PO)
605 switch (break_class[i+1])
607 case b_JL:
608 case b_JV:
609 case b_JT:
610 case b_H2:
611 case b_H3:
612 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
616 /* LB28 */
617 if ((break_class[i] == b_AL && break_class[i+1] == b_AL) ||
618 (break_class[i] == b_AL && break_class[i+1] == b_HL) ||
619 (break_class[i] == b_HL && break_class[i+1] == b_AL) ||
620 (break_class[i] == b_HL && break_class[i+1] == b_HL))
621 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
623 /* LB29 */
624 if ((break_class[i] == b_IS && break_class[i+1] == b_AL) ||
625 (break_class[i] == b_IS && break_class[i+1] == b_HL))
626 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
628 /* LB30 */
629 if ((break_class[i] == b_AL || break_class[i] == b_HL || break_class[i] == b_NU) &&
630 break_class[i+1] == b_OP)
631 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
632 if (break_class[i] == b_CP &&
633 (break_class[i+1] == b_AL || break_class[i] == b_HL || break_class[i] == b_NU))
634 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
636 /* LB30a */
637 if (break_class[i] == b_RI && break_class[i+1] == b_RI)
638 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
642 heap_free(break_class);
643 return S_OK;
646 static HRESULT WINAPI dwritetextanalyzer_QueryInterface(IDWriteTextAnalyzer2 *iface, REFIID riid, void **obj)
648 TRACE("(%s %p)\n", debugstr_guid(riid), obj);
650 if (IsEqualIID(riid, &IID_IDWriteTextAnalyzer2) ||
651 IsEqualIID(riid, &IID_IDWriteTextAnalyzer1) ||
652 IsEqualIID(riid, &IID_IDWriteTextAnalyzer) ||
653 IsEqualIID(riid, &IID_IUnknown))
655 *obj = iface;
656 return S_OK;
659 *obj = NULL;
660 return E_NOINTERFACE;
664 static ULONG WINAPI dwritetextanalyzer_AddRef(IDWriteTextAnalyzer2 *iface)
666 return 2;
669 static ULONG WINAPI dwritetextanalyzer_Release(IDWriteTextAnalyzer2 *iface)
671 return 1;
674 static HRESULT WINAPI dwritetextanalyzer_AnalyzeScript(IDWriteTextAnalyzer2 *iface,
675 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
677 const WCHAR *text;
678 HRESULT hr;
679 UINT32 len;
681 TRACE("(%p %u %u %p)\n", source, position, length, sink);
683 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, position, &text, &len);
684 if (FAILED(hr)) return hr;
686 return analyze_script(text, len, sink);
689 static HRESULT WINAPI dwritetextanalyzer_AnalyzeBidi(IDWriteTextAnalyzer2 *iface,
690 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
692 UINT8 *levels = NULL, *explicit = NULL;
693 UINT8 baselevel, level, explicit_level;
694 WCHAR *buff = NULL;
695 const WCHAR *text;
696 UINT32 len, pos, i;
697 HRESULT hr;
699 TRACE("(%p %u %u %p)\n", source, position, length, sink);
701 if (length == 0)
702 return S_OK;
704 /* get some, check for length */
705 text = NULL;
706 len = 0;
707 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, position, &text, &len);
708 if (FAILED(hr)) return hr;
710 if (len < length) {
711 UINT32 read;
713 buff = heap_alloc(length*sizeof(WCHAR));
714 if (!buff)
715 return E_OUTOFMEMORY;
716 memcpy(buff, text, len*sizeof(WCHAR));
717 read = len;
719 while (read < length && text) {
720 text = NULL;
721 len = 0;
722 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, read, &text, &len);
723 if (FAILED(hr))
724 goto done;
725 memcpy(&buff[read], text, min(len, length-read)*sizeof(WCHAR));
726 read += len;
729 text = buff;
732 levels = heap_alloc(length*sizeof(*levels));
733 explicit = heap_alloc(length*sizeof(*explicit));
735 if (!levels || !explicit) {
736 hr = E_OUTOFMEMORY;
737 goto done;
740 baselevel = IDWriteTextAnalysisSource_GetParagraphReadingDirection(source);
741 hr = bidi_computelevels(text, length, baselevel, explicit, levels);
742 if (FAILED(hr))
743 goto done;
745 level = levels[0];
746 explicit_level = explicit[0];
747 pos = 0;
748 for (i = 1; i < length; i++) {
749 if (levels[i] != level || explicit[i] != explicit_level) {
750 hr = IDWriteTextAnalysisSink_SetBidiLevel(sink, pos, i - pos, explicit_level, level);
751 if (FAILED(hr))
752 break;
753 level = levels[i];
754 explicit_level = explicit[i];
755 pos = i;
758 if (i == length - 1)
759 hr = IDWriteTextAnalysisSink_SetBidiLevel(sink, pos, length - pos, explicit_level, level);
762 done:
763 heap_free(explicit);
764 heap_free(levels);
765 heap_free(buff);
767 return hr;
770 static HRESULT WINAPI dwritetextanalyzer_AnalyzeNumberSubstitution(IDWriteTextAnalyzer2 *iface,
771 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
773 FIXME("(%p %u %u %p): stub\n", source, position, length, sink);
774 return E_NOTIMPL;
777 static HRESULT WINAPI dwritetextanalyzer_AnalyzeLineBreakpoints(IDWriteTextAnalyzer2 *iface,
778 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
780 DWRITE_LINE_BREAKPOINT *breakpoints = NULL;
781 WCHAR *buff = NULL;
782 const WCHAR *text;
783 HRESULT hr;
784 UINT32 len;
786 TRACE("(%p %u %u %p)\n", source, position, length, sink);
788 if (length == 0)
789 return S_OK;
791 /* get some, check for length */
792 text = NULL;
793 len = 0;
794 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, position, &text, &len);
795 if (FAILED(hr)) return hr;
797 if (len < length) {
798 UINT32 read;
800 buff = heap_alloc(length*sizeof(WCHAR));
801 if (!buff)
802 return E_OUTOFMEMORY;
803 memcpy(buff, text, len*sizeof(WCHAR));
804 read = len;
806 while (read < length && text) {
807 text = NULL;
808 len = 0;
809 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, read, &text, &len);
810 if (FAILED(hr))
811 goto done;
812 memcpy(&buff[read], text, min(len, length-read)*sizeof(WCHAR));
813 read += len;
816 text = buff;
819 breakpoints = heap_alloc(length*sizeof(*breakpoints));
820 if (!breakpoints) {
821 hr = E_OUTOFMEMORY;
822 goto done;
825 hr = analyze_linebreaks(text, length, breakpoints);
826 if (FAILED(hr))
827 goto done;
829 hr = IDWriteTextAnalysisSink_SetLineBreakpoints(sink, position, length, breakpoints);
831 done:
832 heap_free(breakpoints);
833 heap_free(buff);
835 return hr;
838 static HRESULT WINAPI dwritetextanalyzer_GetGlyphs(IDWriteTextAnalyzer2 *iface,
839 WCHAR const* text, UINT32 length, IDWriteFontFace* fontface, BOOL is_sideways,
840 BOOL is_rtl, DWRITE_SCRIPT_ANALYSIS const* analysis, WCHAR const* locale,
841 IDWriteNumberSubstitution* substitution, DWRITE_TYPOGRAPHIC_FEATURES const** features,
842 UINT32 const* feature_range_len, UINT32 feature_ranges, UINT32 max_glyph_count,
843 UINT16* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES* text_props, UINT16* glyph_indices,
844 DWRITE_SHAPING_GLYPH_PROPERTIES* glyph_props, UINT32* actual_glyph_count)
846 const struct dwritescript_properties *scriptprops;
847 struct scriptshaping_cache *cache;
848 WCHAR *string;
849 BOOL update_cluster;
850 UINT32 i, g;
851 HRESULT hr = S_OK;
852 UINT16 script;
854 TRACE("(%s:%u %p %d %d %p %s %p %p %p %u %u %p %p %p %p %p)\n", debugstr_wn(text, length),
855 length, fontface, is_sideways, is_rtl, analysis, debugstr_w(locale), substitution, features, feature_range_len,
856 feature_ranges, max_glyph_count, clustermap, text_props, glyph_indices, glyph_props, actual_glyph_count);
858 script = analysis->script > Script_LastId ? Script_Unknown : analysis->script;
860 if (max_glyph_count < length)
861 return E_NOT_SUFFICIENT_BUFFER;
863 if (substitution)
864 FIXME("number substitution is not supported.\n");
866 for (i = 0; i < length; i++) {
867 /* FIXME: set to better values */
868 glyph_props[i].justification = text[i] == ' ' ? SCRIPT_JUSTIFY_BLANK : SCRIPT_JUSTIFY_CHARACTER;
869 glyph_props[i].isClusterStart = 1;
870 glyph_props[i].isDiacritic = 0;
871 glyph_props[i].isZeroWidthSpace = 0;
872 glyph_props[i].reserved = 0;
874 /* FIXME: have the shaping engine set this */
875 text_props[i].isShapedAlone = 0;
876 text_props[i].reserved = 0;
878 clustermap[i] = i;
881 for (; i < max_glyph_count; i++) {
882 glyph_props[i].justification = SCRIPT_JUSTIFY_NONE;
883 glyph_props[i].isClusterStart = 0;
884 glyph_props[i].isDiacritic = 0;
885 glyph_props[i].isZeroWidthSpace = 0;
886 glyph_props[i].reserved = 0;
889 string = heap_alloc(sizeof(WCHAR)*length);
890 if (!string)
891 return E_OUTOFMEMORY;
893 for (i = 0, g = 0, update_cluster = FALSE; i < length; i++) {
894 UINT32 codepoint;
896 if (!update_cluster) {
897 codepoint = decode_surrogate_pair(text, i, length);
898 if (!codepoint) {
899 codepoint = is_rtl ? bidi_get_mirrored_char(text[i]) : text[i];
900 string[i] = codepoint;
902 else {
903 string[i] = text[i];
904 string[i+1] = text[i+1];
905 update_cluster = TRUE;
908 hr = IDWriteFontFace_GetGlyphIndices(fontface, &codepoint, 1, &glyph_indices[g]);
909 if (FAILED(hr))
910 goto done;
912 g++;
914 else {
915 INT32 k;
917 update_cluster = FALSE;
918 /* mark surrogate halves with same cluster */
919 clustermap[i] = clustermap[i-1];
920 /* update following clusters */
921 for (k = i + 1; k >= 0 && k < length; k++)
922 clustermap[k]--;
925 *actual_glyph_count = g;
927 hr = create_scriptshaping_cache(fontface, locale, &cache);
928 if (FAILED(hr))
929 goto done;
931 scriptprops = &dwritescripts_properties[script];
932 if (scriptprops->ops && scriptprops->ops->contextual_shaping) {
933 hr = scriptprops->ops->contextual_shaping(cache, is_rtl, string, length, max_glyph_count, clustermap, glyph_indices, actual_glyph_count);
934 if (FAILED(hr))
935 goto done;
938 /* FIXME: apply default features */
940 if (scriptprops->ops && scriptprops->ops->set_text_glyphs_props)
941 hr = scriptprops->ops->set_text_glyphs_props(cache, string, length, clustermap, glyph_indices, *actual_glyph_count, text_props, glyph_props);
942 else
943 hr = default_shaping_ops.set_text_glyphs_props(cache, string, length, clustermap, glyph_indices, *actual_glyph_count, text_props, glyph_props);
945 done:
946 release_scriptshaping_cache(cache);
947 heap_free(string);
949 return hr;
952 static HRESULT WINAPI dwritetextanalyzer_GetGlyphPlacements(IDWriteTextAnalyzer2 *iface,
953 WCHAR const* text, UINT16 const* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES* props,
954 UINT32 text_len, UINT16 const* glyph_indices, DWRITE_SHAPING_GLYPH_PROPERTIES const* glyph_props,
955 UINT32 glyph_count, IDWriteFontFace * font_face, FLOAT fontEmSize, BOOL is_sideways, BOOL is_rtl,
956 DWRITE_SCRIPT_ANALYSIS const* analysis, WCHAR const* locale, DWRITE_TYPOGRAPHIC_FEATURES const** features,
957 UINT32 const* feature_range_len, UINT32 feature_ranges, FLOAT* glyph_advances, DWRITE_GLYPH_OFFSET* glyph_offsets)
959 FIXME("(%s %p %p %u %p %p %u %p %f %d %d %p %s %p %p %u %p %p): stub\n", debugstr_w(text),
960 clustermap, props, text_len, glyph_indices, glyph_props, glyph_count, font_face, fontEmSize, is_sideways,
961 is_rtl, analysis, debugstr_w(locale), features, feature_range_len, feature_ranges, glyph_advances, glyph_offsets);
962 return E_NOTIMPL;
965 static HRESULT WINAPI dwritetextanalyzer_GetGdiCompatibleGlyphPlacements(IDWriteTextAnalyzer2 *iface,
966 WCHAR const* text, UINT16 const* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES* props,
967 UINT32 text_len, UINT16 const* glyph_indices, DWRITE_SHAPING_GLYPH_PROPERTIES const* glyph_props,
968 UINT32 glyph_count, IDWriteFontFace * font_face, FLOAT fontEmSize, FLOAT pixels_per_dip,
969 DWRITE_MATRIX const* transform, BOOL use_gdi_natural, BOOL is_sideways, BOOL is_rtl,
970 DWRITE_SCRIPT_ANALYSIS const* analysis, WCHAR const* locale, DWRITE_TYPOGRAPHIC_FEATURES const** features,
971 UINT32 const* feature_range_lengths, UINT32 feature_ranges, FLOAT* glyph_advances, DWRITE_GLYPH_OFFSET* glyph_offsets)
973 FIXME("(%s %p %p %u %p %p %u %p %f %f %p %d %d %d %p %s %p %p %u %p %p): stub\n", debugstr_w(text),
974 clustermap, props, text_len, glyph_indices, glyph_props, glyph_count, font_face, fontEmSize, pixels_per_dip,
975 transform, use_gdi_natural, is_sideways, is_rtl, analysis, debugstr_w(locale), features, feature_range_lengths,
976 feature_ranges, glyph_advances, glyph_offsets);
977 return E_NOTIMPL;
980 static HRESULT WINAPI dwritetextanalyzer1_ApplyCharacterSpacing(IDWriteTextAnalyzer2 *iface,
981 FLOAT leading_spacing, FLOAT trailing_spacing, FLOAT min_advance_width, UINT32 len,
982 UINT32 glyph_count, UINT16 const *clustermap, FLOAT const *advances, DWRITE_GLYPH_OFFSET const *offsets,
983 DWRITE_SHAPING_GLYPH_PROPERTIES const *props, FLOAT *modified_advances, DWRITE_GLYPH_OFFSET *modified_offsets)
985 FIXME("(%.2f %.2f %.2f %u %u %p %p %p %p %p %p): stub\n", leading_spacing, trailing_spacing, min_advance_width,
986 len, glyph_count, clustermap, advances, offsets, props, modified_advances, modified_offsets);
987 return E_NOTIMPL;
990 static HRESULT WINAPI dwritetextanalyzer1_GetBaseline(IDWriteTextAnalyzer2 *iface, IDWriteFontFace *face,
991 DWRITE_BASELINE baseline, BOOL vertical, BOOL is_simulation_allowed, DWRITE_SCRIPT_ANALYSIS sa,
992 const WCHAR *localeName, INT32 *baseline_coord, BOOL *exists)
994 FIXME("(%p %d %d %u %s %p %p): stub\n", face, vertical, is_simulation_allowed, sa.script, debugstr_w(localeName),
995 baseline_coord, exists);
996 return E_NOTIMPL;
999 static HRESULT WINAPI dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation(IDWriteTextAnalyzer2 *iface,
1000 IDWriteTextAnalysisSource1* source, UINT32 text_pos, UINT32 len, IDWriteTextAnalysisSink1 *sink)
1002 FIXME("(%p %u %u %p): stub\n", source, text_pos, len, sink);
1003 return E_NOTIMPL;
1006 static HRESULT WINAPI dwritetextanalyzer1_GetGlyphOrientationTransform(IDWriteTextAnalyzer2 *iface,
1007 DWRITE_GLYPH_ORIENTATION_ANGLE angle, BOOL is_sideways, DWRITE_MATRIX *transform)
1009 FIXME("(%d %d %p): stub\n", angle, is_sideways, transform);
1010 return E_NOTIMPL;
1013 static HRESULT WINAPI dwritetextanalyzer1_GetScriptProperties(IDWriteTextAnalyzer2 *iface, DWRITE_SCRIPT_ANALYSIS sa,
1014 DWRITE_SCRIPT_PROPERTIES *props)
1016 TRACE("(%u %p)\n", sa.script, props);
1018 if (sa.script > Script_LastId)
1019 return E_INVALIDARG;
1021 *props = dwritescripts_properties[sa.script].props;
1022 return S_OK;
1025 static inline BOOL is_char_from_simple_script(WCHAR c)
1027 if (IS_HIGH_SURROGATE(c) || IS_LOW_SURROGATE(c))
1028 return FALSE;
1029 else {
1030 UINT16 script = get_char_script(c);
1031 return !dwritescripts_properties[script].is_complex;
1035 static HRESULT WINAPI dwritetextanalyzer1_GetTextComplexity(IDWriteTextAnalyzer2 *iface, const WCHAR *text,
1036 UINT32 len, IDWriteFontFace *face, BOOL *is_simple, UINT32 *len_read, UINT16 *indices)
1038 HRESULT hr = S_OK;
1039 int i;
1041 TRACE("(%s:%u %p %p %p %p)\n", debugstr_wn(text, len), len, face, is_simple, len_read, indices);
1043 *is_simple = FALSE;
1044 *len_read = 0;
1046 if (!face)
1047 return E_INVALIDARG;
1049 if (len == 0) {
1050 *is_simple = TRUE;
1051 return S_OK;
1054 *is_simple = text[0] && is_char_from_simple_script(text[0]);
1055 for (i = 1; i < len && text[i]; i++) {
1056 if (is_char_from_simple_script(text[i])) {
1057 if (!*is_simple)
1058 break;
1060 else
1061 *is_simple = FALSE;
1064 *len_read = i;
1066 /* fetch indices */
1067 if (*is_simple && indices) {
1068 UINT32 *codepoints = heap_alloc(*len_read*sizeof(UINT32));
1069 if (!codepoints)
1070 return E_OUTOFMEMORY;
1072 for (i = 0; i < *len_read; i++)
1073 codepoints[i] = text[i];
1075 hr = IDWriteFontFace_GetGlyphIndices(face, codepoints, *len_read, indices);
1076 heap_free(codepoints);
1079 return hr;
1082 static HRESULT WINAPI dwritetextanalyzer1_GetJustificationOpportunities(IDWriteTextAnalyzer2 *iface,
1083 IDWriteFontFace *face, FLOAT font_em_size, DWRITE_SCRIPT_ANALYSIS sa, UINT32 length, UINT32 glyph_count,
1084 const WCHAR *text, const UINT16 *clustermap, const DWRITE_SHAPING_GLYPH_PROPERTIES *prop, DWRITE_JUSTIFICATION_OPPORTUNITY *jo)
1086 FIXME("(%p %.2f %u %u %u %s %p %p %p): stub\n", face, font_em_size, sa.script, length, glyph_count,
1087 debugstr_w(text), clustermap, prop, jo);
1088 return E_NOTIMPL;
1091 static HRESULT WINAPI dwritetextanalyzer1_JustifyGlyphAdvances(IDWriteTextAnalyzer2 *iface,
1092 FLOAT width, UINT32 glyph_count, const DWRITE_JUSTIFICATION_OPPORTUNITY *jo, const FLOAT *advances,
1093 const DWRITE_GLYPH_OFFSET *offsets, FLOAT *justifiedadvances, DWRITE_GLYPH_OFFSET *justifiedoffsets)
1095 FIXME("(%.2f %u %p %p %p %p %p): stub\n", width, glyph_count, jo, advances, offsets, justifiedadvances,
1096 justifiedoffsets);
1097 return E_NOTIMPL;
1100 static HRESULT WINAPI dwritetextanalyzer1_GetJustifiedGlyphs(IDWriteTextAnalyzer2 *iface,
1101 IDWriteFontFace *face, FLOAT font_em_size, DWRITE_SCRIPT_ANALYSIS sa, UINT32 length,
1102 UINT32 glyph_count, UINT32 max_glyphcount, const UINT16 *clustermap, const UINT16 *indices,
1103 const FLOAT *advances, const FLOAT *justifiedadvances, const DWRITE_GLYPH_OFFSET *justifiedoffsets,
1104 const DWRITE_SHAPING_GLYPH_PROPERTIES *prop, UINT32 *actual_count, UINT16 *modified_clustermap,
1105 UINT16 *modified_indices, FLOAT *modified_advances, DWRITE_GLYPH_OFFSET *modified_offsets)
1107 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,
1108 length, glyph_count, max_glyphcount, clustermap, indices, advances, justifiedadvances, justifiedoffsets,
1109 prop, actual_count, modified_clustermap, modified_indices, modified_advances, modified_offsets);
1110 return E_NOTIMPL;
1113 static HRESULT WINAPI dwritetextanalyzer2_GetGlyphOrientationTransform(IDWriteTextAnalyzer2 *iface,
1114 DWRITE_GLYPH_ORIENTATION_ANGLE angle, BOOL is_sideways, FLOAT originX, FLOAT originY, DWRITE_MATRIX *transform)
1116 FIXME("(%d %d %.2f %.2f %p): stub\n", angle, is_sideways, originX, originY, transform);
1117 return E_NOTIMPL;
1120 static HRESULT WINAPI dwritetextanalyzer2_GetTypographicFeatures(IDWriteTextAnalyzer2 *iface,
1121 IDWriteFontFace *face, DWRITE_SCRIPT_ANALYSIS sa, const WCHAR *localeName,
1122 UINT32 max_tagcount, UINT32 *actual_tagcount, DWRITE_FONT_FEATURE_TAG *tags)
1124 FIXME("(%p %u %s %u %p %p): stub\n", face, sa.script, debugstr_w(localeName), max_tagcount, actual_tagcount,
1125 tags);
1126 return E_NOTIMPL;
1129 static HRESULT WINAPI dwritetextanalyzer2_CheckTypographicFeature(IDWriteTextAnalyzer2 *iface,
1130 IDWriteFontFace *face, DWRITE_SCRIPT_ANALYSIS sa, const WCHAR *localeName,
1131 DWRITE_FONT_FEATURE_TAG feature, UINT32 glyph_count, const UINT16 *indices, UINT8 *feature_applies)
1133 FIXME("(%p %u %s %x %u %p %p): stub\n", face, sa.script, debugstr_w(localeName), feature, glyph_count, indices,
1134 feature_applies);
1135 return E_NOTIMPL;
1138 static const struct IDWriteTextAnalyzer2Vtbl textanalyzervtbl = {
1139 dwritetextanalyzer_QueryInterface,
1140 dwritetextanalyzer_AddRef,
1141 dwritetextanalyzer_Release,
1142 dwritetextanalyzer_AnalyzeScript,
1143 dwritetextanalyzer_AnalyzeBidi,
1144 dwritetextanalyzer_AnalyzeNumberSubstitution,
1145 dwritetextanalyzer_AnalyzeLineBreakpoints,
1146 dwritetextanalyzer_GetGlyphs,
1147 dwritetextanalyzer_GetGlyphPlacements,
1148 dwritetextanalyzer_GetGdiCompatibleGlyphPlacements,
1149 dwritetextanalyzer1_ApplyCharacterSpacing,
1150 dwritetextanalyzer1_GetBaseline,
1151 dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation,
1152 dwritetextanalyzer1_GetGlyphOrientationTransform,
1153 dwritetextanalyzer1_GetScriptProperties,
1154 dwritetextanalyzer1_GetTextComplexity,
1155 dwritetextanalyzer1_GetJustificationOpportunities,
1156 dwritetextanalyzer1_JustifyGlyphAdvances,
1157 dwritetextanalyzer1_GetJustifiedGlyphs,
1158 dwritetextanalyzer2_GetGlyphOrientationTransform,
1159 dwritetextanalyzer2_GetTypographicFeatures,
1160 dwritetextanalyzer2_CheckTypographicFeature
1163 static IDWriteTextAnalyzer2 textanalyzer = { &textanalyzervtbl };
1165 HRESULT get_textanalyzer(IDWriteTextAnalyzer **ret)
1167 *ret = (IDWriteTextAnalyzer*)&textanalyzer;
1168 return S_OK;
1171 static HRESULT WINAPI dwritenumbersubstitution_QueryInterface(IDWriteNumberSubstitution *iface, REFIID riid, void **obj)
1173 struct dwrite_numbersubstitution *This = impl_from_IDWriteNumberSubstitution(iface);
1175 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
1177 if (IsEqualIID(riid, &IID_IDWriteNumberSubstitution) ||
1178 IsEqualIID(riid, &IID_IUnknown))
1180 *obj = iface;
1181 IDWriteNumberSubstitution_AddRef(iface);
1182 return S_OK;
1185 *obj = NULL;
1187 return E_NOINTERFACE;
1190 static ULONG WINAPI dwritenumbersubstitution_AddRef(IDWriteNumberSubstitution *iface)
1192 struct dwrite_numbersubstitution *This = impl_from_IDWriteNumberSubstitution(iface);
1193 ULONG ref = InterlockedIncrement(&This->ref);
1194 TRACE("(%p)->(%d)\n", This, ref);
1195 return ref;
1198 static ULONG WINAPI dwritenumbersubstitution_Release(IDWriteNumberSubstitution *iface)
1200 struct dwrite_numbersubstitution *This = impl_from_IDWriteNumberSubstitution(iface);
1201 ULONG ref = InterlockedDecrement(&This->ref);
1203 TRACE("(%p)->(%d)\n", This, ref);
1205 if (!ref) {
1206 heap_free(This->locale);
1207 heap_free(This);
1210 return ref;
1213 static const struct IDWriteNumberSubstitutionVtbl numbersubstitutionvtbl = {
1214 dwritenumbersubstitution_QueryInterface,
1215 dwritenumbersubstitution_AddRef,
1216 dwritenumbersubstitution_Release
1219 HRESULT create_numbersubstitution(DWRITE_NUMBER_SUBSTITUTION_METHOD method, const WCHAR *locale,
1220 BOOL ignore_user_override, IDWriteNumberSubstitution **ret)
1222 struct dwrite_numbersubstitution *substitution;
1224 *ret = NULL;
1226 if ((UINT32)method > DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL)
1227 return E_INVALIDARG;
1229 if (method != DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE && !IsValidLocaleName(locale))
1230 return E_INVALIDARG;
1232 substitution = heap_alloc(sizeof(*substitution));
1233 if (!substitution)
1234 return E_OUTOFMEMORY;
1236 substitution->IDWriteNumberSubstitution_iface.lpVtbl = &numbersubstitutionvtbl;
1237 substitution->ref = 1;
1238 substitution->ignore_user_override = ignore_user_override;
1239 substitution->method = method;
1240 substitution->locale = heap_strdupW(locale);
1241 if (locale && !substitution->locale) {
1242 heap_free(substitution);
1243 return E_OUTOFMEMORY;
1246 *ret = &substitution->IDWriteNumberSubstitution_iface;
1247 return S_OK;