dwrite: Introduce cache to be used by shaping engines.
[wine.git] / dlls / dwrite / analyzer.c
blobe2fac55383fa1d8632eba974e7ca619c116253e3
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.h"
25 #include "dwrite_2.h"
26 #include "dwrite_private.h"
27 #include "scripts.h"
29 WINE_DEFAULT_DEBUG_CHANNEL(dwrite);
31 extern const unsigned short wine_linebreak_table[];
32 extern const unsigned short wine_scripts_table[];
34 struct dwritescript_properties {
35 DWRITE_SCRIPT_PROPERTIES props;
36 BOOL is_complex;
37 const struct scriptshaping_ops *ops;
40 /* NOTE: keep this array synced with script ids from scripts.h */
41 static const struct dwritescript_properties dwritescripts_properties[Script_LastId+1] = {
42 { /* Zzzz */ { 0x7a7a7a5a, 999, 15, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
43 { /* Zyyy */ { 0x7979795a, 998, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
44 { /* Arab */ { 0x62617241, 160, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, TRUE },
45 { /* Armn */ { 0x6e6d7241, 230, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
46 { /* Avst */ { 0x74737641, 134, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
47 { /* Bali */ { 0x696c6142, 360, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 } },
48 { /* Bamu */ { 0x756d6142, 435, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 } },
49 { /* Bass */ { 0x73736142, 259, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
50 { /* Batk */ { 0x6b746142, 365, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
51 { /* Beng */ { 0x676e6542, 325, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, TRUE },
52 { /* Bopo */ { 0x6f706f42, 285, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 } },
53 { /* Brah */ { 0x68617242, 300, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
54 { /* Brai */ { 0x69617242, 570, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, TRUE },
55 { /* Bugi */ { 0x69677542, 367, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
56 { /* Buhd */ { 0x64687542, 372, 8, 0x0020, 0, 0, 1, 0, 0, 0, 0 } },
57 { /* Cans */ { 0x736e6143, 440, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, TRUE },
58 { /* Cari */ { 0x69726143, 201, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 } },
59 { /* Aghb */ { 0x62686741, 239, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
60 { /* Cakm */ { 0x6d6b6143, 349, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
61 { /* Cham */ { 0x6d616843, 358, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 } },
62 { /* Cher */ { 0x72656843, 445, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, TRUE },
63 { /* Copt */ { 0x74706f43, 204, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
64 { /* Xsux */ { 0x78757358, 20, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 } },
65 { /* Cprt */ { 0x74727043, 403, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 } },
66 { /* Cyrl */ { 0x6c727943, 220, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
67 { /* Dsrt */ { 0x74727344, 250, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, TRUE },
68 { /* Deva */ { 0x61766544, 315, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, TRUE },
69 { /* Dupl */ { 0x6c707544, 755, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
70 { /* Egyp */ { 0x70796745, 50, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 } },
71 { /* Elba */ { 0x61626c45, 226, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
72 { /* Ethi */ { 0x69687445, 430, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, TRUE },
73 { /* Geor */ { 0x726f6547, 240, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
74 { /* Glag */ { 0x67616c47, 225, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
75 { /* Goth */ { 0x68746f47, 206, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
76 { /* Gran */ { 0x6e617247, 343, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
77 { /* Grek */ { 0x6b657247, 200, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
78 { /* Gujr */ { 0x726a7547, 320, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, TRUE },
79 { /* Guru */ { 0x75727547, 310, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 }, TRUE },
80 { /* Hani */ { 0x696e6148, 500, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 } },
81 { /* Hang */ { 0x676e6148, 286, 8, 0x0020, 1, 1, 1, 1, 0, 0, 0 }, TRUE },
82 { /* Hano */ { 0x6f6e6148, 371, 8, 0x0020, 0, 0, 1, 0, 0, 0, 0 } },
83 { /* Hebr */ { 0x72626548, 125, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, TRUE },
84 { /* Hira */ { 0x61726948, 410, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 } },
85 { /* Armi */ { 0x696d7241, 124, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
86 { /* Phli */ { 0x696c6850, 131, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
87 { /* Prti */ { 0x69747250, 130, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
88 { /* Java */ { 0x6176614a, 361, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 } },
89 { /* Kthi */ { 0x6968744b, 317, 15, 0x0020, 1, 1, 0, 0, 0, 1, 0 } },
90 { /* Knda */ { 0x61646e4b, 345, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, TRUE },
91 { /* Kana */ { 0x616e614b, 411, 8, 0x0020, 0, 0, 1, 1, 0, 0, 0 } },
92 { /* Kali */ { 0x696c614b, 357, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
93 { /* Khar */ { 0x7261684b, 305, 15, 0x0020, 1, 0, 1, 0, 0, 0, 0 } },
94 { /* Khmr */ { 0x726d684b, 355, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, TRUE },
95 { /* Khoj */ { 0x6a6f684b, 322, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
96 { /* Sind */ { 0x646e6953, 318, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
97 { /* Laoo */ { 0x6f6f614c, 356, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, TRUE },
98 { /* Latn */ { 0x6e74614c, 215, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, FALSE, &latn_shaping_ops },
99 { /* Lepc */ { 0x6370654c, 335, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 } },
100 { /* Limb */ { 0x626d694c, 336, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 } },
101 { /* Lina */ { 0x616e694c, 400, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
102 { /* Linb */ { 0x626e694c, 401, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 } },
103 { /* Lisu */ { 0x7573694c, 399, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
104 { /* Lyci */ { 0x6963794c, 202, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
105 { /* Lydi */ { 0x6964794c, 116, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
106 { /* Mahj */ { 0x6a68614d, 314, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
107 { /* Mlym */ { 0x6d796c4d, 347, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, TRUE },
108 { /* Mand */ { 0x646e614d, 140, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 } },
109 { /* Mani */ { 0x696e614d, 139, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
110 { /* Mtei */ { 0x6965744d, 337, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 } },
111 { /* Mend */ { 0x646e654d, 438, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
112 { /* Merc */ { 0x6372654d, 101, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
113 { /* Mero */ { 0x6f72654d, 100, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
114 { /* Plrd */ { 0x64726c50, 282, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
115 { /* Modi */ { 0x69646f4d, 324, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
116 { /* Mong */ { 0x676e6f4d, 145, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, TRUE },
117 { /* Mroo */ { 0x6f6f724d, 199, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
118 { /* Mymr */ { 0x726d794d, 350, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, TRUE },
119 { /* Nbat */ { 0x7461624e, 159, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
120 { /* Talu */ { 0x756c6154, 354, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, TRUE },
121 { /* Nkoo */ { 0x6f6f6b4e, 165, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, TRUE },
122 { /* Ogam */ { 0x6d61674f, 212, 1, 0x1680, 0, 1, 0, 0, 0, 1, 0 }, TRUE },
123 { /* Olck */ { 0x6b636c4f, 261, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
124 { /* Ital */ { 0x6c617449, 210, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
125 { /* Narb */ { 0x6272614e, 106, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
126 { /* Perm */ { 0x6d726550, 227, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
127 { /* Xpeo */ { 0x6f657058, 30, 1, 0x0020, 0, 1, 1, 1, 0, 0, 0 }, TRUE },
128 { /* Sarb */ { 0x62726153, 105, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
129 { /* Orkh */ { 0x686b724f, 175, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
130 { /* Orya */ { 0x6179724f, 327, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, TRUE },
131 { /* Osma */ { 0x616d734f, 260, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, TRUE },
132 { /* Hmng */ { 0x676e6d48, 450, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
133 { /* Palm */ { 0x6d6c6150, 126, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
134 { /* Pauc */ { 0x63756150, 263, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
135 { /* Phag */ { 0x67616850, 331, 8, 0x0020, 0, 1, 0, 0, 0, 1, 1 }, TRUE },
136 { /* Phnx */ { 0x786e6850, 115, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 } },
137 { /* Phlp */ { 0x706c6850, 132, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
138 { /* Rjng */ { 0x676e6a52, 363, 8, 0x0020, 1, 0, 1, 0, 0, 0, 0 } },
139 { /* Runr */ { 0x726e7552, 211, 1, 0x0020, 0, 0, 1, 0, 0, 0, 0 }, TRUE },
140 { /* Samr */ { 0x726d6153, 123, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
141 { /* Saur */ { 0x72756153, 344, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 } },
142 { /* Shrd */ { 0x64726853, 319, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
143 { /* Shaw */ { 0x77616853, 281, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
144 { /* Sidd */ { 0x64646953, 302, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
145 { /* Sinh */ { 0x686e6953, 348, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, TRUE },
146 { /* Sora */ { 0x61726f53, 398, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
147 { /* Sund */ { 0x646e7553, 362, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 } },
148 { /* Sylo */ { 0x6f6c7953, 316, 8, 0x0020, 1, 1, 0, 0, 0, 1, 0 } },
149 { /* Syrc */ { 0x63727953, 135, 8, 0x0640, 0, 1, 0, 0, 0, 1, 1 }, TRUE },
150 { /* Tglg */ { 0x676c6754, 370, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
151 { /* Tagb */ { 0x62676154, 373, 8, 0x0020, 0, 1, 1, 0, 0, 0, 0 } },
152 { /* Tale */ { 0x656c6154, 353, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, TRUE },
153 { /* Lana */ { 0x616e614c, 351, 8, 0x0020, 1, 0, 1, 0, 0, 0, 0 } },
154 { /* Tavt */ { 0x74766154, 359, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 } },
155 { /* Takr */ { 0x726b6154, 321, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
156 { /* Taml */ { 0x6c6d6154, 346, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, TRUE },
157 { /* Telu */ { 0x756c6554, 340, 15, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, TRUE },
158 { /* Thaa */ { 0x61616854, 170, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, TRUE },
159 { /* Thai */ { 0x69616854, 352, 8, 0x0020, 1, 0, 1, 0, 1, 0, 0 }, TRUE },
160 { /* Tibt */ { 0x74626954, 330, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, TRUE },
161 { /* Tfng */ { 0x676e6654, 120, 8, 0x0020, 1, 1, 1, 0, 0, 0, 0 }, TRUE },
162 { /* Tirh */ { 0x68726954, 326, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
163 { /* Ugar */ { 0x72616755, 40, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 } },
164 { /* Vaii */ { 0x69696156, 470, 1, 0x0020, 0, 1, 1, 0, 0, 0, 0 }, TRUE },
165 { /* Wara */ { 0x61726157, 262, 1, 0x0020, 0, 0, 0, 0, 0, 0, 0 } },
166 { /* Yiii */ { 0x69696959, 460, 1, 0x0020, 0, 0, 1, 1, 0, 0, 0 }, TRUE }
169 struct dwrite_numbersubstitution {
170 IDWriteNumberSubstitution IDWriteNumberSubstitution_iface;
171 LONG ref;
173 DWRITE_NUMBER_SUBSTITUTION_METHOD method;
174 WCHAR *locale;
175 BOOL ignore_user_override;
178 static inline struct dwrite_numbersubstitution *impl_from_IDWriteNumberSubstitution(IDWriteNumberSubstitution *iface)
180 return CONTAINING_RECORD(iface, struct dwrite_numbersubstitution, IDWriteNumberSubstitution_iface);
183 static inline UINT32 decode_surrogate_pair(const WCHAR *str, UINT32 index, UINT32 end)
185 if (index < end-1 && IS_SURROGATE_PAIR(str[index], str[index+1])) {
186 UINT32 ch = 0x10000 + ((str[index] - 0xd800) << 10) + (str[index+1] - 0xdc00);
187 TRACE("surrogate pair (%x %x) => %x\n", str[index], str[index+1], ch);
188 return ch;
190 return 0;
193 static inline UINT16 get_char_script(WCHAR c)
195 UINT16 script = get_table_entry(wine_scripts_table, c);
196 if (script == Script_Unknown) {
197 WORD type;
198 if (GetStringTypeW(CT_CTYPE1, &c, 1, &type) && (type & C1_CNTRL))
199 script = Script_Common;
201 return script;
204 static HRESULT analyze_script(const WCHAR *text, UINT32 len, IDWriteTextAnalysisSink *sink)
206 DWRITE_SCRIPT_ANALYSIS sa;
207 UINT32 pos, i, length;
209 if (!len) return S_OK;
211 sa.script = get_char_script(*text);
213 pos = 0;
214 length = 1;
216 for (i = 1; i < len; i++)
218 UINT16 script = get_char_script(text[i]);
220 /* Unknown type is ignored when preceded or followed by another script */
221 if (sa.script == Script_Unknown) sa.script = script;
222 if (script == Script_Unknown && sa.script != Script_Common) script = sa.script;
223 /* this is a length of a sequence to be reported next */
224 if (sa.script == script) length++;
226 if (sa.script != script)
228 HRESULT hr;
230 sa.shapes = sa.script != Script_Common ? DWRITE_SCRIPT_SHAPES_DEFAULT : DWRITE_SCRIPT_SHAPES_NO_VISUAL;
231 hr = IDWriteTextAnalysisSink_SetScriptAnalysis(sink, pos, length, &sa);
232 if (FAILED(hr)) return hr;
233 pos = i;
234 length = 1;
235 sa.script = script;
239 /* 1 length case or normal completion call */
240 sa.shapes = sa.script != Script_Common ? DWRITE_SCRIPT_SHAPES_DEFAULT : DWRITE_SCRIPT_SHAPES_NO_VISUAL;
241 return IDWriteTextAnalysisSink_SetScriptAnalysis(sink, pos, length, &sa);
244 struct linebreaking_state {
245 DWRITE_LINE_BREAKPOINT *breakpoints;
246 UINT32 count;
249 enum BreakConditionLocation {
250 BreakConditionBefore,
251 BreakConditionAfter
254 enum linebreaking_classes {
255 b_BK = 1,
256 b_CR,
257 b_LF,
258 b_CM,
259 b_SG,
260 b_GL,
261 b_CB,
262 b_SP,
263 b_ZW,
264 b_NL,
265 b_WJ,
266 b_JL,
267 b_JV,
268 b_JT,
269 b_H2,
270 b_H3,
271 b_XX,
272 b_OP,
273 b_CL,
274 b_CP,
275 b_QU,
276 b_NS,
277 b_EX,
278 b_SY,
279 b_IS,
280 b_PR,
281 b_PO,
282 b_NU,
283 b_AL,
284 b_ID,
285 b_IN,
286 b_HY,
287 b_BB,
288 b_BA,
289 b_SA,
290 b_AI,
291 b_B2,
292 b_HL,
293 b_CJ,
294 b_RI
297 /* "Can break" is a weak condition, stronger "may not break" and "must break" override it. Initially all conditions are
298 set to "can break" and could only be changed once. */
299 static inline void set_break_condition(UINT32 pos, enum BreakConditionLocation location, DWRITE_BREAK_CONDITION condition,
300 struct linebreaking_state *state)
302 if (location == BreakConditionBefore) {
303 if (state->breakpoints[pos].breakConditionBefore != DWRITE_BREAK_CONDITION_CAN_BREAK)
304 return;
305 state->breakpoints[pos].breakConditionBefore = condition;
306 if (pos > 0)
307 state->breakpoints[pos-1].breakConditionAfter = condition;
309 else {
310 if (state->breakpoints[pos].breakConditionAfter != DWRITE_BREAK_CONDITION_CAN_BREAK)
311 return;
312 state->breakpoints[pos].breakConditionAfter = condition;
313 if (pos + 1 < state->count)
314 state->breakpoints[pos+1].breakConditionBefore = condition;
318 static HRESULT analyze_linebreaks(const WCHAR *text, UINT32 count, DWRITE_LINE_BREAKPOINT *breakpoints)
320 struct linebreaking_state state;
321 short *break_class;
322 int i, j;
324 break_class = heap_alloc(count*sizeof(short));
325 if (!break_class)
326 return E_OUTOFMEMORY;
328 state.breakpoints = breakpoints;
329 state.count = count;
331 /* LB31 - allow breaks everywhere. It will be overridden if needed as
332 other rules dictate. */
333 for (i = 0; i < count; i++)
335 break_class[i] = get_table_entry(wine_linebreak_table, text[i]);
337 breakpoints[i].breakConditionBefore = DWRITE_BREAK_CONDITION_CAN_BREAK;
338 breakpoints[i].breakConditionAfter = DWRITE_BREAK_CONDITION_CAN_BREAK;
339 breakpoints[i].isWhitespace = break_class[i] == b_BK || break_class[i] == b_ZW || break_class[i] == b_SP || isspaceW(text[i]);
340 breakpoints[i].isSoftHyphen = FALSE;
341 breakpoints[i].padding = 0;
343 /* LB1 - resolve some classes. TODO: use external algorithms for these classes. */
344 switch (break_class[i])
346 case b_AI:
347 case b_SA:
348 case b_SG:
349 case b_XX:
350 break_class[i] = b_AL;
351 break;
352 case b_CJ:
353 break_class[i] = b_NS;
354 break;
358 /* LB2 - never break at the start */
359 set_break_condition(0, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
360 /* LB3 - always break at the end. This one is ignored. */
362 for (i = 0; i < count; i++)
364 switch (break_class[i])
366 /* LB4 - LB6 */
367 case b_CR:
368 /* LB5 - don't break CR x LF */
369 if (i < count-1 && break_class[i+1] == b_LF)
371 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
372 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
373 break;
375 case b_LF:
376 case b_NL:
377 case b_BK:
378 /* LB4 - LB5 - always break after hard breaks */
379 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MUST_BREAK, &state);
380 /* LB6 - do not break before hard breaks */
381 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
382 break;
383 /* LB7 - do not break before spaces */
384 case b_SP:
385 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
386 break;
387 case b_ZW:
388 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
389 /* LB8 - break before character after zero-width space, skip spaces in-between */
390 while (i < count-1 && break_class[i+1] == b_SP)
391 i++;
392 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
393 break;
397 /* LB9 - LB10 */
398 for (i = 0; i < count; i++)
400 if (break_class[i] == b_CM)
402 if (i > 0)
404 switch (break_class[i-1])
406 case b_SP:
407 case b_BK:
408 case b_CR:
409 case b_LF:
410 case b_NL:
411 case b_ZW:
412 break_class[i] = b_AL;
413 break;
414 default:
415 break_class[i] = break_class[i-1];
418 else break_class[i] = b_AL;
422 for (i = 0; i < count; i++)
424 switch (break_class[i])
426 /* LB11 - don't break before and after word joiner */
427 case b_WJ:
428 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
429 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
430 break;
431 /* LB12 - don't break after glue */
432 case b_GL:
433 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
434 /* LB12a */
435 if (i > 0)
437 if (break_class[i-1] != b_SP && break_class[i-1] != b_BA && break_class[i-1] != b_HY)
438 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
440 break;
441 /* LB13 */
442 case b_CL:
443 case b_CP:
444 case b_EX:
445 case b_IS:
446 case b_SY:
447 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
448 break;
449 /* LB14 */
450 case b_OP:
451 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
452 while (i < count-1 && break_class[i+1] == b_SP) {
453 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
454 i++;
456 break;
457 /* LB15 */
458 case b_QU:
459 j = i+1;
460 while (j < count-1 && break_class[j] == b_SP)
461 j++;
462 if (break_class[j] == b_OP)
463 for (; j > i; j--)
464 set_break_condition(j, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
465 break;
466 /* LB16 */
467 case b_NS:
468 j = i-1;
469 while(j > 0 && break_class[j] == b_SP)
470 j--;
471 if (break_class[j] == b_CL || break_class[j] == b_CP)
472 for (j++; j <= i; j++)
473 set_break_condition(j, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
474 break;
475 /* LB17 */
476 case b_B2:
477 j = i+1;
478 while (j < count && break_class[j] == b_SP)
479 j++;
480 if (break_class[j] == b_B2)
481 for (; j > i; j--)
482 set_break_condition(j, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
483 break;
487 for (i = 0; i < count; i++)
489 switch(break_class[i])
491 /* LB18 - break is allowed after space */
492 case b_SP:
493 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
494 break;
495 /* LB19 - don't break before or after quotation mark */
496 case b_QU:
497 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
498 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
499 break;
500 /* LB20 */
501 case b_CB:
502 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
503 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_CAN_BREAK, &state);
504 break;
505 /* LB21 */
506 case b_BA:
507 case b_HY:
508 case b_NS:
509 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
510 break;
511 case b_BB:
512 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
513 break;
514 /* LB21a */
515 case b_HL:
516 if (i < count-2)
517 switch (break_class[i+1])
519 case b_HY:
520 case b_BA:
521 set_break_condition(i+1, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
523 break;
524 /* LB22 */
525 case b_IN:
526 if (i > 0)
528 switch (break_class[i-1])
530 case b_AL:
531 case b_HL:
532 case b_ID:
533 case b_IN:
534 case b_NU:
535 set_break_condition(i, BreakConditionBefore, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
538 break;
541 if (i < count-1)
543 /* LB23 */
544 if ((break_class[i] == b_ID && break_class[i+1] == b_PO) ||
545 (break_class[i] == b_AL && break_class[i+1] == b_NU) ||
546 (break_class[i] == b_HL && break_class[i+1] == b_NU) ||
547 (break_class[i] == b_NU && break_class[i+1] == b_AL) ||
548 (break_class[i] == b_NU && break_class[i+1] == b_HL))
549 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
550 /* LB24 */
551 if ((break_class[i] == b_PR && break_class[i+1] == b_ID) ||
552 (break_class[i] == b_PR && break_class[i+1] == b_AL) ||
553 (break_class[i] == b_PR && break_class[i+1] == b_HL) ||
554 (break_class[i] == b_PO && break_class[i+1] == b_AL) ||
555 (break_class[i] == b_PO && break_class[i+1] == b_HL))
556 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
558 /* LB25 */
559 if ((break_class[i] == b_CL && break_class[i+1] == b_PO) ||
560 (break_class[i] == b_CP && break_class[i+1] == b_PO) ||
561 (break_class[i] == b_CL && break_class[i+1] == b_PR) ||
562 (break_class[i] == b_CP && break_class[i+1] == b_PR) ||
563 (break_class[i] == b_NU && break_class[i+1] == b_PO) ||
564 (break_class[i] == b_NU && break_class[i+1] == b_PR) ||
565 (break_class[i] == b_PO && break_class[i+1] == b_OP) ||
566 (break_class[i] == b_PO && break_class[i+1] == b_NU) ||
567 (break_class[i] == b_PR && break_class[i+1] == b_OP) ||
568 (break_class[i] == b_PR && break_class[i+1] == b_NU) ||
569 (break_class[i] == b_HY && break_class[i+1] == b_NU) ||
570 (break_class[i] == b_IS && break_class[i+1] == b_NU) ||
571 (break_class[i] == b_NU && break_class[i+1] == b_NU) ||
572 (break_class[i] == b_SY && break_class[i+1] == b_NU))
573 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
575 /* LB26 */
576 if (break_class[i] == b_JL)
578 switch (break_class[i+1])
580 case b_JL:
581 case b_JV:
582 case b_H2:
583 case b_H3:
584 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
587 if ((break_class[i] == b_JV || break_class[i] == b_H2) &&
588 (break_class[i+1] == b_JV || break_class[i+1] == b_JT))
589 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
590 if ((break_class[i] == b_JT || break_class[i] == b_H3) &&
591 break_class[i+1] == b_JT)
592 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
594 /* LB27 */
595 switch (break_class[i])
597 case b_JL:
598 case b_JV:
599 case b_JT:
600 case b_H2:
601 case b_H3:
602 if (break_class[i+1] == b_IN || break_class[i+1] == b_PO)
603 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
605 if (break_class[i] == b_PO)
607 switch (break_class[i+1])
609 case b_JL:
610 case b_JV:
611 case b_JT:
612 case b_H2:
613 case b_H3:
614 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
618 /* LB28 */
619 if ((break_class[i] == b_AL && break_class[i+1] == b_AL) ||
620 (break_class[i] == b_AL && break_class[i+1] == b_HL) ||
621 (break_class[i] == b_HL && break_class[i+1] == b_AL) ||
622 (break_class[i] == b_HL && break_class[i+1] == b_HL))
623 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
625 /* LB29 */
626 if ((break_class[i] == b_IS && break_class[i+1] == b_AL) ||
627 (break_class[i] == b_IS && break_class[i+1] == b_HL))
628 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
630 /* LB30 */
631 if ((break_class[i] == b_AL || break_class[i] == b_HL || break_class[i] == b_NU) &&
632 break_class[i+1] == b_OP)
633 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
634 if (break_class[i] == b_CP &&
635 (break_class[i+1] == b_AL || break_class[i] == b_HL || break_class[i] == b_NU))
636 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
638 /* LB30a */
639 if (break_class[i] == b_RI && break_class[i+1] == b_RI)
640 set_break_condition(i, BreakConditionAfter, DWRITE_BREAK_CONDITION_MAY_NOT_BREAK, &state);
644 heap_free(break_class);
645 return S_OK;
648 static HRESULT WINAPI dwritetextanalyzer_QueryInterface(IDWriteTextAnalyzer2 *iface, REFIID riid, void **obj)
650 TRACE("(%s %p)\n", debugstr_guid(riid), obj);
652 if (IsEqualIID(riid, &IID_IDWriteTextAnalyzer2) ||
653 IsEqualIID(riid, &IID_IDWriteTextAnalyzer1) ||
654 IsEqualIID(riid, &IID_IDWriteTextAnalyzer) ||
655 IsEqualIID(riid, &IID_IUnknown))
657 *obj = iface;
658 return S_OK;
661 *obj = NULL;
662 return E_NOINTERFACE;
666 static ULONG WINAPI dwritetextanalyzer_AddRef(IDWriteTextAnalyzer2 *iface)
668 return 2;
671 static ULONG WINAPI dwritetextanalyzer_Release(IDWriteTextAnalyzer2 *iface)
673 return 1;
676 static HRESULT WINAPI dwritetextanalyzer_AnalyzeScript(IDWriteTextAnalyzer2 *iface,
677 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
679 const WCHAR *text;
680 HRESULT hr;
681 UINT32 len;
683 TRACE("(%p %u %u %p)\n", source, position, length, sink);
685 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, position, &text, &len);
686 if (FAILED(hr)) return hr;
688 return analyze_script(text, len, sink);
691 static HRESULT WINAPI dwritetextanalyzer_AnalyzeBidi(IDWriteTextAnalyzer2 *iface,
692 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
694 UINT8 *levels = NULL, *explicit = NULL;
695 UINT8 baselevel, level, explicit_level;
696 WCHAR *buff = NULL;
697 const WCHAR *text;
698 UINT32 len, pos, i;
699 HRESULT hr;
701 TRACE("(%p %u %u %p)\n", source, position, length, sink);
703 if (length == 0)
704 return S_OK;
706 /* get some, check for length */
707 text = NULL;
708 len = 0;
709 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, position, &text, &len);
710 if (FAILED(hr)) return hr;
712 if (len < length) {
713 UINT32 read;
715 buff = heap_alloc(length*sizeof(WCHAR));
716 if (!buff)
717 return E_OUTOFMEMORY;
718 memcpy(buff, text, len*sizeof(WCHAR));
719 read = len;
721 while (read < length && text) {
722 text = NULL;
723 len = 0;
724 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, read, &text, &len);
725 if (FAILED(hr))
726 goto done;
727 memcpy(&buff[read], text, min(len, length-read)*sizeof(WCHAR));
728 read += len;
731 text = buff;
734 levels = heap_alloc(length*sizeof(*levels));
735 explicit = heap_alloc(length*sizeof(*explicit));
737 if (!levels || !explicit) {
738 hr = E_OUTOFMEMORY;
739 goto done;
742 baselevel = IDWriteTextAnalysisSource_GetParagraphReadingDirection(source);
743 hr = bidi_computelevels(text, length, baselevel, explicit, levels);
744 if (FAILED(hr))
745 goto done;
747 level = levels[0];
748 explicit_level = explicit[0];
749 pos = 0;
750 for (i = 1; i < length; i++) {
751 if (levels[i] != level || explicit[i] != explicit_level) {
752 hr = IDWriteTextAnalysisSink_SetBidiLevel(sink, pos, i - pos, explicit_level, level);
753 if (FAILED(hr))
754 break;
755 level = levels[i];
756 explicit_level = explicit[i];
757 pos = i;
760 if (i == length - 1)
761 hr = IDWriteTextAnalysisSink_SetBidiLevel(sink, pos, length - pos, explicit_level, level);
764 done:
765 heap_free(explicit);
766 heap_free(levels);
767 heap_free(buff);
769 return hr;
772 static HRESULT WINAPI dwritetextanalyzer_AnalyzeNumberSubstitution(IDWriteTextAnalyzer2 *iface,
773 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
775 FIXME("(%p %u %u %p): stub\n", source, position, length, sink);
776 return E_NOTIMPL;
779 static HRESULT WINAPI dwritetextanalyzer_AnalyzeLineBreakpoints(IDWriteTextAnalyzer2 *iface,
780 IDWriteTextAnalysisSource* source, UINT32 position, UINT32 length, IDWriteTextAnalysisSink* sink)
782 DWRITE_LINE_BREAKPOINT *breakpoints = NULL;
783 WCHAR *buff = NULL;
784 const WCHAR *text;
785 HRESULT hr;
786 UINT32 len;
788 TRACE("(%p %u %u %p)\n", source, position, length, sink);
790 if (length == 0)
791 return S_OK;
793 /* get some, check for length */
794 text = NULL;
795 len = 0;
796 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, position, &text, &len);
797 if (FAILED(hr)) return hr;
799 if (len < length) {
800 UINT32 read;
802 buff = heap_alloc(length*sizeof(WCHAR));
803 if (!buff)
804 return E_OUTOFMEMORY;
805 memcpy(buff, text, len*sizeof(WCHAR));
806 read = len;
808 while (read < length && text) {
809 text = NULL;
810 len = 0;
811 hr = IDWriteTextAnalysisSource_GetTextAtPosition(source, read, &text, &len);
812 if (FAILED(hr))
813 goto done;
814 memcpy(&buff[read], text, min(len, length-read)*sizeof(WCHAR));
815 read += len;
818 text = buff;
821 breakpoints = heap_alloc(length*sizeof(*breakpoints));
822 if (!breakpoints) {
823 hr = E_OUTOFMEMORY;
824 goto done;
827 hr = analyze_linebreaks(text, length, breakpoints);
828 if (FAILED(hr))
829 goto done;
831 hr = IDWriteTextAnalysisSink_SetLineBreakpoints(sink, position, length, breakpoints);
833 done:
834 heap_free(breakpoints);
835 heap_free(buff);
837 return hr;
840 static HRESULT WINAPI dwritetextanalyzer_GetGlyphs(IDWriteTextAnalyzer2 *iface,
841 WCHAR const* text, UINT32 length, IDWriteFontFace* fontface, BOOL is_sideways,
842 BOOL is_rtl, DWRITE_SCRIPT_ANALYSIS const* analysis, WCHAR const* locale,
843 IDWriteNumberSubstitution* substitution, DWRITE_TYPOGRAPHIC_FEATURES const** features,
844 UINT32 const* feature_range_len, UINT32 feature_ranges, UINT32 max_glyph_count,
845 UINT16* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES* text_props, UINT16* glyph_indices,
846 DWRITE_SHAPING_GLYPH_PROPERTIES* glyph_props, UINT32* actual_glyph_count)
848 const struct dwritescript_properties *scriptprops;
849 struct scriptshaping_cache *cache;
850 WCHAR *string;
851 BOOL update_cluster;
852 UINT32 i, g;
853 HRESULT hr = S_OK;
854 UINT16 script;
856 TRACE("(%s:%u %p %d %d %p %s %p %p %p %u %u %p %p %p %p %p)\n", debugstr_wn(text, length),
857 length, fontface, is_sideways, is_rtl, analysis, debugstr_w(locale), substitution, features, feature_range_len,
858 feature_ranges, max_glyph_count, clustermap, text_props, glyph_indices, glyph_props, actual_glyph_count);
860 script = analysis->script > Script_LastId ? Script_Unknown : analysis->script;
862 if (max_glyph_count < length)
863 return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
865 if (substitution)
866 FIXME("number substitution is not supported.\n");
868 for (i = 0; i < length; i++) {
869 /* FIXME: set to better values */
870 glyph_props[i].justification = text[i] == ' ' ? SCRIPT_JUSTIFY_BLANK : SCRIPT_JUSTIFY_CHARACTER;
871 glyph_props[i].isClusterStart = 1;
872 glyph_props[i].isDiacritic = 0;
873 glyph_props[i].isZeroWidthSpace = 0;
874 glyph_props[i].reserved = 0;
876 /* FIXME: have the shaping engine set this */
877 text_props[i].isShapedAlone = 0;
878 text_props[i].reserved = 0;
880 clustermap[i] = i;
883 for (; i < max_glyph_count; i++) {
884 glyph_props[i].justification = SCRIPT_JUSTIFY_NONE;
885 glyph_props[i].isClusterStart = 0;
886 glyph_props[i].isDiacritic = 0;
887 glyph_props[i].isZeroWidthSpace = 0;
888 glyph_props[i].reserved = 0;
891 string = heap_alloc(sizeof(WCHAR)*length);
892 if (!string)
893 return E_OUTOFMEMORY;
895 for (i = 0, g = 0, update_cluster = FALSE; i < length; i++) {
896 UINT32 codepoint;
898 if (!update_cluster) {
899 codepoint = decode_surrogate_pair(text, i, length);
900 if (!codepoint) {
901 codepoint = is_rtl ? bidi_get_mirrored_char(text[i]) : text[i];
902 string[i] = codepoint;
904 else {
905 string[i] = text[i];
906 string[i+1] = text[i+1];
907 update_cluster = TRUE;
910 hr = IDWriteFontFace_GetGlyphIndices(fontface, &codepoint, 1, &glyph_indices[g]);
911 if (FAILED(hr))
912 goto done;
914 g++;
916 else {
917 INT32 k;
919 update_cluster = FALSE;
920 /* mark surrogate halves with same cluster */
921 clustermap[i] = clustermap[i-1];
922 /* update following clusters */
923 for (k = i + 1; k >= 0 && k < length; k++)
924 clustermap[k]--;
927 *actual_glyph_count = g;
929 hr = create_scriptshaping_cache(fontface, &cache);
930 if (FAILED(hr))
931 goto done;
933 scriptprops = &dwritescripts_properties[script];
934 if (scriptprops->ops && scriptprops->ops->contextual_shaping) {
935 hr = scriptprops->ops->contextual_shaping(cache, is_rtl, string, length, max_glyph_count, clustermap, glyph_indices, actual_glyph_count);
936 if (FAILED(hr))
937 goto done;
940 /* FIXME: apply default features */
942 if (scriptprops->ops && scriptprops->ops->set_text_glyphs_props)
943 hr = scriptprops->ops->set_text_glyphs_props(cache, string, length, clustermap, glyph_indices, *actual_glyph_count, text_props, glyph_props);
944 else
945 hr = default_shaping_ops.set_text_glyphs_props(cache, string, length, clustermap, glyph_indices, *actual_glyph_count, text_props, glyph_props);
947 done:
948 release_scriptshaping_cache(cache);
949 heap_free(string);
951 return hr;
954 static HRESULT WINAPI dwritetextanalyzer_GetGlyphPlacements(IDWriteTextAnalyzer2 *iface,
955 WCHAR const* text, UINT16 const* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES* props,
956 UINT32 text_len, UINT16 const* glyph_indices, DWRITE_SHAPING_GLYPH_PROPERTIES const* glyph_props,
957 UINT32 glyph_count, IDWriteFontFace * font_face, FLOAT fontEmSize, BOOL is_sideways, BOOL is_rtl,
958 DWRITE_SCRIPT_ANALYSIS const* analysis, WCHAR const* locale, DWRITE_TYPOGRAPHIC_FEATURES const** features,
959 UINT32 const* feature_range_len, UINT32 feature_ranges, FLOAT* glyph_advances, DWRITE_GLYPH_OFFSET* glyph_offsets)
961 FIXME("(%s %p %p %u %p %p %u %p %f %d %d %p %s %p %p %u %p %p): stub\n", debugstr_w(text),
962 clustermap, props, text_len, glyph_indices, glyph_props, glyph_count, font_face, fontEmSize, is_sideways,
963 is_rtl, analysis, debugstr_w(locale), features, feature_range_len, feature_ranges, glyph_advances, glyph_offsets);
964 return E_NOTIMPL;
967 static HRESULT WINAPI dwritetextanalyzer_GetGdiCompatibleGlyphPlacements(IDWriteTextAnalyzer2 *iface,
968 WCHAR const* text, UINT16 const* clustermap, DWRITE_SHAPING_TEXT_PROPERTIES* props,
969 UINT32 text_len, UINT16 const* glyph_indices, DWRITE_SHAPING_GLYPH_PROPERTIES const* glyph_props,
970 UINT32 glyph_count, IDWriteFontFace * font_face, FLOAT fontEmSize, FLOAT pixels_per_dip,
971 DWRITE_MATRIX const* transform, BOOL use_gdi_natural, BOOL is_sideways, BOOL is_rtl,
972 DWRITE_SCRIPT_ANALYSIS const* analysis, WCHAR const* locale, DWRITE_TYPOGRAPHIC_FEATURES const** features,
973 UINT32 const* feature_range_lengths, UINT32 feature_ranges, FLOAT* glyph_advances, DWRITE_GLYPH_OFFSET* glyph_offsets)
975 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),
976 clustermap, props, text_len, glyph_indices, glyph_props, glyph_count, font_face, fontEmSize, pixels_per_dip,
977 transform, use_gdi_natural, is_sideways, is_rtl, analysis, debugstr_w(locale), features, feature_range_lengths,
978 feature_ranges, glyph_advances, glyph_offsets);
979 return E_NOTIMPL;
982 static HRESULT WINAPI dwritetextanalyzer1_ApplyCharacterSpacing(IDWriteTextAnalyzer2 *iface,
983 FLOAT leading_spacing, FLOAT trailing_spacing, FLOAT min_advance_width, UINT32 len,
984 UINT32 glyph_count, UINT16 const *clustermap, FLOAT const *advances, DWRITE_GLYPH_OFFSET const *offsets,
985 DWRITE_SHAPING_GLYPH_PROPERTIES const *props, FLOAT *modified_advances, DWRITE_GLYPH_OFFSET *modified_offsets)
987 FIXME("(%.2f %.2f %.2f %u %u %p %p %p %p %p %p): stub\n", leading_spacing, trailing_spacing, min_advance_width,
988 len, glyph_count, clustermap, advances, offsets, props, modified_advances, modified_offsets);
989 return E_NOTIMPL;
992 static HRESULT WINAPI dwritetextanalyzer1_GetBaseline(IDWriteTextAnalyzer2 *iface, IDWriteFontFace *face,
993 DWRITE_BASELINE baseline, BOOL vertical, BOOL is_simulation_allowed, DWRITE_SCRIPT_ANALYSIS sa,
994 const WCHAR *localeName, INT32 *baseline_coord, BOOL *exists)
996 FIXME("(%p %d %d %u %s %p %p): stub\n", face, vertical, is_simulation_allowed, sa.script, debugstr_w(localeName),
997 baseline_coord, exists);
998 return E_NOTIMPL;
1001 static HRESULT WINAPI dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation(IDWriteTextAnalyzer2 *iface,
1002 IDWriteTextAnalysisSource1* source, UINT32 text_pos, UINT32 len, IDWriteTextAnalysisSink1 *sink)
1004 FIXME("(%p %u %u %p): stub\n", source, text_pos, len, sink);
1005 return E_NOTIMPL;
1008 static HRESULT WINAPI dwritetextanalyzer1_GetGlyphOrientationTransform(IDWriteTextAnalyzer2 *iface,
1009 DWRITE_GLYPH_ORIENTATION_ANGLE angle, BOOL is_sideways, DWRITE_MATRIX *transform)
1011 FIXME("(%d %d %p): stub\n", angle, is_sideways, transform);
1012 return E_NOTIMPL;
1015 static HRESULT WINAPI dwritetextanalyzer1_GetScriptProperties(IDWriteTextAnalyzer2 *iface, DWRITE_SCRIPT_ANALYSIS sa,
1016 DWRITE_SCRIPT_PROPERTIES *props)
1018 TRACE("(%u %p)\n", sa.script, props);
1020 if (sa.script > Script_LastId)
1021 return E_INVALIDARG;
1023 *props = dwritescripts_properties[sa.script].props;
1024 return S_OK;
1027 static inline BOOL is_char_from_simple_script(WCHAR c)
1029 if (IS_HIGH_SURROGATE(c) || IS_LOW_SURROGATE(c))
1030 return FALSE;
1031 else {
1032 UINT16 script = get_char_script(c);
1033 return !dwritescripts_properties[script].is_complex;
1037 static HRESULT WINAPI dwritetextanalyzer1_GetTextComplexity(IDWriteTextAnalyzer2 *iface, const WCHAR *text,
1038 UINT32 len, IDWriteFontFace *face, BOOL *is_simple, UINT32 *len_read, UINT16 *indices)
1040 HRESULT hr = S_OK;
1041 int i;
1043 TRACE("(%s:%u %p %p %p %p)\n", debugstr_wn(text, len), len, face, is_simple, len_read, indices);
1045 *is_simple = FALSE;
1046 *len_read = 0;
1048 if (!face)
1049 return E_INVALIDARG;
1051 if (len == 0) {
1052 *is_simple = TRUE;
1053 return S_OK;
1056 *is_simple = text[0] && is_char_from_simple_script(text[0]);
1057 for (i = 1; i < len && text[i]; i++) {
1058 if (is_char_from_simple_script(text[i])) {
1059 if (!*is_simple)
1060 break;
1062 else
1063 *is_simple = FALSE;
1066 *len_read = i;
1068 /* fetch indices */
1069 if (*is_simple && indices) {
1070 UINT32 *codepoints = heap_alloc(*len_read*sizeof(UINT32));
1071 if (!codepoints)
1072 return E_OUTOFMEMORY;
1074 for (i = 0; i < *len_read; i++)
1075 codepoints[i] = text[i];
1077 hr = IDWriteFontFace_GetGlyphIndices(face, codepoints, *len_read, indices);
1078 heap_free(codepoints);
1081 return hr;
1084 static HRESULT WINAPI dwritetextanalyzer1_GetJustificationOpportunities(IDWriteTextAnalyzer2 *iface,
1085 IDWriteFontFace *face, FLOAT font_em_size, DWRITE_SCRIPT_ANALYSIS sa, UINT32 length, UINT32 glyph_count,
1086 const WCHAR *text, const UINT16 *clustermap, const DWRITE_SHAPING_GLYPH_PROPERTIES *prop, DWRITE_JUSTIFICATION_OPPORTUNITY *jo)
1088 FIXME("(%p %.2f %u %u %u %s %p %p %p): stub\n", face, font_em_size, sa.script, length, glyph_count,
1089 debugstr_w(text), clustermap, prop, jo);
1090 return E_NOTIMPL;
1093 static HRESULT WINAPI dwritetextanalyzer1_JustifyGlyphAdvances(IDWriteTextAnalyzer2 *iface,
1094 FLOAT width, UINT32 glyph_count, const DWRITE_JUSTIFICATION_OPPORTUNITY *jo, const FLOAT *advances,
1095 const DWRITE_GLYPH_OFFSET *offsets, FLOAT *justifiedadvances, DWRITE_GLYPH_OFFSET *justifiedoffsets)
1097 FIXME("(%.2f %u %p %p %p %p %p): stub\n", width, glyph_count, jo, advances, offsets, justifiedadvances,
1098 justifiedoffsets);
1099 return E_NOTIMPL;
1102 static HRESULT WINAPI dwritetextanalyzer1_GetJustifiedGlyphs(IDWriteTextAnalyzer2 *iface,
1103 IDWriteFontFace *face, FLOAT font_em_size, DWRITE_SCRIPT_ANALYSIS sa, UINT32 length,
1104 UINT32 glyph_count, UINT32 max_glyphcount, const UINT16 *clustermap, const UINT16 *indices,
1105 const FLOAT *advances, const FLOAT *justifiedadvances, const DWRITE_GLYPH_OFFSET *justifiedoffsets,
1106 const DWRITE_SHAPING_GLYPH_PROPERTIES *prop, UINT32 *actual_count, UINT16 *modified_clustermap,
1107 UINT16 *modified_indices, FLOAT *modified_advances, DWRITE_GLYPH_OFFSET *modified_offsets)
1109 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,
1110 length, glyph_count, max_glyphcount, clustermap, indices, advances, justifiedadvances, justifiedoffsets,
1111 prop, actual_count, modified_clustermap, modified_indices, modified_advances, modified_offsets);
1112 return E_NOTIMPL;
1115 static HRESULT WINAPI dwritetextanalyzer2_GetGlyphOrientationTransform(IDWriteTextAnalyzer2 *iface,
1116 DWRITE_GLYPH_ORIENTATION_ANGLE angle, BOOL is_sideways, FLOAT originX, FLOAT originY, DWRITE_MATRIX *transform)
1118 FIXME("(%d %d %.2f %.2f %p): stub\n", angle, is_sideways, originX, originY, transform);
1119 return E_NOTIMPL;
1122 static HRESULT WINAPI dwritetextanalyzer2_GetTypographicFeatures(IDWriteTextAnalyzer2 *iface,
1123 IDWriteFontFace *face, DWRITE_SCRIPT_ANALYSIS sa, const WCHAR *localeName,
1124 UINT32 max_tagcount, UINT32 *actual_tagcount, DWRITE_FONT_FEATURE_TAG *tags)
1126 FIXME("(%p %u %s %u %p %p): stub\n", face, sa.script, debugstr_w(localeName), max_tagcount, actual_tagcount,
1127 tags);
1128 return E_NOTIMPL;
1131 static HRESULT WINAPI dwritetextanalyzer2_CheckTypographicFeature(IDWriteTextAnalyzer2 *iface,
1132 IDWriteFontFace *face, DWRITE_SCRIPT_ANALYSIS sa, const WCHAR *localeName,
1133 DWRITE_FONT_FEATURE_TAG feature, UINT32 glyph_count, const UINT16 *indices, UINT8 *feature_applies)
1135 FIXME("(%p %u %s %x %u %p %p): stub\n", face, sa.script, debugstr_w(localeName), feature, glyph_count, indices,
1136 feature_applies);
1137 return E_NOTIMPL;
1140 static const struct IDWriteTextAnalyzer2Vtbl textanalyzervtbl = {
1141 dwritetextanalyzer_QueryInterface,
1142 dwritetextanalyzer_AddRef,
1143 dwritetextanalyzer_Release,
1144 dwritetextanalyzer_AnalyzeScript,
1145 dwritetextanalyzer_AnalyzeBidi,
1146 dwritetextanalyzer_AnalyzeNumberSubstitution,
1147 dwritetextanalyzer_AnalyzeLineBreakpoints,
1148 dwritetextanalyzer_GetGlyphs,
1149 dwritetextanalyzer_GetGlyphPlacements,
1150 dwritetextanalyzer_GetGdiCompatibleGlyphPlacements,
1151 dwritetextanalyzer1_ApplyCharacterSpacing,
1152 dwritetextanalyzer1_GetBaseline,
1153 dwritetextanalyzer1_AnalyzeVerticalGlyphOrientation,
1154 dwritetextanalyzer1_GetGlyphOrientationTransform,
1155 dwritetextanalyzer1_GetScriptProperties,
1156 dwritetextanalyzer1_GetTextComplexity,
1157 dwritetextanalyzer1_GetJustificationOpportunities,
1158 dwritetextanalyzer1_JustifyGlyphAdvances,
1159 dwritetextanalyzer1_GetJustifiedGlyphs,
1160 dwritetextanalyzer2_GetGlyphOrientationTransform,
1161 dwritetextanalyzer2_GetTypographicFeatures,
1162 dwritetextanalyzer2_CheckTypographicFeature
1165 static IDWriteTextAnalyzer2 textanalyzer = { &textanalyzervtbl };
1167 HRESULT get_textanalyzer(IDWriteTextAnalyzer **ret)
1169 *ret = (IDWriteTextAnalyzer*)&textanalyzer;
1170 return S_OK;
1173 static HRESULT WINAPI dwritenumbersubstitution_QueryInterface(IDWriteNumberSubstitution *iface, REFIID riid, void **obj)
1175 struct dwrite_numbersubstitution *This = impl_from_IDWriteNumberSubstitution(iface);
1177 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
1179 if (IsEqualIID(riid, &IID_IDWriteNumberSubstitution) ||
1180 IsEqualIID(riid, &IID_IUnknown))
1182 *obj = iface;
1183 IDWriteNumberSubstitution_AddRef(iface);
1184 return S_OK;
1187 *obj = NULL;
1189 return E_NOINTERFACE;
1192 static ULONG WINAPI dwritenumbersubstitution_AddRef(IDWriteNumberSubstitution *iface)
1194 struct dwrite_numbersubstitution *This = impl_from_IDWriteNumberSubstitution(iface);
1195 ULONG ref = InterlockedIncrement(&This->ref);
1196 TRACE("(%p)->(%d)\n", This, ref);
1197 return ref;
1200 static ULONG WINAPI dwritenumbersubstitution_Release(IDWriteNumberSubstitution *iface)
1202 struct dwrite_numbersubstitution *This = impl_from_IDWriteNumberSubstitution(iface);
1203 ULONG ref = InterlockedDecrement(&This->ref);
1205 TRACE("(%p)->(%d)\n", This, ref);
1207 if (!ref) {
1208 heap_free(This->locale);
1209 heap_free(This);
1212 return ref;
1215 static const struct IDWriteNumberSubstitutionVtbl numbersubstitutionvtbl = {
1216 dwritenumbersubstitution_QueryInterface,
1217 dwritenumbersubstitution_AddRef,
1218 dwritenumbersubstitution_Release
1221 HRESULT create_numbersubstitution(DWRITE_NUMBER_SUBSTITUTION_METHOD method, const WCHAR *locale,
1222 BOOL ignore_user_override, IDWriteNumberSubstitution **ret)
1224 struct dwrite_numbersubstitution *substitution;
1226 *ret = NULL;
1228 if (method < DWRITE_NUMBER_SUBSTITUTION_METHOD_FROM_CULTURE || method > DWRITE_NUMBER_SUBSTITUTION_METHOD_TRADITIONAL)
1229 return E_INVALIDARG;
1231 if (method != DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE && !IsValidLocaleName(locale))
1232 return E_INVALIDARG;
1234 substitution = heap_alloc(sizeof(*substitution));
1235 if (!substitution)
1236 return E_OUTOFMEMORY;
1238 substitution->IDWriteNumberSubstitution_iface.lpVtbl = &numbersubstitutionvtbl;
1239 substitution->ref = 1;
1240 substitution->ignore_user_override = ignore_user_override;
1241 substitution->method = method;
1242 substitution->locale = heap_strdupW(locale);
1243 if (locale && !substitution->locale) {
1244 heap_free(substitution);
1245 return E_OUTOFMEMORY;
1248 *ret = &substitution->IDWriteNumberSubstitution_iface;
1249 return S_OK;