058fc6f368d3bf428f76c4bb5d1c249e7eac7d09
[reactos.git] / dll / win32 / usp10 / usp10.c
1 /*
2 * Implementation of Uniscribe Script Processor (usp10.dll)
3 *
4 * Copyright 2005 Steven Edwards for CodeWeavers
5 * Copyright 2006 Hans Leidekker
6 * Copyright 2010 CodeWeavers, Aric Stewart
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 *
22 * Notes:
23 * Uniscribe allows for processing of complex scripts such as joining
24 * and filtering characters and bi-directional text with custom line breaks.
25 */
26
27 #include "usp10_internal.h"
28
29 #include <math.h>
30 #include <winuser.h>
31 #include <winreg.h>
32
33 WINE_DEFAULT_DEBUG_CHANNEL(uniscribe);
34
35 static const struct usp10_script_range
36 {
37 enum usp10_script script;
38 DWORD rangeFirst;
39 DWORD rangeLast;
40 enum usp10_script numericScript;
41 enum usp10_script punctScript;
42 }
43 script_ranges[] =
44 {
45 /* Basic Latin: U+0000–U+007A */
46 { Script_Latin, 0x00, 0x07a , Script_Numeric, Script_Punctuation},
47 /* Latin-1 Supplement: U+0080–U+00FF */
48 /* Latin Extended-A: U+0100–U+017F */
49 /* Latin Extended-B: U+0180–U+024F */
50 /* IPA Extensions: U+0250–U+02AF */
51 /* Spacing Modifier Letters:U+02B0–U+02FF */
52 { Script_Latin, 0x80, 0x2ff , Script_Numeric2, Script_Punctuation},
53 /* Combining Diacritical Marks : U+0300–U+036F */
54 { Script_Diacritical,0x300, 0x36f, 0, 0},
55 /* Greek: U+0370–U+03FF */
56 { Script_Greek, 0x370, 0x3ff, 0, 0},
57 /* Cyrillic: U+0400–U+04FF */
58 /* Cyrillic Supplement: U+0500–U+052F */
59 { Script_Cyrillic, 0x400, 0x52f, 0, 0},
60 /* Armenian: U+0530–U+058F */
61 { Script_Armenian, 0x530, 0x58f, 0, 0},
62 /* Hebrew: U+0590–U+05FF */
63 { Script_Hebrew, 0x590, 0x5ff, 0, 0},
64 /* Arabic: U+0600–U+06FF */
65 { Script_Arabic, 0x600, 0x6ef, Script_Arabic_Numeric, 0},
66 /* Defined by Windows */
67 { Script_Persian, 0x6f0, 0x6f9, 0, 0},
68 /* Continue Arabic: U+0600–U+06FF */
69 { Script_Arabic, 0x6fa, 0x6ff, 0, 0},
70 /* Syriac: U+0700–U+074F*/
71 { Script_Syriac, 0x700, 0x74f, 0, 0},
72 /* Arabic Supplement: U+0750–U+077F */
73 { Script_Arabic, 0x750, 0x77f, 0, 0},
74 /* Thaana: U+0780–U+07BF */
75 { Script_Thaana, 0x780, 0x7bf, 0, 0},
76 /* N’Ko: U+07C0–U+07FF */
77 { Script_NKo, 0x7c0, 0x7ff, 0, 0},
78 /* Devanagari: U+0900–U+097F */
79 { Script_Devanagari, 0x900, 0x97f, Script_Devanagari_Numeric, 0},
80 /* Bengali: U+0980–U+09FF */
81 { Script_Bengali, 0x980, 0x9ff, Script_Bengali_Numeric, 0},
82 /* Gurmukhi: U+0A00–U+0A7F*/
83 { Script_Gurmukhi, 0xa00, 0xa7f, Script_Gurmukhi_Numeric, 0},
84 /* Gujarati: U+0A80–U+0AFF*/
85 { Script_Gujarati, 0xa80, 0xaff, Script_Gujarati_Numeric, 0},
86 /* Oriya: U+0B00–U+0B7F */
87 { Script_Oriya, 0xb00, 0xb7f, Script_Oriya_Numeric, 0},
88 /* Tamil: U+0B80–U+0BFF */
89 { Script_Tamil, 0xb80, 0xbff, Script_Tamil_Numeric, 0},
90 /* Telugu: U+0C00–U+0C7F */
91 { Script_Telugu, 0xc00, 0xc7f, Script_Telugu_Numeric, 0},
92 /* Kannada: U+0C80–U+0CFF */
93 { Script_Kannada, 0xc80, 0xcff, Script_Kannada_Numeric, 0},
94 /* Malayalam: U+0D00–U+0D7F */
95 { Script_Malayalam, 0xd00, 0xd7f, Script_Malayalam_Numeric, 0},
96 /* Sinhala: U+0D80–U+0DFF */
97 { Script_Sinhala, 0xd80, 0xdff, 0, 0},
98 /* Thai: U+0E00–U+0E7F */
99 { Script_Thai, 0xe00, 0xe7f, Script_Thai_Numeric, 0},
100 /* Lao: U+0E80–U+0EFF */
101 { Script_Lao, 0xe80, 0xeff, Script_Lao_Numeric, 0},
102 /* Tibetan: U+0F00–U+0FFF */
103 { Script_Tibetan, 0xf00, 0xfff, 0, 0},
104 /* Myanmar: U+1000–U+109F */
105 { Script_Myanmar, 0x1000, 0x109f, Script_Myanmar_Numeric, 0},
106 /* Georgian: U+10A0–U+10FF */
107 { Script_Georgian, 0x10a0, 0x10ff, 0, 0},
108 /* Hangul Jamo: U+1100–U+11FF */
109 { Script_Hangul, 0x1100, 0x11ff, 0, 0},
110 /* Ethiopic: U+1200–U+137F */
111 /* Ethiopic Extensions: U+1380–U+139F */
112 { Script_Ethiopic, 0x1200, 0x139f, 0, 0},
113 /* Cherokee: U+13A0–U+13FF */
114 { Script_Cherokee, 0x13a0, 0x13ff, 0, 0},
115 /* Canadian Aboriginal Syllabics: U+1400–U+167F */
116 { Script_Canadian, 0x1400, 0x167f, 0, 0},
117 /* Ogham: U+1680–U+169F */
118 { Script_Ogham, 0x1680, 0x169f, 0, 0},
119 /* Runic: U+16A0–U+16F0 */
120 { Script_Runic, 0x16a0, 0x16f0, 0, 0},
121 /* Khmer: U+1780–U+17FF */
122 { Script_Khmer, 0x1780, 0x17ff, Script_Khmer_Numeric, 0},
123 /* Mongolian: U+1800–U+18AF */
124 { Script_Mongolian, 0x1800, 0x18af, Script_Mongolian_Numeric, 0},
125 /* Canadian Aboriginal Syllabics Extended: U+18B0–U+18FF */
126 { Script_Canadian, 0x18b0, 0x18ff, 0, 0},
127 /* Tai Le: U+1950–U+197F */
128 { Script_Tai_Le, 0x1950, 0x197f, 0, 0},
129 /* New Tai Lue: U+1980–U+19DF */
130 { Script_New_Tai_Lue,0x1980, 0x19df, Script_New_Tai_Lue_Numeric, 0},
131 /* Khmer Symbols: U+19E0–U+19FF */
132 { Script_Khmer, 0x19e0, 0x19ff, Script_Khmer_Numeric, 0},
133 /* Vedic Extensions: U+1CD0-U+1CFF */
134 { Script_Devanagari, 0x1cd0, 0x1cff, Script_Devanagari_Numeric, 0},
135 /* Phonetic Extensions: U+1D00–U+1DBF */
136 { Script_Latin, 0x1d00, 0x1dbf, 0, 0},
137 /* Combining Diacritical Marks Supplement: U+1DC0–U+1DFF */
138 { Script_Diacritical,0x1dc0, 0x1dff, 0, 0},
139 /* Latin Extended Additional: U+1E00–U+1EFF */
140 { Script_Latin, 0x1e00, 0x1eff, 0, 0},
141 /* Greek Extended: U+1F00–U+1FFF */
142 { Script_Greek, 0x1f00, 0x1fff, 0, 0},
143 /* General Punctuation: U+2000 –U+206f */
144 { Script_Latin, 0x2000, 0x206f, 0, 0},
145 /* Superscripts and Subscripts : U+2070 –U+209f */
146 /* Currency Symbols : U+20a0 –U+20cf */
147 { Script_Numeric2, 0x2070, 0x2070, 0, 0},
148 { Script_Latin, 0x2071, 0x2073, 0, 0},
149 { Script_Numeric2, 0x2074, 0x2079, 0, 0},
150 { Script_Latin, 0x207a, 0x207f, 0, 0},
151 { Script_Numeric2, 0x2080, 0x2089, 0, 0},
152 { Script_Latin, 0x208a, 0x20cf, 0, 0},
153 /* Letterlike Symbols : U+2100 –U+214f */
154 /* Number Forms : U+2150 –U+218f */
155 /* Arrows : U+2190 –U+21ff */
156 /* Mathematical Operators : U+2200 –U+22ff */
157 /* Miscellaneous Technical : U+2300 –U+23ff */
158 /* Control Pictures : U+2400 –U+243f */
159 /* Optical Character Recognition : U+2440 –U+245f */
160 /* Enclosed Alphanumerics : U+2460 –U+24ff */
161 /* Box Drawing : U+2500 –U+25ff */
162 /* Block Elements : U+2580 –U+259f */
163 /* Geometric Shapes : U+25a0 –U+25ff */
164 /* Miscellaneous Symbols : U+2600 –U+26ff */
165 /* Dingbats : U+2700 –U+27bf */
166 /* Miscellaneous Mathematical Symbols-A : U+27c0 –U+27ef */
167 /* Supplemental Arrows-A : U+27f0 –U+27ff */
168 { Script_Latin, 0x2100, 0x27ff, 0, 0},
169 /* Braille Patterns: U+2800–U+28FF */
170 { Script_Braille, 0x2800, 0x28ff, 0, 0},
171 /* Supplemental Arrows-B : U+2900 –U+297f */
172 /* Miscellaneous Mathematical Symbols-B : U+2980 –U+29ff */
173 /* Supplemental Mathematical Operators : U+2a00 –U+2aff */
174 /* Miscellaneous Symbols and Arrows : U+2b00 –U+2bff */
175 { Script_Latin, 0x2900, 0x2bff, 0, 0},
176 /* Latin Extended-C: U+2C60–U+2C7F */
177 { Script_Latin, 0x2c60, 0x2c7f, 0, 0},
178 /* Georgian: U+2D00–U+2D2F */
179 { Script_Georgian, 0x2d00, 0x2d2f, 0, 0},
180 /* Tifinagh: U+2D30–U+2D7F */
181 { Script_Tifinagh, 0x2d30, 0x2d7f, 0, 0},
182 /* Ethiopic Extensions: U+2D80–U+2DDF */
183 { Script_Ethiopic, 0x2d80, 0x2ddf, 0, 0},
184 /* Cyrillic Extended-A: U+2DE0–U+2DFF */
185 { Script_Cyrillic, 0x2de0, 0x2dff, 0, 0},
186 /* CJK Radicals Supplement: U+2E80–U+2EFF */
187 /* Kangxi Radicals: U+2F00–U+2FDF */
188 { Script_CJK_Han, 0x2e80, 0x2fdf, 0, 0},
189 /* Ideographic Description Characters: U+2FF0–U+2FFF */
190 { Script_Ideograph ,0x2ff0, 0x2fff, 0, 0},
191 /* CJK Symbols and Punctuation: U+3000–U+303F */
192 { Script_Ideograph ,0x3000, 0x3004, 0, 0},
193 { Script_CJK_Han ,0x3005, 0x3005, 0, 0},
194 { Script_Ideograph ,0x3006, 0x3006, 0, 0},
195 { Script_CJK_Han ,0x3007, 0x3007, 0, 0},
196 { Script_Ideograph ,0x3008, 0x3020, 0, 0},
197 { Script_CJK_Han ,0x3021, 0x3029, 0, 0},
198 { Script_Ideograph ,0x302a, 0x3030, 0, 0},
199 /* Kana Marks: */
200 { Script_Kana ,0x3031, 0x3035, 0, 0},
201 { Script_Ideograph ,0x3036, 0x3037, 0, 0},
202 { Script_CJK_Han ,0x3038, 0x303b, 0, 0},
203 { Script_Ideograph ,0x303c, 0x303f, 0, 0},
204 /* Hiragana: U+3040–U+309F */
205 /* Katakana: U+30A0–U+30FF */
206 { Script_Kana ,0x3040, 0x30ff, 0, 0},
207 /* Bopomofo: U+3100–U+312F */
208 { Script_Bopomofo ,0x3100, 0x312f, 0, 0},
209 /* Hangul Compatibility Jamo: U+3130–U+318F */
210 { Script_Hangul ,0x3130, 0x318f, 0, 0},
211 /* Kanbun: U+3190–U+319F */
212 { Script_Ideograph ,0x3190, 0x319f, 0, 0},
213 /* Bopomofo Extended: U+31A0–U+31BF */
214 { Script_Bopomofo ,0x31a0, 0x31bf, 0, 0},
215 /* CJK Strokes: U+31C0–U+31EF */
216 { Script_Ideograph ,0x31c0, 0x31ef, 0, 0},
217 /* Katakana Phonetic Extensions: U+31F0–U+31FF */
218 { Script_Kana ,0x31f0, 0x31ff, 0, 0},
219 /* Enclosed CJK Letters and Months: U+3200–U+32FF */
220 { Script_Hangul ,0x3200, 0x321f, 0, 0},
221 { Script_Ideograph ,0x3220, 0x325f, 0, 0},
222 { Script_Hangul ,0x3260, 0x327f, 0, 0},
223 { Script_Ideograph ,0x3280, 0x32ef, 0, 0},
224 { Script_Kana ,0x32d0, 0x31ff, 0, 0},
225 /* CJK Compatibility: U+3300–U+33FF*/
226 { Script_Kana ,0x3300, 0x3357, 0, 0},
227 { Script_Ideograph ,0x3358, 0x33ff, 0, 0},
228 /* CJK Unified Ideographs Extension A: U+3400–U+4DBF */
229 { Script_CJK_Han ,0x3400, 0x4dbf, 0, 0},
230 /* CJK Unified Ideographs: U+4E00–U+9FFF */
231 { Script_CJK_Han ,0x4e00, 0x9fff, 0, 0},
232 /* Yi: U+A000–U+A4CF */
233 { Script_Yi ,0xa000, 0xa4cf, 0, 0},
234 /* Vai: U+A500–U+A63F */
235 { Script_Vai ,0xa500, 0xa63f, Script_Vai_Numeric, 0},
236 /* Cyrillic Extended-B: U+A640–U+A69F */
237 { Script_Cyrillic, 0xa640, 0xa69f, 0, 0},
238 /* Modifier Tone Letters: U+A700–U+A71F */
239 /* Latin Extended-D: U+A720–U+A7FF */
240 { Script_Latin, 0xa700, 0xa7ff, 0, 0},
241 /* Phags-pa: U+A840–U+A87F */
242 { Script_Phags_pa, 0xa840, 0xa87f, 0, 0},
243 /* Devanagari Extended: U+A8E0-U+A8FF */
244 { Script_Devanagari, 0xa8e0, 0xa8ff, Script_Devanagari_Numeric, 0},
245 /* Myanmar Extended-A: U+AA60–U+AA7F */
246 { Script_Myanmar, 0xaa60, 0xaa7f, Script_Myanmar_Numeric, 0},
247 /* Hangul Jamo Extended-A: U+A960–U+A97F */
248 { Script_Hangul, 0xa960, 0xa97f, 0, 0},
249 /* Hangul Syllables: U+AC00–U+D7A3 */
250 { Script_Hangul, 0xac00, 0xd7a3, 0, 0},
251 /* Hangul Jamo Extended-B: U+D7B0–U+D7FF */
252 { Script_Hangul, 0xd7b0, 0xd7ff, 0, 0},
253 /* Surrogates Area: U+D800–U+DFFF */
254 { Script_Surrogates, 0xd800, 0xdbfe, 0, 0},
255 { Script_Private, 0xdbff, 0xdc00, 0, 0},
256 { Script_Surrogates, 0xdc01, 0xdfff, 0, 0},
257 /* Private Use Area: U+E000–U+F8FF */
258 { Script_Private, 0xe000, 0xf8ff, 0, 0},
259 /* CJK Compatibility Ideographs: U+F900–U+FAFF */
260 { Script_CJK_Han ,0xf900, 0xfaff, 0, 0},
261 /* Latin Ligatures: U+FB00–U+FB06 */
262 { Script_Latin, 0xfb00, 0xfb06, 0, 0},
263 /* Armenian ligatures U+FB13..U+FB17 */
264 { Script_Armenian, 0xfb13, 0xfb17, 0, 0},
265 /* Alphabetic Presentation Forms: U+FB1D–U+FB4F */
266 { Script_Hebrew, 0xfb1d, 0xfb4f, 0, 0},
267 /* Arabic Presentation Forms-A: U+FB50–U+FDFF*/
268 { Script_Arabic, 0xfb50, 0xfdff, 0, 0},
269 /* Vertical Forms: U+FE10–U+FE1F */
270 /* Combining Half Marks: U+FE20–U+FE2F */
271 /* CJK Compatibility Forms: U+FE30–U+FE4F */
272 /* Small Form Variants: U+FE50–U+FE6F */
273 { Script_Ideograph ,0xfe10, 0xfe6f, 0, 0},
274 /* Arabic Presentation Forms-B: U+FE70–U+FEFF*/
275 { Script_Arabic, 0xfe70, 0xfeff, 0, 0},
276 /* Halfwidth and Fullwidth Forms: U+FF00–FFEF */
277 { Script_Ideograph ,0xff00, 0xff64, Script_Numeric2, 0},
278 { Script_Kana ,0xff65, 0xff9f, 0, 0},
279 { Script_Hangul ,0xffa0, 0xffdf, 0, 0},
280 { Script_Ideograph ,0xffe0, 0xffef, 0, 0},
281 /* Plane - 1 */
282 /* Deseret: U+10400–U+1044F */
283 { Script_Deseret, 0x10400, 0x1044F, 0, 0},
284 /* Osmanya: U+10480–U+104AF */
285 { Script_Osmanya, 0x10480, 0x104AF, Script_Osmanya_Numeric, 0},
286 /* Mathematical Alphanumeric Symbols: U+1D400–U+1D7FF */
287 { Script_MathAlpha, 0x1D400, 0x1D7FF, 0, 0},
288 };
289
290 /* this must be in order so that the index matches the Script value */
291 const scriptData scriptInformation[] = {
292 {{SCRIPT_UNDEFINED, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
293 {LANG_NEUTRAL, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
294 0x00000000,
295 {0}},
296 {{Script_Latin, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
297 {LANG_ENGLISH, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
298 MS_MAKE_TAG('l','a','t','n'),
299 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
300 {{Script_CR, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
301 {LANG_NEUTRAL, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
302 0x00000000,
303 {0}},
304 {{Script_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
305 {LANG_ENGLISH, 1, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
306 0x00000000,
307 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
308 {{Script_Control, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
309 {LANG_ENGLISH, 0, 1, 0, 0, ANSI_CHARSET, 1, 0, 0, 0, 0, 0, 1, 0, 0},
310 0x00000000,
311 {0}},
312 {{Script_Punctuation, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
313 {LANG_NEUTRAL, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
314 0x00000000,
315 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
316 {{Script_Arabic, 1, 1, 0, 0, 0, 0, { 1,0,0,0,0,0,0,0,0,0,0}},
317 {LANG_ARABIC, 0, 1, 0, 0, ARABIC_CHARSET, 0, 0, 0, 0, 0, 0, 1, 1, 0},
318 MS_MAKE_TAG('a','r','a','b'),
319 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
320 {{Script_Arabic_Numeric, 0, 1, 0, 0, 0, 0, { 2,0,0,0,0,0,0,0,0,0,0}},
321 {LANG_ARABIC, 1, 1, 0, 0, ARABIC_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
322 MS_MAKE_TAG('a','r','a','b'),
323 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
324 {{Script_Hebrew, 1, 1, 0, 0, 0, 0, { 1,0,0,0,0,0,0,0,0,0,0}},
325 {LANG_HEBREW, 0, 1, 0, 1, HEBREW_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
326 MS_MAKE_TAG('h','e','b','r'),
327 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
328 {{Script_Syriac, 1, 1, 0, 0, 0, 0, { 1,0,0,0,0,0,0,0,0,0,0}},
329 {LANG_SYRIAC, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 1, 0},
330 MS_MAKE_TAG('s','y','r','c'),
331 {'E','s','t','r','a','n','g','e','l','o',' ','E','d','e','s','s','a',0}},
332 {{Script_Persian, 0, 1, 0, 0, 0, 0, { 2,0,0,0,0,0,0,0,0,0,0}},
333 {LANG_PERSIAN, 1, 1, 0, 0, ARABIC_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
334 MS_MAKE_TAG('a','r','a','b'),
335 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
336 {{Script_Thaana, 1, 1, 0, 0, 0, 0, { 1,0,0,0,0,0,0,0,0,0,0}},
337 {LANG_DIVEHI, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
338 MS_MAKE_TAG('t','h','a','a'),
339 {'M','V',' ','B','o','l','i',0}},
340 {{Script_Greek, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
341 {LANG_GREEK, 0, 0, 0, 0, GREEK_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
342 MS_MAKE_TAG('g','r','e','k'),
343 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
344 {{Script_Cyrillic, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
345 {LANG_RUSSIAN, 0, 0, 0, 0, RUSSIAN_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
346 MS_MAKE_TAG('c','y','r','l'),
347 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
348 {{Script_Armenian, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
349 {LANG_ARMENIAN, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
350 MS_MAKE_TAG('a','r','m','n'),
351 {'S','y','l','f','a','e','n',0}},
352 {{Script_Georgian, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
353 {LANG_GEORGIAN, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
354 MS_MAKE_TAG('g','e','o','r'),
355 {'S','y','l','f','a','e','n',0}},
356 {{Script_Sinhala, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
357 {LANG_SINHALESE, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
358 MS_MAKE_TAG('s','i','n','h'),
359 {'I','s','k','o','o','l','a',' ','P','o','t','a',0}},
360 {{Script_Tibetan, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
361 {LANG_TIBETAN, 0, 1, 1, 1, DEFAULT_CHARSET, 0, 0, 1, 0, 1, 0, 0, 0, 0},
362 MS_MAKE_TAG('t','i','b','t'),
363 {'M','i','c','r','o','s','o','f','t',' ','H','i','m','a','l','a','y','a',0}},
364 {{Script_Tibetan_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
365 {LANG_TIBETAN, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
366 MS_MAKE_TAG('t','i','b','t'),
367 {'M','i','c','r','o','s','o','f','t',' ','H','i','m','a','l','a','y','a',0}},
368 {{Script_Phags_pa, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
369 {LANG_MONGOLIAN, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
370 MS_MAKE_TAG('p','h','a','g'),
371 {'M','i','c','r','o','s','o','f','t',' ','P','h','a','g','s','P','a',0}},
372 {{Script_Thai, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
373 {LANG_THAI, 0, 1, 1, 1, THAI_CHARSET, 0, 0, 1, 0, 1, 0, 0, 0, 1},
374 MS_MAKE_TAG('t','h','a','i'),
375 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
376 {{Script_Thai_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
377 {LANG_THAI, 1, 1, 0, 0, THAI_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
378 MS_MAKE_TAG('t','h','a','i'),
379 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
380 {{Script_Lao, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
381 {LANG_LAO, 0, 1, 1, 1, DEFAULT_CHARSET, 0, 0, 1, 0, 1, 0, 0, 0, 0},
382 MS_MAKE_TAG('l','a','o',' '),
383 {'D','o','k','C','h','a','m','p','a',0}},
384 {{Script_Lao_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
385 {LANG_LAO, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
386 MS_MAKE_TAG('l','a','o',' '),
387 {'D','o','k','C','h','a','m','p','a',0}},
388 {{Script_Devanagari, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
389 {LANG_HINDI, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
390 MS_MAKE_TAG('d','e','v','a'),
391 {'M','a','n','g','a','l',0}},
392 {{Script_Devanagari_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
393 {LANG_HINDI, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
394 MS_MAKE_TAG('d','e','v','a'),
395 {'M','a','n','g','a','l',0}},
396 {{Script_Bengali, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
397 {LANG_BENGALI, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
398 MS_MAKE_TAG('b','e','n','g'),
399 {'V','r','i','n','d','a',0}},
400 {{Script_Bengali_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
401 {LANG_BENGALI, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
402 MS_MAKE_TAG('b','e','n','g'),
403 {'V','r','i','n','d','a',0}},
404 {{Script_Bengali_Currency, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
405 {LANG_BENGALI, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
406 MS_MAKE_TAG('b','e','n','g'),
407 {'V','r','i','n','d','a',0}},
408 {{Script_Gurmukhi, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
409 {LANG_PUNJABI, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
410 MS_MAKE_TAG('g','u','r','u'),
411 {'R','a','a','v','i',0}},
412 {{Script_Gurmukhi_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
413 {LANG_PUNJABI, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
414 MS_MAKE_TAG('g','u','r','u'),
415 {'R','a','a','v','i',0}},
416 {{Script_Gujarati, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
417 {LANG_GUJARATI, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
418 MS_MAKE_TAG('g','u','j','r'),
419 {'S','h','r','u','t','i',0}},
420 {{Script_Gujarati_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
421 {LANG_GUJARATI, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
422 MS_MAKE_TAG('g','u','j','r'),
423 {'S','h','r','u','t','i',0}},
424 {{Script_Gujarati_Currency, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
425 {LANG_GUJARATI, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
426 MS_MAKE_TAG('g','u','j','r'),
427 {'S','h','r','u','t','i',0}},
428 {{Script_Oriya, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
429 {LANG_ORIYA, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
430 MS_MAKE_TAG('o','r','y','a'),
431 {'K','a','l','i','n','g','a',0}},
432 {{Script_Oriya_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
433 {LANG_ORIYA, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
434 MS_MAKE_TAG('o','r','y','a'),
435 {'K','a','l','i','n','g','a',0}},
436 {{Script_Tamil, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
437 {LANG_TAMIL, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
438 MS_MAKE_TAG('t','a','m','l'),
439 {'L','a','t','h','a',0}},
440 {{Script_Tamil_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
441 {LANG_TAMIL, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
442 MS_MAKE_TAG('t','a','m','l'),
443 {'L','a','t','h','a',0}},
444 {{Script_Telugu, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
445 {LANG_TELUGU, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
446 MS_MAKE_TAG('t','e','l','u'),
447 {'G','a','u','t','a','m','i',0}},
448 {{Script_Telugu_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
449 {LANG_TELUGU, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
450 MS_MAKE_TAG('t','e','l','u'),
451 {'G','a','u','t','a','m','i',0}},
452 {{Script_Kannada, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
453 {LANG_KANNADA, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
454 MS_MAKE_TAG('k','n','d','a'),
455 {'T','u','n','g','a',0}},
456 {{Script_Kannada_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
457 {LANG_KANNADA, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
458 MS_MAKE_TAG('k','n','d','a'),
459 {'T','u','n','g','a',0}},
460 {{Script_Malayalam, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
461 {LANG_MALAYALAM, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
462 MS_MAKE_TAG('m','l','y','m'),
463 {'K','a','r','t','i','k','a',0}},
464 {{Script_Malayalam_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
465 {LANG_MALAYALAM, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
466 MS_MAKE_TAG('m','l','y','m'),
467 {'K','a','r','t','i','k','a',0}},
468 {{Script_Diacritical, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
469 {LANG_ENGLISH, 0, 1, 0, 1, ANSI_CHARSET, 0, 0, 0, 0, 0, 1, 1, 0, 0},
470 0x00000000,
471 {0}},
472 {{Script_Punctuation2, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
473 {LANG_ENGLISH, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
474 MS_MAKE_TAG('l','a','t','n'),
475 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
476 {{Script_Numeric2, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
477 {LANG_ENGLISH, 1, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
478 0x00000000,
479 {0}},
480 {{Script_Myanmar, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
481 {0x55, 0, 1, 1, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
482 MS_MAKE_TAG('m','y','m','r'),
483 {'M','y','a','n','m','a','r',' ','T','e','x','t',0}},
484 {{Script_Myanmar_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
485 {0x55, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
486 MS_MAKE_TAG('m','y','m','r'),
487 {0}},
488 {{Script_Tai_Le, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
489 {0, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
490 MS_MAKE_TAG('t','a','l','e'),
491 {'M','i','c','r','o','s','o','f','t',' ','T','a','i',' ','L','e',0}},
492 {{Script_New_Tai_Lue, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
493 {0, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
494 MS_MAKE_TAG('t','a','l','u'),
495 {'M','i','c','r','o','s','o','f','t',' ','N','e','w',' ','T','a','i',' ','L','u','e',0}},
496 {{Script_New_Tai_Lue_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
497 {0, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
498 MS_MAKE_TAG('t','a','l','u'),
499 {'M','i','c','r','o','s','o','f','t',' ','N','e','w',' ','T','a','i',' ','L','u','e',0}},
500 {{Script_Khmer, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
501 {0x53, 0, 1, 1, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
502 MS_MAKE_TAG('k','h','m','r'),
503 {'D','a','u','n','P','e','n','h',0}},
504 {{Script_Khmer_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
505 {0x53, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
506 MS_MAKE_TAG('k','h','m','r'),
507 {'D','a','u','n','P','e','n','h',0}},
508 {{Script_CJK_Han, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
509 {LANG_ENGLISH, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
510 MS_MAKE_TAG('h','a','n','i'),
511 {0}},
512 {{Script_Ideograph, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
513 {LANG_ENGLISH, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
514 MS_MAKE_TAG('h','a','n','i'),
515 {0}},
516 {{Script_Bopomofo, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
517 {LANG_ENGLISH, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
518 MS_MAKE_TAG('b','o','p','o'),
519 {0}},
520 {{Script_Kana, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
521 {LANG_ENGLISH, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
522 MS_MAKE_TAG('k','a','n','a'),
523 {0}},
524 {{Script_Hangul, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
525 {LANG_KOREAN, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
526 MS_MAKE_TAG('h','a','n','g'),
527 {0}},
528 {{Script_Yi, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
529 {LANG_ENGLISH, 0, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
530 MS_MAKE_TAG('y','i',' ',' '),
531 {'M','i','c','r','o','s','o','f','t',' ','Y','i',' ','B','a','i','t','i',0}},
532 {{Script_Ethiopic, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
533 {0x5e, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
534 MS_MAKE_TAG('e','t','h','i'),
535 {'N','y','a','l','a',0}},
536 {{Script_Ethiopic_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
537 {0x5e, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
538 MS_MAKE_TAG('e','t','h','i'),
539 {'N','y','a','l','a',0}},
540 {{Script_Mongolian, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
541 {LANG_MONGOLIAN, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
542 MS_MAKE_TAG('m','o','n','g'),
543 {'M','o','n','g','o','l','i','a','n',' ','B','a','i','t','i',0}},
544 {{Script_Mongolian_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
545 {LANG_MONGOLIAN, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
546 MS_MAKE_TAG('m','o','n','g'),
547 {'M','o','n','g','o','l','i','a','n',' ','B','a','i','t','i',0}},
548 {{Script_Tifinagh, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
549 {0, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
550 MS_MAKE_TAG('t','f','n','g'),
551 {'E','b','r','i','m','a',0}},
552 {{Script_NKo, 1, 1, 0, 0, 0, 0, { 1,0,0,0,0,0,0,0,0,0,0}},
553 {0, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
554 MS_MAKE_TAG('n','k','o',' '),
555 {'E','b','r','i','m','a',0}},
556 {{Script_Vai, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
557 {0, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
558 MS_MAKE_TAG('v','a','i',' '),
559 {'E','b','r','i','m','a',0}},
560 {{Script_Vai_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
561 {0, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
562 MS_MAKE_TAG('v','a','i',' '),
563 {'E','b','r','i','m','a',0}},
564 {{Script_Cherokee, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
565 {0x5c, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
566 MS_MAKE_TAG('c','h','e','r'),
567 {'P','l','a','n','t','a','g','e','n','e','t',' ','C','h','e','r','o','k','e','e',0}},
568 {{Script_Canadian, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
569 {0x5d, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
570 MS_MAKE_TAG('c','a','n','s'),
571 {'E','u','p','h','e','m','i','a',0}},
572 {{Script_Ogham, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
573 {0, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
574 MS_MAKE_TAG('o','g','a','m'),
575 {'S','e','g','o','e',' ','U','I',' ','S','y','m','b','o','l',0}},
576 {{Script_Runic, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
577 {0, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
578 MS_MAKE_TAG('r','u','n','r'),
579 {'S','e','g','o','e',' ','U','I',' ','S','y','m','b','o','l',0}},
580 {{Script_Braille, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
581 {LANG_ENGLISH, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
582 MS_MAKE_TAG('b','r','a','i'),
583 {'S','e','g','o','e',' ','U','I',' ','S','y','m','b','o','l',0}},
584 {{Script_Surrogates, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
585 {LANG_ENGLISH, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
586 0x00000000,
587 {0}},
588 {{Script_Private, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
589 {0, 0, 0, 0, 0, DEFAULT_CHARSET, 0, 1, 0, 0, 0, 0, 1, 0, 0},
590 0x00000000,
591 {0}},
592 {{Script_Deseret, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
593 {0, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
594 MS_MAKE_TAG('d','s','r','t'),
595 {'S','e','g','o','e',' ','U','I',' ','S','y','m','b','o','l',0}},
596 {{Script_Osmanya, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
597 {0, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
598 MS_MAKE_TAG('o','s','m','a'),
599 {'E','b','r','i','m','a',0}},
600 {{Script_Osmanya_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
601 {0, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
602 MS_MAKE_TAG('o','s','m','a'),
603 {'E','b','r','i','m','a',0}},
604 {{Script_MathAlpha, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
605 {0, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
606 MS_MAKE_TAG('m','a','t','h'),
607 {'C','a','m','b','r','i','a',' ','M','a','t','h',0}},
608 {{Script_Hebrew_Currency, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
609 {LANG_HEBREW, 0, 1, 0, 0, HEBREW_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
610 MS_MAKE_TAG('h','e','b','r'),
611 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
612 {{Script_Vietnamese_Currency, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
613 {LANG_VIETNAMESE, 0, 0, 0, 0, VIETNAMESE_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
614 MS_MAKE_TAG('l','a','t','n'),
615 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
616 {{Script_Thai_Currency, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
617 {LANG_THAI, 0, 1, 0, 0, THAI_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
618 MS_MAKE_TAG('t','h','a','i'),
619 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
620 };
621
622 static const SCRIPT_PROPERTIES *script_props[] =
623 {
624 &scriptInformation[0].props, &scriptInformation[1].props,
625 &scriptInformation[2].props, &scriptInformation[3].props,
626 &scriptInformation[4].props, &scriptInformation[5].props,
627 &scriptInformation[6].props, &scriptInformation[7].props,
628 &scriptInformation[8].props, &scriptInformation[9].props,
629 &scriptInformation[10].props, &scriptInformation[11].props,
630 &scriptInformation[12].props, &scriptInformation[13].props,
631 &scriptInformation[14].props, &scriptInformation[15].props,
632 &scriptInformation[16].props, &scriptInformation[17].props,
633 &scriptInformation[18].props, &scriptInformation[19].props,
634 &scriptInformation[20].props, &scriptInformation[21].props,
635 &scriptInformation[22].props, &scriptInformation[23].props,
636 &scriptInformation[24].props, &scriptInformation[25].props,
637 &scriptInformation[26].props, &scriptInformation[27].props,
638 &scriptInformation[28].props, &scriptInformation[29].props,
639 &scriptInformation[30].props, &scriptInformation[31].props,
640 &scriptInformation[32].props, &scriptInformation[33].props,
641 &scriptInformation[34].props, &scriptInformation[35].props,
642 &scriptInformation[36].props, &scriptInformation[37].props,
643 &scriptInformation[38].props, &scriptInformation[39].props,
644 &scriptInformation[40].props, &scriptInformation[41].props,
645 &scriptInformation[42].props, &scriptInformation[43].props,
646 &scriptInformation[44].props, &scriptInformation[45].props,
647 &scriptInformation[46].props, &scriptInformation[47].props,
648 &scriptInformation[48].props, &scriptInformation[49].props,
649 &scriptInformation[50].props, &scriptInformation[51].props,
650 &scriptInformation[52].props, &scriptInformation[53].props,
651 &scriptInformation[54].props, &scriptInformation[55].props,
652 &scriptInformation[56].props, &scriptInformation[57].props,
653 &scriptInformation[58].props, &scriptInformation[59].props,
654 &scriptInformation[60].props, &scriptInformation[61].props,
655 &scriptInformation[62].props, &scriptInformation[63].props,
656 &scriptInformation[64].props, &scriptInformation[65].props,
657 &scriptInformation[66].props, &scriptInformation[67].props,
658 &scriptInformation[68].props, &scriptInformation[69].props,
659 &scriptInformation[70].props, &scriptInformation[71].props,
660 &scriptInformation[72].props, &scriptInformation[73].props,
661 &scriptInformation[74].props, &scriptInformation[75].props,
662 &scriptInformation[76].props, &scriptInformation[77].props,
663 &scriptInformation[78].props, &scriptInformation[79].props,
664 &scriptInformation[80].props, &scriptInformation[81].props
665 };
666
667 typedef struct {
668 ScriptCache *sc;
669 int numGlyphs;
670 WORD* glyphs;
671 WORD* pwLogClust;
672 int* piAdvance;
673 SCRIPT_VISATTR* psva;
674 GOFFSET* pGoffset;
675 ABC abc;
676 int iMaxPosX;
677 HFONT fallbackFont;
678 } StringGlyphs;
679
680 enum stringanalysis_flags
681 {
682 SCRIPT_STRING_ANALYSIS_FLAGS_SIZE = 0x1,
683 SCRIPT_STRING_ANALYSIS_FLAGS_INVALID = 0x2,
684 };
685
686 typedef struct {
687 HDC hdc;
688 DWORD ssa_flags;
689 DWORD flags;
690 int clip_len;
691 int cItems;
692 int cMaxGlyphs;
693 SCRIPT_ITEM* pItem;
694 int numItems;
695 StringGlyphs* glyphs;
696 SCRIPT_LOGATTR* logattrs;
697 SIZE sz;
698 int* logical2visual;
699 } StringAnalysis;
700
701 typedef struct {
702 BOOL ascending;
703 WORD target;
704 } FindGlyph_struct;
705
706 BOOL usp10_array_reserve(void **elements, SIZE_T *capacity, SIZE_T count, SIZE_T size)
707 {
708 SIZE_T max_capacity, new_capacity;
709 void *new_elements;
710
711 if (count <= *capacity)
712 return TRUE;
713
714 max_capacity = ~(SIZE_T)0 / size;
715 if (count > max_capacity)
716 return FALSE;
717
718 new_capacity = max(1, *capacity);
719 while (new_capacity < count && new_capacity <= max_capacity / 2)
720 new_capacity *= 2;
721 if (new_capacity < count)
722 new_capacity = count;
723
724 if (!*elements)
725 new_elements = heap_alloc_zero(new_capacity * size);
726 else
727 new_elements = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *elements, new_capacity * size);
728 if (!new_elements)
729 return FALSE;
730
731 *elements = new_elements;
732 *capacity = new_capacity;
733 return TRUE;
734 }
735
736 /* TODO Fix font properties on Arabic locale */
737 static inline BOOL set_cache_font_properties(const HDC hdc, ScriptCache *sc)
738 {
739 sc->sfp.cBytes = sizeof(sc->sfp);
740
741 if (!sc->sfnt)
742 {
743 sc->sfp.wgBlank = sc->tm.tmBreakChar;
744 sc->sfp.wgDefault = sc->tm.tmDefaultChar;
745 sc->sfp.wgInvalid = sc->sfp.wgBlank;
746 sc->sfp.wgKashida = 0xFFFF;
747 sc->sfp.iKashidaWidth = 0;
748 }
749 else
750 {
751 static const WCHAR chars[4] = {0x0020, 0x200B, 0xF71B, 0x0640};
752 /* U+0020: numeric space
753 U+200B: zero width space
754 U+F71B: unknown char found by black box testing
755 U+0640: kashida */
756 WORD gi[4];
757
758 if (GetGlyphIndicesW(hdc, chars, 4, gi, GGI_MARK_NONEXISTING_GLYPHS) != GDI_ERROR)
759 {
760 if(gi[0] != 0xFFFF) /* 0xFFFF: index of default non exist char */
761 sc->sfp.wgBlank = gi[0];
762 else
763 sc->sfp.wgBlank = 0;
764
765 sc->sfp.wgDefault = 0;
766
767 if (gi[2] != 0xFFFF)
768 sc->sfp.wgInvalid = gi[2];
769 else if (gi[1] != 0xFFFF)
770 sc->sfp.wgInvalid = gi[1];
771 else if (gi[0] != 0xFFFF)
772 sc->sfp.wgInvalid = gi[0];
773 else
774 sc->sfp.wgInvalid = 0;
775
776 sc->sfp.wgKashida = gi[3];
777
778 sc->sfp.iKashidaWidth = 0; /* TODO */
779 }
780 else
781 return FALSE;
782 }
783 return TRUE;
784 }
785
786 static inline void get_cache_font_properties(SCRIPT_FONTPROPERTIES *sfp, ScriptCache *sc)
787 {
788 *sfp = sc->sfp;
789 }
790
791 static inline LONG get_cache_height(SCRIPT_CACHE *psc)
792 {
793 return ((ScriptCache *)*psc)->tm.tmHeight;
794 }
795
796 static inline BYTE get_cache_pitch_family(SCRIPT_CACHE *psc)
797 {
798 return ((ScriptCache *)*psc)->tm.tmPitchAndFamily;
799 }
800
801 static inline WORD get_cache_glyph(SCRIPT_CACHE *psc, DWORD c)
802 {
803 CacheGlyphPage *page = ((ScriptCache *)*psc)->page[c / 0x10000];
804 WORD *block;
805
806 if (!page) return 0;
807 block = page->glyphs[(c % 0x10000) >> GLYPH_BLOCK_SHIFT];
808 if (!block) return 0;
809 return block[(c % 0x10000) & GLYPH_BLOCK_MASK];
810 }
811
812 static inline WORD set_cache_glyph(SCRIPT_CACHE *psc, WCHAR c, WORD glyph)
813 {
814 CacheGlyphPage **page = &((ScriptCache *)*psc)->page[c / 0x10000];
815 WORD **block;
816 if (!*page && !(*page = heap_alloc_zero(sizeof(CacheGlyphPage)))) return 0;
817
818 block = &(*page)->glyphs[(c % 0x10000) >> GLYPH_BLOCK_SHIFT];
819 if (!*block && !(*block = heap_alloc_zero(sizeof(WORD) * GLYPH_BLOCK_SIZE))) return 0;
820 return ((*block)[(c % 0x10000) & GLYPH_BLOCK_MASK] = glyph);
821 }
822
823 static inline BOOL get_cache_glyph_widths(SCRIPT_CACHE *psc, WORD glyph, ABC *abc)
824 {
825 static const ABC nil;
826 ABC *block = ((ScriptCache *)*psc)->widths[glyph >> GLYPH_BLOCK_SHIFT];
827
828 if (!block || !memcmp(&block[glyph & GLYPH_BLOCK_MASK], &nil, sizeof(ABC))) return FALSE;
829 memcpy(abc, &block[glyph & GLYPH_BLOCK_MASK], sizeof(ABC));
830 return TRUE;
831 }
832
833 static inline BOOL set_cache_glyph_widths(SCRIPT_CACHE *psc, WORD glyph, ABC *abc)
834 {
835 ABC **block = &((ScriptCache *)*psc)->widths[glyph >> GLYPH_BLOCK_SHIFT];
836
837 if (!*block && !(*block = heap_alloc_zero(sizeof(ABC) * GLYPH_BLOCK_SIZE))) return FALSE;
838 memcpy(&(*block)[glyph & GLYPH_BLOCK_MASK], abc, sizeof(ABC));
839 return TRUE;
840 }
841
842 static HRESULT init_script_cache(const HDC hdc, SCRIPT_CACHE *psc)
843 {
844 ScriptCache *sc;
845 int size;
846
847 if (!psc) return E_INVALIDARG;
848 if (*psc) return S_OK;
849 if (!hdc) return E_PENDING;
850
851 if (!(sc = heap_alloc_zero(sizeof(ScriptCache)))) return E_OUTOFMEMORY;
852 if (!GetTextMetricsW(hdc, &sc->tm))
853 {
854 heap_free(sc);
855 return E_INVALIDARG;
856 }
857 size = GetOutlineTextMetricsW(hdc, 0, NULL);
858 if (size)
859 {
860 sc->otm = heap_alloc(size);
861 sc->otm->otmSize = size;
862 GetOutlineTextMetricsW(hdc, size, sc->otm);
863 }
864 if (!GetObjectW(GetCurrentObject(hdc, OBJ_FONT), sizeof(LOGFONTW), &sc->lf))
865 {
866 heap_free(sc);
867 return E_INVALIDARG;
868 }
869 sc->sfnt = (GetFontData(hdc, MS_MAKE_TAG('h','e','a','d'), 0, NULL, 0)!=GDI_ERROR);
870 if (!set_cache_font_properties(hdc, sc))
871 {
872 heap_free(sc);
873 return E_INVALIDARG;
874 }
875 *psc = sc;
876 TRACE("<- %p\n", sc);
877 return S_OK;
878 }
879
880 static WCHAR mirror_char( WCHAR ch )
881 {
882 extern const WCHAR wine_mirror_map[] DECLSPEC_HIDDEN;
883 return ch + wine_mirror_map[wine_mirror_map[ch >> 8] + (ch & 0xff)];
884 }
885
886 static DWORD decode_surrogate_pair(const WCHAR *str, unsigned int index, unsigned int end)
887 {
888 if (index < end-1 && IS_SURROGATE_PAIR(str[index],str[index+1]))
889 {
890 DWORD ch = 0x10000 + ((str[index] - 0xd800) << 10) + (str[index+1] - 0xdc00);
891 TRACE("Surrogate Pair %x %x => %x\n",str[index], str[index+1], ch);
892 return ch;
893 }
894 return 0;
895 }
896
897 static int usp10_compare_script_range(const void *key, const void *value)
898 {
899 const struct usp10_script_range *range = value;
900 const DWORD *ch = key;
901
902 if (*ch < range->rangeFirst)
903 return -1;
904 if (*ch > range->rangeLast)
905 return 1;
906 return 0;
907 }
908
909 static enum usp10_script get_char_script(const WCHAR *str, unsigned int index,
910 unsigned int end, unsigned int *consumed)
911 {
912 static const WCHAR latin_punc[] = {'#','$','&','\'',',',';','<','>','?','@','\\','^','_','`','{','|','}','~', 0x00a0, 0};
913 struct usp10_script_range *range;
914 WORD type = 0, type2 = 0;
915 DWORD ch;
916
917 *consumed = 1;
918
919 if (str[index] == 0xc || str[index] == 0x20 || str[index] == 0x202f)
920 return Script_CR;
921
922 /* These punctuation characters are separated out as Latin punctuation */
923 if (strchrW(latin_punc,str[index]))
924 return Script_Punctuation2;
925
926 /* These chars are itemized as Punctuation by Windows */
927 if (str[index] == 0x2212 || str[index] == 0x2044)
928 return Script_Punctuation;
929
930 /* Currency Symbols by Unicode point */
931 switch (str[index])
932 {
933 case 0x09f2:
934 case 0x09f3: return Script_Bengali_Currency;
935 case 0x0af1: return Script_Gujarati_Currency;
936 case 0x0e3f: return Script_Thai_Currency;
937 case 0x20aa: return Script_Hebrew_Currency;
938 case 0x20ab: return Script_Vietnamese_Currency;
939 case 0xfb29: return Script_Hebrew_Currency;
940 }
941
942 GetStringTypeW(CT_CTYPE1, &str[index], 1, &type);
943 GetStringTypeW(CT_CTYPE2, &str[index], 1, &type2);
944
945 if (type == 0)
946 return SCRIPT_UNDEFINED;
947
948 if (type & C1_CNTRL)
949 return Script_Control;
950
951 ch = decode_surrogate_pair(str, index, end);
952 if (ch)
953 *consumed = 2;
954 else
955 ch = str[index];
956
957 if (!(range = bsearch(&ch, script_ranges, ARRAY_SIZE(script_ranges),
958 sizeof(*script_ranges), usp10_compare_script_range)))
959 return (*consumed == 2) ? Script_Surrogates : Script_Undefined;
960
961 if (range->numericScript && (type & C1_DIGIT || type2 == C2_ARABICNUMBER))
962 return range->numericScript;
963 if (range->punctScript && type & C1_PUNCT)
964 return range->punctScript;
965 return range->script;
966 }
967
968 static int compare_FindGlyph(const void *a, const void* b)
969 {
970 const FindGlyph_struct *find = (FindGlyph_struct*)a;
971 const WORD *idx= (WORD*)b;
972 int rc = 0;
973
974 if ( find->target > *idx)
975 rc = 1;
976 else if (find->target < *idx)
977 rc = -1;
978
979 if (!find->ascending)
980 rc *= -1;
981 return rc;
982 }
983
984 int USP10_FindGlyphInLogClust(const WORD* pwLogClust, int cChars, WORD target)
985 {
986 FindGlyph_struct fgs;
987 WORD *ptr;
988 INT k;
989
990 if (pwLogClust[0] < pwLogClust[cChars-1])
991 fgs.ascending = TRUE;
992 else
993 fgs.ascending = FALSE;
994
995 fgs.target = target;
996 ptr = bsearch(&fgs, pwLogClust, cChars, sizeof(WORD), compare_FindGlyph);
997
998 if (!ptr)
999 return -1;
1000
1001 for (k = (ptr - pwLogClust)-1; k >= 0 && pwLogClust[k] == target; k--)
1002 ;
1003 k++;
1004
1005 return k;
1006 }
1007
1008 /***********************************************************************
1009 * ScriptFreeCache (USP10.@)
1010 *
1011 * Free a script cache.
1012 *
1013 * PARAMS
1014 * psc [I/O] Script cache.
1015 *
1016 * RETURNS
1017 * Success: S_OK
1018 * Failure: Non-zero HRESULT value.
1019 */
1020 HRESULT WINAPI ScriptFreeCache(SCRIPT_CACHE *psc)
1021 {
1022 TRACE("%p\n", psc);
1023
1024 if (psc && *psc)
1025 {
1026 unsigned int i;
1027 INT n;
1028 for (i = 0; i < GLYPH_MAX / GLYPH_BLOCK_SIZE; i++)
1029 {
1030 heap_free(((ScriptCache *)*psc)->widths[i]);
1031 }
1032 for (i = 0; i < NUM_PAGES; i++)
1033 {
1034 unsigned int j;
1035 if (((ScriptCache *)*psc)->page[i])
1036 for (j = 0; j < GLYPH_MAX / GLYPH_BLOCK_SIZE; j++)
1037 heap_free(((ScriptCache *)*psc)->page[i]->glyphs[j]);
1038 heap_free(((ScriptCache *)*psc)->page[i]);
1039 }
1040 heap_free(((ScriptCache *)*psc)->GSUB_Table);
1041 heap_free(((ScriptCache *)*psc)->GDEF_Table);
1042 heap_free(((ScriptCache *)*psc)->CMAP_Table);
1043 heap_free(((ScriptCache *)*psc)->GPOS_Table);
1044 for (n = 0; n < ((ScriptCache *)*psc)->script_count; n++)
1045 {
1046 int j;
1047 for (j = 0; j < ((ScriptCache *)*psc)->scripts[n].language_count; j++)
1048 {
1049 int k;
1050 for (k = 0; k < ((ScriptCache *)*psc)->scripts[n].languages[j].feature_count; k++)
1051 heap_free(((ScriptCache *)*psc)->scripts[n].languages[j].features[k].lookups);
1052 heap_free(((ScriptCache *)*psc)->scripts[n].languages[j].features);
1053 }
1054 for (j = 0; j < ((ScriptCache *)*psc)->scripts[n].default_language.feature_count; j++)
1055 heap_free(((ScriptCache *)*psc)->scripts[n].default_language.features[j].lookups);
1056 heap_free(((ScriptCache *)*psc)->scripts[n].default_language.features);
1057 heap_free(((ScriptCache *)*psc)->scripts[n].languages);
1058 }
1059 heap_free(((ScriptCache *)*psc)->scripts);
1060 heap_free(((ScriptCache *)*psc)->otm);
1061 heap_free(*psc);
1062 *psc = NULL;
1063 }
1064 return S_OK;
1065 }
1066
1067 /***********************************************************************
1068 * ScriptGetProperties (USP10.@)
1069 *
1070 * Retrieve a list of script properties.
1071 *
1072 * PARAMS
1073 * props [I] Pointer to an array of SCRIPT_PROPERTIES pointers.
1074 * num [I] Pointer to the number of scripts.
1075 *
1076 * RETURNS
1077 * Success: S_OK
1078 * Failure: Non-zero HRESULT value.
1079 *
1080 * NOTES
1081 * Behaviour matches WinXP.
1082 */
1083 HRESULT WINAPI ScriptGetProperties(const SCRIPT_PROPERTIES ***props, int *num)
1084 {
1085 TRACE("(%p,%p)\n", props, num);
1086
1087 if (!props && !num) return E_INVALIDARG;
1088
1089 if (num) *num = sizeof(script_props)/sizeof(script_props[0]);
1090 if (props) *props = script_props;
1091
1092 return S_OK;
1093 }
1094
1095 /***********************************************************************
1096 * ScriptGetFontProperties (USP10.@)
1097 *
1098 * Get information on special glyphs.
1099 *
1100 * PARAMS
1101 * hdc [I] Device context.
1102 * psc [I/O] Opaque pointer to a script cache.
1103 * sfp [O] Font properties structure.
1104 */
1105 HRESULT WINAPI ScriptGetFontProperties(HDC hdc, SCRIPT_CACHE *psc, SCRIPT_FONTPROPERTIES *sfp)
1106 {
1107 HRESULT hr;
1108
1109 TRACE("%p,%p,%p\n", hdc, psc, sfp);
1110
1111 if (!sfp) return E_INVALIDARG;
1112 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
1113
1114 if (sfp->cBytes != sizeof(SCRIPT_FONTPROPERTIES))
1115 return E_INVALIDARG;
1116
1117 get_cache_font_properties(sfp, *psc);
1118
1119 return S_OK;
1120 }
1121
1122 /***********************************************************************
1123 * ScriptRecordDigitSubstitution (USP10.@)
1124 *
1125 * Record digit substitution settings for a given locale.
1126 *
1127 * PARAMS
1128 * locale [I] Locale identifier.
1129 * sds [I] Structure to record substitution settings.
1130 *
1131 * RETURNS
1132 * Success: S_OK
1133 * Failure: E_POINTER if sds is NULL, E_INVALIDARG otherwise.
1134 *
1135 * SEE ALSO
1136 * http://blogs.msdn.com/michkap/archive/2006/02/22/536877.aspx
1137 */
1138 HRESULT WINAPI ScriptRecordDigitSubstitution(LCID locale, SCRIPT_DIGITSUBSTITUTE *sds)
1139 {
1140 DWORD plgid, sub;
1141
1142 TRACE("0x%x, %p\n", locale, sds);
1143
1144 /* This implementation appears to be correct for all languages, but it's
1145 * not clear if sds->DigitSubstitute is ever set to anything except
1146 * CONTEXT or NONE in reality */
1147
1148 if (!sds) return E_POINTER;
1149
1150 locale = ConvertDefaultLocale(locale);
1151
1152 if (!IsValidLocale(locale, LCID_INSTALLED))
1153 return E_INVALIDARG;
1154
1155 plgid = PRIMARYLANGID(LANGIDFROMLCID(locale));
1156 sds->TraditionalDigitLanguage = plgid;
1157
1158 if (plgid == LANG_ARABIC || plgid == LANG_FARSI)
1159 sds->NationalDigitLanguage = plgid;
1160 else
1161 sds->NationalDigitLanguage = LANG_ENGLISH;
1162
1163 if (!GetLocaleInfoW(locale, LOCALE_IDIGITSUBSTITUTION | LOCALE_RETURN_NUMBER,
1164 (WCHAR *)&sub, sizeof(sub) / sizeof(WCHAR)))
1165 return E_INVALIDARG;
1166
1167 switch (sub)
1168 {
1169 case 0:
1170 if (plgid == LANG_ARABIC || plgid == LANG_FARSI)
1171 sds->DigitSubstitute = SCRIPT_DIGITSUBSTITUTE_CONTEXT;
1172 else
1173 sds->DigitSubstitute = SCRIPT_DIGITSUBSTITUTE_NONE;
1174 break;
1175 case 1:
1176 sds->DigitSubstitute = SCRIPT_DIGITSUBSTITUTE_NONE;
1177 break;
1178 case 2:
1179 sds->DigitSubstitute = SCRIPT_DIGITSUBSTITUTE_NATIONAL;
1180 break;
1181 default:
1182 sds->DigitSubstitute = SCRIPT_DIGITSUBSTITUTE_TRADITIONAL;
1183 break;
1184 }
1185
1186 sds->dwReserved = 0;
1187 return S_OK;
1188 }
1189
1190 /***********************************************************************
1191 * ScriptApplyDigitSubstitution (USP10.@)
1192 *
1193 * Apply digit substitution settings.
1194 *
1195 * PARAMS
1196 * sds [I] Structure with recorded substitution settings.
1197 * sc [I] Script control structure.
1198 * ss [I] Script state structure.
1199 *
1200 * RETURNS
1201 * Success: S_OK
1202 * Failure: E_INVALIDARG if sds is invalid. Otherwise an HRESULT.
1203 */
1204 HRESULT WINAPI ScriptApplyDigitSubstitution(const SCRIPT_DIGITSUBSTITUTE *sds,
1205 SCRIPT_CONTROL *sc, SCRIPT_STATE *ss)
1206 {
1207 SCRIPT_DIGITSUBSTITUTE psds;
1208
1209 TRACE("%p, %p, %p\n", sds, sc, ss);
1210
1211 if (!sc || !ss) return E_POINTER;
1212 if (!sds)
1213 {
1214 sds = &psds;
1215 if (ScriptRecordDigitSubstitution(LOCALE_USER_DEFAULT, &psds) != S_OK)
1216 return E_INVALIDARG;
1217 }
1218
1219 sc->uDefaultLanguage = LANG_ENGLISH;
1220 sc->fContextDigits = 0;
1221 ss->fDigitSubstitute = 0;
1222
1223 switch (sds->DigitSubstitute) {
1224 case SCRIPT_DIGITSUBSTITUTE_CONTEXT:
1225 case SCRIPT_DIGITSUBSTITUTE_NATIONAL:
1226 case SCRIPT_DIGITSUBSTITUTE_NONE:
1227 case SCRIPT_DIGITSUBSTITUTE_TRADITIONAL:
1228 return S_OK;
1229 default:
1230 return E_INVALIDARG;
1231 }
1232 }
1233
1234 static inline BOOL is_indic(enum usp10_script script)
1235 {
1236 return (script >= Script_Devanagari && script <= Script_Malayalam_Numeric);
1237 }
1238
1239 static inline enum usp10_script base_indic(enum usp10_script script)
1240 {
1241 switch (script)
1242 {
1243 case Script_Devanagari:
1244 case Script_Devanagari_Numeric: return Script_Devanagari;
1245 case Script_Bengali:
1246 case Script_Bengali_Numeric:
1247 case Script_Bengali_Currency: return Script_Bengali;
1248 case Script_Gurmukhi:
1249 case Script_Gurmukhi_Numeric: return Script_Gurmukhi;
1250 case Script_Gujarati:
1251 case Script_Gujarati_Numeric:
1252 case Script_Gujarati_Currency: return Script_Gujarati;
1253 case Script_Oriya:
1254 case Script_Oriya_Numeric: return Script_Oriya;
1255 case Script_Tamil:
1256 case Script_Tamil_Numeric: return Script_Tamil;
1257 case Script_Telugu:
1258 case Script_Telugu_Numeric: return Script_Telugu;
1259 case Script_Kannada:
1260 case Script_Kannada_Numeric: return Script_Kannada;
1261 case Script_Malayalam:
1262 case Script_Malayalam_Numeric: return Script_Malayalam;
1263 default:
1264 return Script_Undefined;
1265 };
1266 }
1267
1268 static BOOL script_is_numeric(enum usp10_script script)
1269 {
1270 return scriptInformation[script].props.fNumeric;
1271 }
1272
1273 static HRESULT _ItemizeInternal(const WCHAR *pwcInChars, int cInChars,
1274 int cMaxItems, const SCRIPT_CONTROL *psControl,
1275 const SCRIPT_STATE *psState, SCRIPT_ITEM *pItems,
1276 OPENTYPE_TAG *pScriptTags, int *pcItems)
1277 {
1278
1279 #define Numeric_space 0x0020
1280 #define ZWSP 0x200B
1281 #define ZWNJ 0x200C
1282 #define ZWJ 0x200D
1283
1284 enum usp10_script last_indic = Script_Undefined;
1285 int cnt = 0, index = 0, str = 0;
1286 enum usp10_script New_Script = -1;
1287 int i;
1288 WORD *levels = NULL;
1289 WORD *layout_levels = NULL;
1290 WORD *overrides = NULL;
1291 WORD *strength = NULL;
1292 enum usp10_script *scripts;
1293 WORD baselevel = 0;
1294 WORD baselayout = 0;
1295 BOOL new_run;
1296 WORD layoutRTL = 0;
1297 BOOL forceLevels = FALSE;
1298 unsigned int consumed = 0;
1299 HRESULT res = E_OUTOFMEMORY;
1300
1301 TRACE("%s,%d,%d,%p,%p,%p,%p\n", debugstr_wn(pwcInChars, cInChars), cInChars, cMaxItems,
1302 psControl, psState, pItems, pcItems);
1303
1304 if (!pwcInChars || !cInChars || !pItems || cMaxItems < 2)
1305 return E_INVALIDARG;
1306
1307 if (!(scripts = heap_alloc(cInChars * sizeof(*scripts))))
1308 return E_OUTOFMEMORY;
1309
1310 for (i = 0; i < cInChars; i++)
1311 {
1312 if (!consumed)
1313 {
1314 scripts[i] = get_char_script(pwcInChars,i,cInChars,&consumed);
1315 consumed --;
1316 }
1317 else
1318 {
1319 scripts[i] = scripts[i-1];
1320 consumed --;
1321 }
1322 /* Devanagari danda (U+0964) and double danda (U+0965) are used for
1323 all Indic scripts */
1324 if ((pwcInChars[i] == 0x964 || pwcInChars[i] ==0x965) && last_indic != Script_Undefined)
1325 scripts[i] = last_indic;
1326 else if (is_indic(scripts[i]))
1327 last_indic = base_indic(scripts[i]);
1328
1329 /* Some unicode points :
1330 (Zero Width Space U+200B - Right-to-Left Mark U+200F)
1331 (Left Right Embed U+202A - Left Right Override U+202D)
1332 (Left Right Isolate U+2066 - Pop Directional Isolate U+2069)
1333 will force us into bidi mode */
1334 if (!forceLevels && ((pwcInChars[i] >= 0x200B && pwcInChars[i] <= 0x200F) ||
1335 (pwcInChars[i] >= 0x202A && pwcInChars[i] <= 0x202E) ||
1336 (pwcInChars[i] >= 0x2066 && pwcInChars[i] <= 0x2069)))
1337
1338 forceLevels = TRUE;
1339
1340 /* Diacritical marks merge with other scripts */
1341 if (scripts[i] == Script_Diacritical)
1342 {
1343 if (i > 0)
1344 {
1345 if (pScriptTags)
1346 scripts[i] = scripts[i-1];
1347 else
1348 {
1349 int j;
1350 BOOL asian = FALSE;
1351 enum usp10_script first_script = scripts[i-1];
1352 for (j = i-1; j >= 0 && scripts[j] == first_script && pwcInChars[j] != Numeric_space; j--)
1353 {
1354 enum usp10_script original = scripts[j];
1355 if (original == Script_Ideograph || original == Script_Kana || original == Script_Yi || original == Script_CJK_Han || original == Script_Bopomofo)
1356 {
1357 asian = TRUE;
1358 break;
1359 }
1360 if (original != Script_MathAlpha && scriptInformation[scripts[j]].props.fComplex)
1361 break;
1362 scripts[j] = scripts[i];
1363 if (original == Script_Punctuation2)
1364 break;
1365 }
1366 if (j >= 0 && (scriptInformation[scripts[j]].props.fComplex || asian))
1367 scripts[i] = scripts[j];
1368 }
1369 }
1370 }
1371 }
1372
1373 for (i = 0; i < cInChars; i++)
1374 {
1375 /* Joiners get merged preferencially right */
1376 if (i > 0 && (pwcInChars[i] == ZWJ || pwcInChars[i] == ZWNJ || pwcInChars[i] == ZWSP))
1377 {
1378 int j;
1379 if (i+1 == cInChars)
1380 scripts[i] = scripts[i-1];
1381 else
1382 {
1383 for (j = i+1; j < cInChars; j++)
1384 {
1385 if (pwcInChars[j] != ZWJ && pwcInChars[j] != ZWNJ
1386 && pwcInChars[j] != ZWSP && pwcInChars[j] != Numeric_space)
1387 {
1388 scripts[i] = scripts[j];
1389 break;
1390 }
1391 }
1392 }
1393 }
1394 }
1395
1396 if (psState && psControl)
1397 {
1398 levels = heap_alloc_zero(cInChars * sizeof(WORD));
1399 if (!levels)
1400 goto nomemory;
1401
1402 overrides = heap_alloc_zero(cInChars * sizeof(WORD));
1403 if (!overrides)
1404 goto nomemory;
1405
1406 layout_levels = heap_alloc_zero(cInChars * sizeof(WORD));
1407 if (!layout_levels)
1408 goto nomemory;
1409
1410 if (psState->fOverrideDirection)
1411 {
1412 if (!forceLevels)
1413 {
1414 SCRIPT_STATE s = *psState;
1415 s.fOverrideDirection = FALSE;
1416 BIDI_DetermineLevels(pwcInChars, cInChars, &s, psControl, layout_levels, overrides);
1417 if (odd(layout_levels[0]))
1418 forceLevels = TRUE;
1419 else for (i = 0; i < cInChars; i++)
1420 if (layout_levels[i]!=layout_levels[0])
1421 {
1422 forceLevels = TRUE;
1423 break;
1424 }
1425 }
1426
1427 BIDI_DetermineLevels(pwcInChars, cInChars, psState, psControl, levels, overrides);
1428 }
1429 else
1430 {
1431 BIDI_DetermineLevels(pwcInChars, cInChars, psState, psControl, levels, overrides);
1432 memcpy(layout_levels, levels, cInChars * sizeof(WORD));
1433 }
1434 baselevel = levels[0];
1435 baselayout = layout_levels[0];
1436 for (i = 0; i < cInChars; i++)
1437 if (levels[i]!=levels[0])
1438 break;
1439 if (i >= cInChars && !odd(baselevel) && !odd(psState->uBidiLevel) && !forceLevels)
1440 {
1441 heap_free(levels);
1442 heap_free(overrides);
1443 heap_free(layout_levels);
1444 overrides = NULL;
1445 levels = NULL;
1446 layout_levels = NULL;
1447 }
1448 else
1449 {
1450 static const WCHAR math_punc[] = {'#','$','%','+',',','-','.','/',':',0x2212, 0x2044, 0x00a0,0};
1451 static const WCHAR repeatable_math_punc[] = {'#','$','%','+','-','/',0x2212, 0x2044,0};
1452
1453 strength = heap_alloc_zero(cInChars * sizeof(WORD));
1454 if (!strength)
1455 goto nomemory;
1456 BIDI_GetStrengths(pwcInChars, cInChars, psControl, strength);
1457
1458 /* We currently mis-level leading Diacriticals */
1459 if (scripts[0] == Script_Diacritical)
1460 for (i = 0; i < cInChars && scripts[0] == Script_Diacritical; i++)
1461 {
1462 levels[i] = odd(levels[i])?levels[i]+1:levels[i];
1463 strength[i] = BIDI_STRONG;
1464 }
1465
1466 /* Math punctuation bordered on both sides by numbers can be
1467 merged into the number */
1468 for (i = 0; i < cInChars; i++)
1469 {
1470 if (i > 0 && i < cInChars-1 &&
1471 script_is_numeric(scripts[i-1]) &&
1472 strchrW(math_punc, pwcInChars[i]))
1473 {
1474 if (script_is_numeric(scripts[i+1]))
1475 {
1476 scripts[i] = scripts[i+1];
1477 levels[i] = levels[i-1];
1478 strength[i] = strength[i-1];
1479 i++;
1480 }
1481 else if (strchrW(repeatable_math_punc, pwcInChars[i]))
1482 {
1483 int j;
1484 for (j = i+1; j < cInChars; j++)
1485 {
1486 if (script_is_numeric(scripts[j]))
1487 {
1488 for(;i<j; i++)
1489 {
1490 scripts[i] = scripts[j];
1491 levels[i] = levels[i-1];
1492 strength[i] = strength[i-1];
1493 }
1494 }
1495 else if (pwcInChars[i] != pwcInChars[j]) break;
1496 }
1497 }
1498 }
1499 }
1500
1501 for (i = 0; i < cInChars; i++)
1502 {
1503 /* Numerics at level 0 get bumped to level 2 */
1504 if (!overrides[i] && (levels[i] == 0 || (odd(psState->uBidiLevel)
1505 && levels[i] == psState->uBidiLevel + 1)) && script_is_numeric(scripts[i]))
1506 {
1507 levels[i] = 2;
1508 }
1509
1510 /* Joiners get merged preferencially right */
1511 if (i > 0 && (pwcInChars[i] == ZWJ || pwcInChars[i] == ZWNJ || pwcInChars[i] == ZWSP))
1512 {
1513 int j;
1514 if (i+1 == cInChars && levels[i-1] == levels[i])
1515 strength[i] = strength[i-1];
1516 else
1517 for (j = i+1; j < cInChars && levels[i] == levels[j]; j++)
1518 if (pwcInChars[j] != ZWJ && pwcInChars[j] != ZWNJ
1519 && pwcInChars[j] != ZWSP && pwcInChars[j] != Numeric_space)
1520 {
1521 strength[i] = strength[j];
1522 break;
1523 }
1524 }
1525 }
1526 if (psControl->fMergeNeutralItems)
1527 {
1528 /* Merge the neutrals */
1529 for (i = 0; i < cInChars; i++)
1530 {
1531 if (strength[i] == BIDI_NEUTRAL || strength[i] == BIDI_WEAK)
1532 {
1533 int j;
1534 for (j = i; j > 0; j--)
1535 {
1536 if (levels[i] != levels[j])
1537 break;
1538 if ((strength[j] == BIDI_STRONG) || (strength[i] == BIDI_NEUTRAL && strength[j] == BIDI_WEAK))
1539 {
1540 scripts[i] = scripts[j];
1541 strength[i] = strength[j];
1542 break;
1543 }
1544 }
1545 }
1546 /* Try going the other way */
1547 if (strength[i] == BIDI_NEUTRAL || strength[i] == BIDI_WEAK)
1548 {
1549 int j;
1550 for (j = i; j < cInChars; j++)
1551 {
1552 if (levels[i] != levels[j])
1553 break;
1554 if ((strength[j] == BIDI_STRONG) || (strength[i] == BIDI_NEUTRAL && strength[j] == BIDI_WEAK))
1555 {
1556 scripts[i] = scripts[j];
1557 strength[i] = strength[j];
1558 break;
1559 }
1560 }
1561 }
1562 }
1563 }
1564 }
1565 }
1566
1567 while ((!levels || (levels && cnt+1 < cInChars && levels[cnt+1] == levels[0]))
1568 && (cnt < cInChars && pwcInChars[cnt] == Numeric_space))
1569 cnt++;
1570
1571 if (cnt == cInChars) /* All Spaces */
1572 {
1573 cnt = 0;
1574 New_Script = scripts[cnt];
1575 }
1576
1577 pItems[index].iCharPos = 0;
1578 pItems[index].a = scriptInformation[scripts[cnt]].a;
1579 if (pScriptTags)
1580 pScriptTags[index] = scriptInformation[scripts[cnt]].scriptTag;
1581
1582 if (strength && strength[cnt] == BIDI_STRONG)
1583 str = strength[cnt];
1584 else if (strength)
1585 str = strength[0];
1586
1587 cnt = 0;
1588
1589 if (levels)
1590 {
1591 if (strength[cnt] == BIDI_STRONG)
1592 layoutRTL = odd(layout_levels[cnt]);
1593 else
1594 layoutRTL = (psState->uBidiLevel || odd(layout_levels[cnt]));
1595 if (overrides)
1596 pItems[index].a.s.fOverrideDirection = (overrides[cnt] != 0);
1597 pItems[index].a.fRTL = odd(levels[cnt]);
1598 if (script_is_numeric(pItems[index].a.eScript))
1599 pItems[index].a.fLayoutRTL = layoutRTL;
1600 else
1601 pItems[index].a.fLayoutRTL = pItems[index].a.fRTL;
1602 pItems[index].a.s.uBidiLevel = levels[cnt];
1603 }
1604 else if (!pItems[index].a.s.uBidiLevel || (overrides && overrides[cnt]))
1605 {
1606 if (pItems[index].a.s.uBidiLevel != baselevel)
1607 pItems[index].a.s.fOverrideDirection = TRUE;
1608 layoutRTL = odd(baselayout);
1609 pItems[index].a.s.uBidiLevel = baselevel;
1610 pItems[index].a.fRTL = odd(baselevel);
1611 if (script_is_numeric(pItems[index].a.eScript))
1612 pItems[index].a.fLayoutRTL = odd(baselayout);
1613 else
1614 pItems[index].a.fLayoutRTL = pItems[index].a.fRTL;
1615 }
1616
1617 TRACE("New_Level=%i New_Strength=%i New_Script=%d, eScript=%d index=%d cnt=%d iCharPos=%d\n",
1618 levels?levels[cnt]:-1, str, New_Script, pItems[index].a.eScript, index, cnt,
1619 pItems[index].iCharPos);
1620
1621 for (cnt=1; cnt < cInChars; cnt++)
1622 {
1623 if(pwcInChars[cnt] != Numeric_space)
1624 New_Script = scripts[cnt];
1625 else if (levels)
1626 {
1627 int j = 1;
1628 while (cnt + j < cInChars - 1 && pwcInChars[cnt+j] == Numeric_space && levels[cnt] == levels[cnt+j])
1629 j++;
1630 if (cnt + j < cInChars && levels[cnt] == levels[cnt+j])
1631 New_Script = scripts[cnt+j];
1632 else
1633 New_Script = scripts[cnt];
1634 }
1635
1636 new_run = FALSE;
1637 /* merge space strengths*/
1638 if (strength && strength[cnt] == BIDI_STRONG && str != BIDI_STRONG && New_Script == pItems[index].a.eScript)
1639 str = BIDI_STRONG;
1640
1641 if (strength && strength[cnt] == BIDI_NEUTRAL && str == BIDI_STRONG && pwcInChars[cnt] != Numeric_space && New_Script == pItems[index].a.eScript)
1642 str = BIDI_NEUTRAL;
1643
1644 /* changes in level */
1645 if (levels && (levels[cnt] != pItems[index].a.s.uBidiLevel))
1646 {
1647 TRACE("Level break(%i/%i)\n",pItems[index].a.s.uBidiLevel,levels[cnt]);
1648 new_run = TRUE;
1649 }
1650 /* changes in strength */
1651 else if (strength && pwcInChars[cnt] != Numeric_space && str != strength[cnt])
1652 {
1653 TRACE("Strength break (%i/%i)\n",str,strength[cnt]);
1654 new_run = TRUE;
1655 }
1656 /* changes in script */
1657 else if (((pwcInChars[cnt] != Numeric_space) && (New_Script != -1) && (New_Script != pItems[index].a.eScript)) || (New_Script == Script_Control))
1658 {
1659 TRACE("Script break(%i/%i)\n",pItems[index].a.eScript,New_Script);
1660 new_run = TRUE;
1661 }
1662
1663 if (!new_run && strength && str == BIDI_STRONG)
1664 {
1665 layoutRTL = odd(layout_levels[cnt]);
1666 if (script_is_numeric(pItems[index].a.eScript))
1667 pItems[index].a.fLayoutRTL = layoutRTL;
1668 }
1669
1670 if (new_run)
1671 {
1672 TRACE("New_Level = %i, New_Strength = %i, New_Script=%d, eScript=%d\n", levels?levels[cnt]:-1, strength?strength[cnt]:str, New_Script, pItems[index].a.eScript);
1673
1674 index++;
1675 if (index+1 > cMaxItems)
1676 goto nomemory;
1677
1678 if (strength)
1679 str = strength[cnt];
1680
1681 pItems[index].iCharPos = cnt;
1682 memset(&pItems[index].a, 0, sizeof(SCRIPT_ANALYSIS));
1683
1684 pItems[index].a = scriptInformation[New_Script].a;
1685 if (pScriptTags)
1686 pScriptTags[index] = scriptInformation[New_Script].scriptTag;
1687 if (levels)
1688 {
1689 if (overrides)
1690 pItems[index].a.s.fOverrideDirection = (overrides[cnt] != 0);
1691 if (layout_levels[cnt] == 0)
1692 layoutRTL = 0;
1693 else
1694 layoutRTL = (layoutRTL || odd(layout_levels[cnt]));
1695 pItems[index].a.fRTL = odd(levels[cnt]);
1696 if (script_is_numeric(pItems[index].a.eScript))
1697 pItems[index].a.fLayoutRTL = layoutRTL;
1698 else
1699 pItems[index].a.fLayoutRTL = pItems[index].a.fRTL;
1700 pItems[index].a.s.uBidiLevel = levels[cnt];
1701 }
1702 else if (!pItems[index].a.s.uBidiLevel || (overrides && overrides[cnt]))
1703 {
1704 if (pItems[index].a.s.uBidiLevel != baselevel)
1705 pItems[index].a.s.fOverrideDirection = TRUE;
1706 pItems[index].a.s.uBidiLevel = baselevel;
1707 pItems[index].a.fRTL = odd(baselevel);
1708 if (script_is_numeric(pItems[index].a.eScript))
1709 pItems[index].a.fLayoutRTL = layoutRTL;
1710 else
1711 pItems[index].a.fLayoutRTL = pItems[index].a.fRTL;
1712 }
1713
1714 TRACE("index=%d cnt=%d iCharPos=%d\n", index, cnt, pItems[index].iCharPos);
1715 }
1716 }
1717
1718 /* While not strictly necessary according to the spec, make sure the n+1
1719 * item is set up to prevent random behaviour if the caller erroneously
1720 * checks the n+1 structure */
1721 index++;
1722 if (index + 1 > cMaxItems) goto nomemory;
1723 memset(&pItems[index].a, 0, sizeof(SCRIPT_ANALYSIS));
1724
1725 TRACE("index=%d cnt=%d iCharPos=%d\n", index, cnt, pItems[index].iCharPos);
1726
1727 /* Set one SCRIPT_STATE item being returned */
1728 if (pcItems) *pcItems = index;
1729
1730 /* Set SCRIPT_ITEM */
1731 pItems[index].iCharPos = cnt; /* the last item contains the ptr to the lastchar */
1732 res = S_OK;
1733 nomemory:
1734 heap_free(levels);
1735 heap_free(overrides);
1736 heap_free(layout_levels);
1737 heap_free(strength);
1738 heap_free(scripts);
1739 return res;
1740 }
1741
1742 /***********************************************************************
1743 * ScriptItemizeOpenType (USP10.@)
1744 *
1745 * Split a Unicode string into shapeable parts.
1746 *
1747 * PARAMS
1748 * pwcInChars [I] String to split.
1749 * cInChars [I] Number of characters in pwcInChars.
1750 * cMaxItems [I] Maximum number of items to return.
1751 * psControl [I] Pointer to a SCRIPT_CONTROL structure.
1752 * psState [I] Pointer to a SCRIPT_STATE structure.
1753 * pItems [O] Buffer to receive SCRIPT_ITEM structures.
1754 * pScriptTags [O] Buffer to receive OPENTYPE_TAGs.
1755 * pcItems [O] Number of script items returned.
1756 *
1757 * RETURNS
1758 * Success: S_OK
1759 * Failure: Non-zero HRESULT value.
1760 */
1761 HRESULT WINAPI ScriptItemizeOpenType(const WCHAR *pwcInChars, int cInChars, int cMaxItems,
1762 const SCRIPT_CONTROL *psControl, const SCRIPT_STATE *psState,
1763 SCRIPT_ITEM *pItems, OPENTYPE_TAG *pScriptTags, int *pcItems)
1764 {
1765 return _ItemizeInternal(pwcInChars, cInChars, cMaxItems, psControl, psState, pItems, pScriptTags, pcItems);
1766 }
1767
1768 /***********************************************************************
1769 * ScriptItemize (USP10.@)
1770 *
1771 * Split a Unicode string into shapeable parts.
1772 *
1773 * PARAMS
1774 * pwcInChars [I] String to split.
1775 * cInChars [I] Number of characters in pwcInChars.
1776 * cMaxItems [I] Maximum number of items to return.
1777 * psControl [I] Pointer to a SCRIPT_CONTROL structure.
1778 * psState [I] Pointer to a SCRIPT_STATE structure.
1779 * pItems [O] Buffer to receive SCRIPT_ITEM structures.
1780 * pcItems [O] Number of script items returned.
1781 *
1782 * RETURNS
1783 * Success: S_OK
1784 * Failure: Non-zero HRESULT value.
1785 */
1786 HRESULT WINAPI ScriptItemize(const WCHAR *pwcInChars, int cInChars, int cMaxItems,
1787 const SCRIPT_CONTROL *psControl, const SCRIPT_STATE *psState,
1788 SCRIPT_ITEM *pItems, int *pcItems)
1789 {
1790 return _ItemizeInternal(pwcInChars, cInChars, cMaxItems, psControl, psState, pItems, NULL, pcItems);
1791 }
1792
1793 static inline int getGivenTabWidth(ScriptCache *psc, SCRIPT_TABDEF *pTabdef, int charPos, int current_x)
1794 {
1795 int defWidth;
1796 int cTabStops=0;
1797 INT *lpTabPos = NULL;
1798 INT nTabOrg = 0;
1799 INT x = 0;
1800
1801 if (pTabdef)
1802 lpTabPos = pTabdef->pTabStops;
1803
1804 if (pTabdef && pTabdef->iTabOrigin)
1805 {
1806 if (pTabdef->iScale)
1807 nTabOrg = (pTabdef->iTabOrigin * pTabdef->iScale)/4;
1808 else
1809 nTabOrg = pTabdef->iTabOrigin * psc->tm.tmAveCharWidth;
1810 }
1811
1812 if (pTabdef)
1813 cTabStops = pTabdef->cTabStops;
1814
1815 if (cTabStops == 1)
1816 {
1817 if (pTabdef->iScale)
1818 defWidth = ((pTabdef->pTabStops[0])*pTabdef->iScale) / 4;
1819 else
1820 defWidth = (pTabdef->pTabStops[0])*psc->tm.tmAveCharWidth;
1821 cTabStops = 0;
1822 }
1823 else
1824 {
1825 if (pTabdef->iScale)
1826 defWidth = (32 * pTabdef->iScale) / 4;
1827 else
1828 defWidth = 8 * psc->tm.tmAveCharWidth;
1829 }
1830
1831 for (; cTabStops>0 ; lpTabPos++, cTabStops--)
1832 {
1833 int position = *lpTabPos;
1834 if (position < 0)
1835 position = -1 * position;
1836 if (pTabdef->iScale)
1837 position = (position * pTabdef->iScale) / 4;
1838 else
1839 position = position * psc->tm.tmAveCharWidth;
1840
1841 if( nTabOrg + position > current_x)
1842 {
1843 if( position >= 0)
1844 {
1845 /* a left aligned tab */
1846 x = (nTabOrg + position) - current_x;
1847 break;
1848 }
1849 else
1850 {
1851 FIXME("Negative tabstop\n");
1852 break;
1853 }
1854 }
1855 }
1856 if ((!cTabStops) && (defWidth > 0))
1857 x =((((current_x - nTabOrg) / defWidth)+1) * defWidth) - current_x;
1858 else if ((!cTabStops) && (defWidth < 0))
1859 FIXME("TODO: Negative defWidth\n");
1860
1861 return x;
1862 }
1863
1864 /***********************************************************************
1865 * Helper function for ScriptStringAnalyse
1866 */
1867 static BOOL requires_fallback(HDC hdc, SCRIPT_CACHE *psc, SCRIPT_ANALYSIS *psa,
1868 const WCHAR *pwcInChars, int cChars )
1869 {
1870 /* FIXME: When to properly fallback is still a bit of a mystery */
1871 WORD *glyphs;
1872
1873 if (psa->fNoGlyphIndex)
1874 return FALSE;
1875
1876 if (init_script_cache(hdc, psc) != S_OK)
1877 return FALSE;
1878
1879 if (SHAPE_CheckFontForRequiredFeatures(hdc, (ScriptCache *)*psc, psa) != S_OK)
1880 return TRUE;
1881
1882 glyphs = heap_alloc(sizeof(WORD) * cChars);
1883 if (!glyphs)
1884 return FALSE;
1885 if (ScriptGetCMap(hdc, psc, pwcInChars, cChars, 0, glyphs) != S_OK)
1886 {
1887 heap_free(glyphs);
1888 return TRUE;
1889 }
1890 heap_free(glyphs);
1891
1892 return FALSE;
1893 }
1894
1895 static void find_fallback_font(enum usp10_script scriptid, WCHAR *FaceName)
1896 {
1897 HKEY hkey;
1898
1899 if (!RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Uniscribe\\Fallback", &hkey))
1900 {
1901 static const WCHAR szFmt[] = {'%','x',0};
1902 WCHAR value[10];
1903 DWORD count = LF_FACESIZE * sizeof(WCHAR);
1904 DWORD type;
1905
1906 sprintfW(value, szFmt, scriptInformation[scriptid].scriptTag);
1907 if (RegQueryValueExW(hkey, value, 0, &type, (BYTE *)FaceName, &count))
1908 lstrcpyW(FaceName,scriptInformation[scriptid].fallbackFont);
1909 RegCloseKey(hkey);
1910 }
1911 else
1912 lstrcpyW(FaceName,scriptInformation[scriptid].fallbackFont);
1913 }
1914
1915 /***********************************************************************
1916 * ScriptStringAnalyse (USP10.@)
1917 *
1918 */
1919 HRESULT WINAPI ScriptStringAnalyse(HDC hdc, const void *pString, int cString,
1920 int cGlyphs, int iCharset, DWORD dwFlags,
1921 int iReqWidth, SCRIPT_CONTROL *psControl,
1922 SCRIPT_STATE *psState, const int *piDx,
1923 SCRIPT_TABDEF *pTabdef, const BYTE *pbInClass,
1924 SCRIPT_STRING_ANALYSIS *pssa)
1925 {
1926 HRESULT hr = E_OUTOFMEMORY;
1927 StringAnalysis *analysis = NULL;
1928 SCRIPT_CONTROL sControl;
1929 SCRIPT_STATE sState;
1930 int i, num_items = 255;
1931 BYTE *BidiLevel;
1932 WCHAR *iString = NULL;
1933
1934 TRACE("(%p,%p,%d,%d,%d,0x%x,%d,%p,%p,%p,%p,%p,%p)\n",
1935 hdc, pString, cString, cGlyphs, iCharset, dwFlags, iReqWidth,
1936 psControl, psState, piDx, pTabdef, pbInClass, pssa);
1937
1938 if (iCharset != -1)
1939 {
1940 FIXME("Only Unicode strings are supported\n");
1941 return E_INVALIDARG;
1942 }
1943 if (cString < 1 || !pString) return E_INVALIDARG;
1944 if ((dwFlags & SSA_GLYPHS) && !hdc) return E_PENDING;
1945
1946 if (!(analysis = heap_alloc_zero(sizeof(StringAnalysis)))) return E_OUTOFMEMORY;
1947 if (!(analysis->pItem = heap_alloc_zero(num_items * sizeof(SCRIPT_ITEM) + 1))) goto error;
1948
1949 /* FIXME: handle clipping */
1950 analysis->clip_len = cString;
1951 analysis->hdc = hdc;
1952 analysis->ssa_flags = dwFlags;
1953
1954 if (psState)
1955 sState = *psState;
1956 else
1957 memset(&sState, 0, sizeof(SCRIPT_STATE));
1958
1959 if (psControl)
1960 sControl = *psControl;
1961 else
1962 memset(&sControl, 0, sizeof(SCRIPT_CONTROL));
1963
1964 if (dwFlags & SSA_PASSWORD)
1965 {
1966 iString = heap_alloc(sizeof(WCHAR)*cString);
1967 if (!iString)
1968 {
1969 hr = E_OUTOFMEMORY;
1970 goto error;
1971 }
1972 for (i = 0; i < cString; i++)
1973 iString[i] = *((const WCHAR *)pString);
1974 pString = iString;
1975 }
1976
1977 hr = ScriptItemize(pString, cString, num_items, &sControl, &sState, analysis->pItem,
1978 &analysis->numItems);
1979
1980 if (FAILED(hr))
1981 {
1982 if (hr == E_OUTOFMEMORY)
1983 hr = E_INVALIDARG;
1984 goto error;
1985 }
1986
1987 /* set back to out of memory for default goto error behaviour */
1988 hr = E_OUTOFMEMORY;
1989
1990 if (dwFlags & SSA_BREAK)
1991 {
1992 if ((analysis->logattrs = heap_alloc(sizeof(SCRIPT_LOGATTR) * cString)))
1993 {
1994 for (i = 0; i < analysis->numItems; i++)
1995 ScriptBreak(&((WCHAR *)pString)[analysis->pItem[i].iCharPos],
1996 analysis->pItem[i + 1].iCharPos - analysis->pItem[i].iCharPos,
1997 &analysis->pItem[i].a, &analysis->logattrs[analysis->pItem[i].iCharPos]);
1998 }
1999 else
2000 goto error;
2001 }
2002
2003 if (!(analysis->logical2visual = heap_alloc_zero(sizeof(int) * analysis->numItems)))
2004 goto error;
2005 if (!(BidiLevel = heap_alloc_zero(analysis->numItems)))
2006 goto error;
2007
2008 if (dwFlags & SSA_GLYPHS)
2009 {
2010 int tab_x = 0;
2011 if (!(analysis->glyphs = heap_alloc_zero(sizeof(StringGlyphs) * analysis->numItems)))
2012 {
2013 heap_free(BidiLevel);
2014 goto error;
2015 }
2016
2017 for (i = 0; i < analysis->numItems; i++)
2018 {
2019 SCRIPT_CACHE *sc = (SCRIPT_CACHE*)&analysis->glyphs[i].sc;
2020 int cChar = analysis->pItem[i+1].iCharPos - analysis->pItem[i].iCharPos;
2021 int numGlyphs = 1.5 * cChar + 16;
2022 WORD *glyphs = heap_alloc_zero(sizeof(WORD) * numGlyphs);
2023 WORD *pwLogClust = heap_alloc_zero(sizeof(WORD) * cChar);
2024 int *piAdvance = heap_alloc_zero(sizeof(int) * numGlyphs);
2025 SCRIPT_VISATTR *psva = heap_alloc_zero(sizeof(SCRIPT_VISATTR) * numGlyphs);
2026 GOFFSET *pGoffset = heap_alloc_zero(sizeof(GOFFSET) * numGlyphs);
2027 int numGlyphsReturned;
2028 HFONT originalFont = 0x0;
2029
2030 /* FIXME: non unicode strings */
2031 const WCHAR* pStr = (const WCHAR*)pString;
2032 analysis->glyphs[i].fallbackFont = NULL;
2033
2034 if (!glyphs || !pwLogClust || !piAdvance || !psva || !pGoffset)
2035 {
2036 heap_free (BidiLevel);
2037 heap_free (glyphs);
2038 heap_free (pwLogClust);
2039 heap_free (piAdvance);
2040 heap_free (psva);
2041 heap_free (pGoffset);
2042 hr = E_OUTOFMEMORY;
2043 goto error;
2044 }
2045
2046 if ((dwFlags & SSA_FALLBACK) && requires_fallback(hdc, sc, &analysis->pItem[i].a, &pStr[analysis->pItem[i].iCharPos], cChar))
2047 {
2048 LOGFONTW lf;
2049 GetObjectW(GetCurrentObject(hdc, OBJ_FONT), sizeof(lf), & lf);
2050 lf.lfCharSet = scriptInformation[analysis->pItem[i].a.eScript].props.bCharSet;
2051 lf.lfFaceName[0] = 0;
2052 find_fallback_font(analysis->pItem[i].a.eScript, lf.lfFaceName);
2053 if (lf.lfFaceName[0])
2054 {
2055 analysis->glyphs[i].fallbackFont = CreateFontIndirectW(&lf);
2056 if (analysis->glyphs[i].fallbackFont)
2057 {
2058 ScriptFreeCache(sc);
2059 originalFont = SelectObject(hdc, analysis->glyphs[i].fallbackFont);
2060 }
2061 }
2062 }
2063
2064 /* FIXME: When we properly shape Hangul remove this check */
2065 if ((dwFlags & SSA_LINK) && !analysis->glyphs[i].fallbackFont && analysis->pItem[i].a.eScript == Script_Hangul)
2066 analysis->pItem[i].a.fNoGlyphIndex = TRUE;
2067
2068 if ((dwFlags & SSA_LINK) && !analysis->glyphs[i].fallbackFont && !scriptInformation[analysis->pItem[i].a.eScript].props.fComplex && !analysis->pItem[i].a.fRTL)
2069 analysis->pItem[i].a.fNoGlyphIndex = TRUE;
2070
2071 ScriptShape(hdc, sc, &pStr[analysis->pItem[i].iCharPos], cChar, numGlyphs,
2072 &analysis->pItem[i].a, glyphs, pwLogClust, psva, &numGlyphsReturned);
2073 hr = ScriptPlace(hdc, sc, glyphs, numGlyphsReturned, psva, &analysis->pItem[i].a,
2074 piAdvance, pGoffset, &analysis->glyphs[i].abc);
2075 if (originalFont)
2076 SelectObject(hdc,originalFont);
2077
2078 if (dwFlags & SSA_TAB)
2079 {
2080 int tabi = 0;
2081 for (tabi = 0; tabi < cChar; tabi++)
2082 {
2083 if (pStr[analysis->pItem[i].iCharPos+tabi] == 0x0009)
2084 piAdvance[tabi] = getGivenTabWidth(analysis->glyphs[i].sc, pTabdef, analysis->pItem[i].iCharPos+tabi, tab_x);
2085 tab_x+=piAdvance[tabi];
2086 }
2087 }
2088
2089 analysis->glyphs[i].numGlyphs = numGlyphsReturned;
2090 analysis->glyphs[i].glyphs = glyphs;
2091 analysis->glyphs[i].pwLogClust = pwLogClust;
2092 analysis->glyphs[i].piAdvance = piAdvance;
2093 analysis->glyphs[i].psva = psva;
2094 analysis->glyphs[i].pGoffset = pGoffset;
2095 analysis->glyphs[i].iMaxPosX= -1;
2096
2097 BidiLevel[i] = analysis->pItem[i].a.s.uBidiLevel;
2098 }
2099 }
2100 else
2101 {
2102 for (i = 0; i < analysis->numItems; i++)
2103 BidiLevel[i] = analysis->pItem[i].a.s.uBidiLevel;
2104 }
2105
2106 ScriptLayout(analysis->numItems, BidiLevel, NULL, analysis->logical2visual);
2107 heap_free(BidiLevel);
2108
2109 *pssa = analysis;
2110 heap_free(iString);
2111 return S_OK;
2112
2113 error:
2114 heap_free(iString);
2115 heap_free(analysis->glyphs);
2116 heap_free(analysis->logattrs);
2117 heap_free(analysis->pItem);
2118 heap_free(analysis->logical2visual);
2119 heap_free(analysis);
2120 return hr;
2121 }
2122
2123 static inline BOOL does_glyph_start_cluster(const SCRIPT_VISATTR *pva, const WORD *pwLogClust, int cChars, int glyph, int direction)
2124 {
2125 if (pva[glyph].fClusterStart)
2126 return TRUE;
2127 if (USP10_FindGlyphInLogClust(pwLogClust, cChars, glyph) >= 0)
2128 return TRUE;
2129
2130 return FALSE;
2131 }
2132
2133
2134 static HRESULT SS_ItemOut( SCRIPT_STRING_ANALYSIS ssa,
2135 int iX,
2136 int iY,
2137 int iItem,
2138 int cStart,
2139 int cEnd,
2140 UINT uOptions,
2141 const RECT *prc,
2142 BOOL fSelected,
2143 BOOL fDisabled)
2144 {
2145 StringAnalysis *analysis;
2146 int off_x = 0;
2147 HRESULT hr;
2148 COLORREF BkColor = 0x0;
2149 COLORREF TextColor = 0x0;
2150 INT BkMode = 0;
2151 INT runStart, runEnd;
2152 INT iGlyph, cGlyphs;
2153 HFONT oldFont = 0x0;
2154 RECT crc;
2155 int i;
2156
2157 TRACE("(%p,%d,%d,%d,%d,%d, 0x%1x, %d, %d)\n",
2158 ssa, iX, iY, iItem, cStart, cEnd, uOptions, fSelected, fDisabled);
2159
2160 if (!(analysis = ssa)) return E_INVALIDARG;
2161
2162 if ((cStart >= 0 && analysis->pItem[iItem+1].iCharPos <= cStart) ||
2163 (cEnd >= 0 && analysis->pItem[iItem].iCharPos >= cEnd))
2164 return S_OK;
2165
2166 CopyRect(&crc,prc);
2167 if (fSelected)
2168 {
2169 BkMode = GetBkMode(analysis->hdc);
2170 SetBkMode( analysis->hdc, OPAQUE);
2171 BkColor = GetBkColor(analysis->hdc);
2172 SetBkColor(analysis->hdc, GetSysColor(COLOR_HIGHLIGHT));
2173 if (!fDisabled)
2174 {
2175 TextColor = GetTextColor(analysis->hdc);
2176 SetTextColor(analysis->hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
2177 }
2178 }
2179 if (analysis->glyphs[iItem].fallbackFont)
2180 oldFont = SelectObject(analysis->hdc, analysis->glyphs[iItem].fallbackFont);
2181
2182 if (cStart >= 0 && analysis->pItem[iItem+1].iCharPos > cStart && analysis->pItem[iItem].iCharPos <= cStart)
2183 runStart = cStart - analysis->pItem[iItem].iCharPos;
2184 else
2185 runStart = 0;
2186 if (cEnd >= 0 && analysis->pItem[iItem+1].iCharPos > cEnd && analysis->pItem[iItem].iCharPos <= cEnd)
2187 runEnd = (cEnd-1) - analysis->pItem[iItem].iCharPos;
2188 else
2189 runEnd = (analysis->pItem[iItem+1].iCharPos - analysis->pItem[iItem].iCharPos) - 1;
2190
2191 if (analysis->pItem[iItem].a.fRTL)
2192 {
2193 if (cEnd >= 0 && cEnd < analysis->pItem[iItem+1].iCharPos)
2194 ScriptStringCPtoX(ssa, cEnd, FALSE, &off_x);
2195 else
2196 ScriptStringCPtoX(ssa, analysis->pItem[iItem+1].iCharPos-1, TRUE, &off_x);
2197 crc.left = iX + off_x;
2198 }
2199 else
2200 {
2201 if (cStart >=0 && runStart)
2202 ScriptStringCPtoX(ssa, cStart, FALSE, &off_x);
2203 else
2204 ScriptStringCPtoX(ssa, analysis->pItem[iItem].iCharPos, FALSE, &off_x);
2205 crc.left = iX + off_x;
2206 }
2207
2208 if (analysis->pItem[iItem].a.fRTL)
2209 iGlyph = analysis->glyphs[iItem].pwLogClust[runEnd];
2210 else
2211 iGlyph = analysis->glyphs[iItem].pwLogClust[runStart];
2212
2213 if (analysis->pItem[iItem].a.fRTL)
2214 cGlyphs = analysis->glyphs[iItem].pwLogClust[runStart] - iGlyph;
2215 else
2216 cGlyphs = analysis->glyphs[iItem].pwLogClust[runEnd] - iGlyph;
2217
2218 cGlyphs++;
2219
2220 /* adjust for cluster glyphs when starting */
2221 if (analysis->pItem[iItem].a.fRTL)
2222 i = analysis->pItem[iItem+1].iCharPos - 1;
2223 else
2224 i = analysis->pItem[iItem].iCharPos;
2225
2226 for (; i >=analysis->pItem[iItem].iCharPos && i < analysis->pItem[iItem+1].iCharPos; (analysis->pItem[iItem].a.fRTL)?i--:i++)
2227 {
2228 if (analysis->glyphs[iItem].pwLogClust[i - analysis->pItem[iItem].iCharPos] == iGlyph)
2229 {
2230 if (analysis->pItem[iItem].a.fRTL)
2231 ScriptStringCPtoX(ssa, i, TRUE, &off_x);
2232 else
2233 ScriptStringCPtoX(ssa, i, FALSE, &off_x);
2234 break;
2235 }
2236 }
2237
2238 if (cEnd < 0 || scriptInformation[analysis->pItem[iItem].a.eScript].props.fNeedsCaretInfo)
2239 {
2240 INT direction;
2241 INT clust_glyph;
2242
2243 clust_glyph = iGlyph + cGlyphs;
2244 if (analysis->pItem[iItem].a.fRTL)
2245 direction = -1;
2246 else
2247 direction = 1;
2248
2249 while(clust_glyph < analysis->glyphs[iItem].numGlyphs &&
2250 !does_glyph_start_cluster(analysis->glyphs[iItem].psva, analysis->glyphs[iItem].pwLogClust, (analysis->pItem[iItem+1].iCharPos - analysis->pItem[iItem].iCharPos), clust_glyph, direction))
2251 {
2252 cGlyphs++;
2253 clust_glyph++;
2254 }
2255 }
2256
2257 hr = ScriptTextOut(analysis->hdc,
2258 (SCRIPT_CACHE *)&analysis->glyphs[iItem].sc, iX + off_x,
2259 iY, uOptions, &crc, &analysis->pItem[iItem].a, NULL, 0,
2260 &analysis->glyphs[iItem].glyphs[iGlyph], cGlyphs,
2261 &analysis->glyphs[iItem].piAdvance[iGlyph], NULL,
2262 &analysis->glyphs[iItem].pGoffset[iGlyph]);
2263
2264 TRACE("ScriptTextOut hr=%08x\n", hr);
2265
2266 if (fSelected)
2267 {
2268 SetBkColor(analysis->hdc, BkColor);
2269 SetBkMode( analysis->hdc, BkMode);
2270 if (!fDisabled)
2271 SetTextColor(analysis->hdc, TextColor);
2272 }
2273 if (analysis->glyphs[iItem].fallbackFont)
2274 SelectObject(analysis->hdc, oldFont);
2275
2276 return hr;
2277 }
2278
2279 /***********************************************************************
2280 * ScriptStringOut (USP10.@)
2281 *
2282 * This function takes the output of ScriptStringAnalyse and joins the segments
2283 * of glyphs and passes the resulting string to ScriptTextOut. ScriptStringOut
2284 * only processes glyphs.
2285 *
2286 * Parameters:
2287 * ssa [I] buffer to hold the analysed string components
2288 * iX [I] X axis displacement for output
2289 * iY [I] Y axis displacement for output
2290 * uOptions [I] flags controlling output processing
2291 * prc [I] rectangle coordinates
2292 * iMinSel [I] starting pos for substringing output string
2293 * iMaxSel [I] ending pos for substringing output string
2294 * fDisabled [I] controls text highlighting
2295 *
2296 * RETURNS
2297 * Success: S_OK
2298 * Failure: is the value returned by ScriptTextOut
2299 */
2300 HRESULT WINAPI ScriptStringOut(SCRIPT_STRING_ANALYSIS ssa,
2301 int iX,
2302 int iY,
2303 UINT uOptions,
2304 const RECT *prc,
2305 int iMinSel,
2306 int iMaxSel,
2307 BOOL fDisabled)
2308 {
2309 StringAnalysis *analysis;
2310 int item;
2311 HRESULT hr;
2312
2313 TRACE("(%p,%d,%d,0x%08x,%s,%d,%d,%d)\n",
2314 ssa, iX, iY, uOptions, wine_dbgstr_rect(prc), iMinSel, iMaxSel, fDisabled);
2315
2316 if (!(analysis = ssa)) return E_INVALIDARG;
2317 if (!(analysis->ssa_flags & SSA_GLYPHS)) return E_INVALIDARG;
2318
2319 for (item = 0; item < analysis->numItems; item++)
2320 {
2321 hr = SS_ItemOut( ssa, iX, iY, analysis->logical2visual[item], -1, -1, uOptions, prc, FALSE, fDisabled);
2322 if (FAILED(hr))
2323 return hr;
2324 }
2325
2326 if (iMinSel < iMaxSel && (iMinSel > 0 || iMaxSel > 0))
2327 {
2328 if (iMaxSel > 0 && iMinSel < 0)
2329 iMinSel = 0;
2330 for (item = 0; item < analysis->numItems; item++)
2331 {
2332 hr = SS_ItemOut( ssa, iX, iY, analysis->logical2visual[item], iMinSel, iMaxSel, uOptions, prc, TRUE, fDisabled);
2333 if (FAILED(hr))
2334 return hr;
2335 }
2336 }
2337
2338 return S_OK;
2339 }
2340
2341 /***********************************************************************
2342 * ScriptStringCPtoX (USP10.@)
2343 *
2344 */
2345 HRESULT WINAPI ScriptStringCPtoX(SCRIPT_STRING_ANALYSIS ssa, int icp, BOOL fTrailing, int* pX)
2346 {
2347 int item;
2348 int runningX = 0;
2349 StringAnalysis* analysis = ssa;
2350
2351 TRACE("(%p), %d, %d, (%p)\n", ssa, icp, fTrailing, pX);
2352
2353 if (!ssa || !pX) return S_FALSE;
2354 if (!(analysis->ssa_flags & SSA_GLYPHS)) return S_FALSE;
2355
2356 /* icp out of range */
2357 if(icp < 0)
2358 {
2359 analysis->flags |= SCRIPT_STRING_ANALYSIS_FLAGS_INVALID;
2360 return E_INVALIDARG;
2361 }
2362
2363 for(item=0; item<analysis->numItems; item++)
2364 {
2365 int CP, i;
2366 int offset;
2367
2368 i = analysis->logical2visual[item];
2369 CP = analysis->pItem[i+1].iCharPos - analysis->pItem[i].iCharPos;
2370 /* initialize max extents for uninitialized runs */
2371 if (analysis->glyphs[i].iMaxPosX == -1)
2372 {
2373 if (analysis->pItem[i].a.fRTL)
2374 ScriptCPtoX(0, FALSE, CP, analysis->glyphs[i].numGlyphs, analysis->glyphs[i].pwLogClust,
2375 analysis->glyphs[i].psva, analysis->glyphs[i].piAdvance,
2376 &analysis->pItem[i].a, &analysis->glyphs[i].iMaxPosX);
2377 else
2378 ScriptCPtoX(CP, TRUE, CP, analysis->glyphs[i].numGlyphs, analysis->glyphs[i].pwLogClust,
2379 analysis->glyphs[i].psva, analysis->glyphs[i].piAdvance,
2380 &analysis->pItem[i].a, &analysis->glyphs[i].iMaxPosX);
2381 }
2382
2383 if (icp >= analysis->pItem[i+1].iCharPos || icp < analysis->pItem[i].iCharPos)
2384 {
2385 runningX += analysis->glyphs[i].iMaxPosX;
2386 continue;
2387 }
2388
2389 icp -= analysis->pItem[i].iCharPos;
2390 ScriptCPtoX(icp, fTrailing, CP, analysis->glyphs[i].numGlyphs, analysis->glyphs[i].pwLogClust,
2391 analysis->glyphs[i].psva, analysis->glyphs[i].piAdvance,
2392 &analysis->pItem[i].a, &offset);
2393 runningX += offset;
2394
2395 *pX = runningX;
2396 return S_OK;
2397 }
2398
2399 /* icp out of range */
2400 analysis->flags |= SCRIPT_STRING_ANALYSIS_FLAGS_INVALID;
2401 return E_INVALIDARG;
2402 }
2403
2404 /***********************************************************************
2405 * ScriptStringXtoCP (USP10.@)
2406 *
2407 */
2408 HRESULT WINAPI ScriptStringXtoCP(SCRIPT_STRING_ANALYSIS ssa, int iX, int* piCh, int* piTrailing)
2409 {
2410 StringAnalysis* analysis = ssa;
2411 int item;
2412
2413 TRACE("(%p), %d, (%p), (%p)\n", ssa, iX, piCh, piTrailing);
2414
2415 if (!ssa || !piCh || !piTrailing) return S_FALSE;
2416 if (!(analysis->ssa_flags & SSA_GLYPHS)) return S_FALSE;
2417
2418 /* out of range */
2419 if(iX < 0)
2420 {
2421 if (analysis->pItem[0].a.fRTL)
2422 {
2423 *piCh = 1;
2424 *piTrailing = FALSE;
2425 }
2426 else
2427 {
2428 *piCh = -1;
2429 *piTrailing = TRUE;
2430 }
2431 return S_OK;
2432 }
2433
2434 for(item=0; item<analysis->numItems; item++)
2435 {
2436 int i;
2437 int CP;
2438
2439 for (i = 0; i < analysis->numItems && analysis->logical2visual[i] != item; i++)
2440 /* nothing */;
2441
2442 CP = analysis->pItem[i+1].iCharPos - analysis->pItem[i].iCharPos;
2443 /* initialize max extents for uninitialized runs */
2444 if (analysis->glyphs[i].iMaxPosX == -1)
2445 {
2446 if (analysis->pItem[i].a.fRTL)
2447 ScriptCPtoX(0, FALSE, CP, analysis->glyphs[i].numGlyphs, analysis->glyphs[i].pwLogClust,
2448 analysis->glyphs[i].psva, analysis->glyphs[i].piAdvance,
2449 &analysis->pItem[i].a, &analysis->glyphs[i].iMaxPosX);
2450 else
2451 ScriptCPtoX(CP, TRUE, CP, analysis->glyphs[i].numGlyphs, analysis->glyphs[i].pwLogClust,
2452 analysis->glyphs[i].psva, analysis->glyphs[i].piAdvance,
2453 &analysis->pItem[i].a, &analysis->glyphs[i].iMaxPosX);
2454 }
2455
2456 if (iX > analysis->glyphs[i].iMaxPosX)
2457 {
2458 iX -= analysis->glyphs[i].iMaxPosX;
2459 continue;
2460 }
2461
2462 ScriptXtoCP(iX, CP, analysis->glyphs[i].numGlyphs, analysis->glyphs[i].pwLogClust,
2463 analysis->glyphs[i].psva, analysis->glyphs[i].piAdvance,
2464 &analysis->pItem[i].a, piCh, piTrailing);
2465 *piCh += analysis->pItem[i].iCharPos;
2466
2467 return S_OK;
2468 }
2469
2470 /* out of range */
2471 *piCh = analysis->pItem[analysis->numItems].iCharPos;
2472 *piTrailing = FALSE;
2473
2474 return S_OK;
2475 }
2476
2477
2478 /***********************************************************************
2479 * ScriptStringFree (USP10.@)
2480 *
2481 * Free a string analysis.
2482 *
2483 * PARAMS
2484 * pssa [I] string analysis.
2485 *
2486 * RETURNS
2487 * Success: S_OK
2488 * Failure: Non-zero HRESULT value.
2489 */
2490 HRESULT WINAPI ScriptStringFree(SCRIPT_STRING_ANALYSIS *pssa)
2491 {
2492 StringAnalysis* analysis;
2493 BOOL invalid;
2494 int i;
2495
2496 TRACE("(%p)\n", pssa);
2497
2498 if (!pssa || !(analysis = *pssa)) return E_INVALIDARG;
2499
2500 invalid = analysis->flags & SCRIPT_STRING_ANALYSIS_FLAGS_INVALID;
2501
2502 if (analysis->glyphs)
2503 {
2504 for (i = 0; i < analysis->numItems; i++)
2505 {
2506 heap_free(analysis->glyphs[i].glyphs);
2507 heap_free(analysis->glyphs[i].pwLogClust);
2508 heap_free(analysis->glyphs[i].piAdvance);
2509 heap_free(analysis->glyphs[i].psva);
2510 heap_free(analysis->glyphs[i].pGoffset);
2511 if (analysis->glyphs[i].fallbackFont)
2512 DeleteObject(analysis->glyphs[i].fallbackFont);
2513 ScriptFreeCache((SCRIPT_CACHE *)&analysis->glyphs[i].sc);
2514 heap_free(analysis->glyphs[i].sc);
2515 }
2516 heap_free(analysis->glyphs);
2517 }
2518
2519 heap_free(analysis->pItem);
2520 heap_free(analysis->logattrs);
2521 heap_free(analysis->logical2visual);
2522 heap_free(analysis);
2523
2524 if (invalid) return E_INVALIDARG;
2525 return S_OK;
2526 }
2527
2528 static inline int get_cluster_size(const WORD *pwLogClust, int cChars, int item,
2529 int direction, int* iCluster, int *check_out)
2530 {
2531 int clust_size = 1;
2532 int check;
2533 WORD clust = pwLogClust[item];
2534
2535 for (check = item+direction; check < cChars && check >= 0; check+=direction)
2536 {
2537 if (pwLogClust[check] == clust)
2538 {
2539 clust_size ++;
2540 if (iCluster && *iCluster == -1)
2541 *iCluster = item;
2542 }
2543 else break;
2544 }
2545
2546 if (check_out)
2547 *check_out = check;
2548
2549 return clust_size;
2550 }
2551
2552 static inline int get_glyph_cluster_advance(const int* piAdvance, const SCRIPT_VISATTR *pva, const WORD *pwLogClust, int cGlyphs, int cChars, int glyph, int direction)
2553 {
2554 int advance;
2555 int log_clust_max;
2556
2557 advance = piAdvance[glyph];
2558
2559 if (pwLogClust[0] > pwLogClust[cChars-1])
2560 log_clust_max = pwLogClust[0];
2561 else
2562 log_clust_max = pwLogClust[cChars-1];
2563
2564 if (glyph > log_clust_max)
2565 return advance;
2566
2567 for (glyph+=direction; glyph < cGlyphs && glyph >= 0; glyph +=direction)
2568 {
2569
2570 if (does_glyph_start_cluster(pva, pwLogClust, cChars, glyph, direction))
2571 break;
2572 if (glyph > log_clust_max)
2573 break;
2574 advance += piAdvance[glyph];
2575 }
2576
2577 return advance;
2578 }
2579
2580 /***********************************************************************
2581 * ScriptCPtoX (USP10.@)
2582 *
2583 */
2584 HRESULT WINAPI ScriptCPtoX(int iCP,
2585 BOOL fTrailing,
2586 int cChars,
2587 int cGlyphs,
2588 const WORD *pwLogClust,
2589 const SCRIPT_VISATTR *psva,
2590 const int *piAdvance,
2591 const SCRIPT_ANALYSIS *psa,
2592 int *piX)
2593 {
2594 int item;
2595 float iPosX;
2596 int iSpecial = -1;
2597 int iCluster = -1;
2598 int clust_size = 1;
2599 float special_size = 0.0;
2600 int iMaxPos = 0;
2601 int advance = 0;
2602 BOOL rtl = FALSE;
2603
2604 TRACE("(%d,%d,%d,%d,%p,%p,%p,%p,%p)\n",
2605 iCP, fTrailing, cChars, cGlyphs, pwLogClust, psva, piAdvance,
2606 psa, piX);
2607
2608 if (psa->fRTL && ! psa->fLogicalOrder)
2609 rtl = TRUE;
2610
2611 if (fTrailing)
2612 iCP++;
2613
2614 if (rtl)
2615 {
2616 int max_clust = pwLogClust[0];
2617
2618 for (item=0; item < cGlyphs; item++)
2619 if (pwLogClust[item] > max_clust)
2620 {
2621 ERR("We do not handle non reversed clusters properly\n");
2622 break;
2623 }
2624
2625 iMaxPos = 0;
2626 for (item = max_clust; item >=0; item --)
2627 iMaxPos += piAdvance[item];
2628 }
2629
2630 iPosX = 0.0;
2631 for (item=0; item < iCP && item < cChars; item++)
2632 {
2633 if (iSpecial == -1 && (iCluster == -1 || iCluster+clust_size <= item))
2634 {
2635 int check;
2636 int clust = pwLogClust[item];
2637
2638 iCluster = -1;
2639 clust_size = get_cluster_size(pwLogClust, cChars, item, 1, &iCluster,
2640 &check);
2641
2642 advance = get_glyph_cluster_advance(piAdvance, psva, pwLogClust, cGlyphs, cChars, clust, 1);
2643
2644 if (check >= cChars && !iMaxPos)
2645 {
2646 int glyph;
2647 for (glyph = clust; glyph < cGlyphs; glyph++)
2648 special_size += get_glyph_cluster_advance(piAdvance, psva, pwLogClust, cGlyphs, cChars, glyph, 1);
2649 iSpecial = item;
2650 special_size /= (cChars - item);
2651 iPosX += special_size;
2652 }
2653 else
2654 {
2655 if (scriptInformation[psa->eScript].props.fNeedsCaretInfo)
2656 {
2657 clust_size --;
2658 if (clust_size == 0)
2659 iPosX += advance;
2660 }
2661 else
2662 iPosX += advance / (float)clust_size;
2663 }
2664 }
2665 else if (iSpecial != -1)
2666 iPosX += special_size;
2667 else /* (iCluster != -1) */
2668 {
2669 int adv = get_glyph_cluster_advance(piAdvance, psva, pwLogClust, cGlyphs, cChars, pwLogClust[iCluster], 1);
2670 if (scriptInformation[psa->eScript].props.fNeedsCaretInfo)
2671 {
2672 clust_size --;
2673 if (clust_size == 0)
2674 iPosX += adv;
2675 }
2676 else
2677 iPosX += adv / (float)clust_size;
2678 }
2679 }
2680
2681 if (iMaxPos > 0)
2682 {
2683 iPosX = iMaxPos - iPosX;
2684 if (iPosX < 0)
2685 iPosX = 0;
2686 }
2687
2688 *piX = iPosX;
2689 TRACE("*piX=%d\n", *piX);
2690 return S_OK;
2691 }
2692
2693 /* Count the number of characters in a cluster and its starting index*/
2694 static inline BOOL get_cluster_data(const WORD *pwLogClust, int cChars, int cluster_index, int *cluster_size, int *start_index)
2695 {
2696 int size = 0;
2697 int i;
2698
2699 for (i = 0; i < cChars; i++)
2700 {
2701 if (pwLogClust[i] == cluster_index)
2702 {
2703 if (!size && start_index)
2704 {
2705 *start_index = i;
2706 if (!cluster_size)
2707 return TRUE;
2708 }
2709 size++;
2710 }
2711 else if (size) break;
2712 }
2713 if (cluster_size)
2714 *cluster_size = size;
2715
2716 return (size > 0);
2717 }
2718
2719 /*
2720 To handle multi-glyph clusters we need to find all the glyphs that are
2721 represented in the cluster. This involves finding the glyph whose
2722 index is the cluster index as well as whose glyph indices are greater than
2723 our cluster index but not part of a new cluster.
2724
2725 Then we sum all those glyphs' advances.
2726 */
2727 static inline int get_cluster_advance(const int* piAdvance,
2728 const SCRIPT_VISATTR *psva,
2729 const WORD *pwLogClust, int cGlyphs,
2730 int cChars, int cluster, int direction)
2731 {
2732 int glyph_start;
2733 int glyph_end;
2734 int i, advance;
2735
2736 if (direction > 0)
2737 i = 0;
2738 else
2739 i = (cChars - 1);
2740
2741 for (glyph_start = -1, glyph_end = -1; i < cChars && i >= 0 && (glyph_start < 0 || glyph_end < 0); i+=direction)
2742 {
2743 if (glyph_start < 0 && pwLogClust[i] != cluster) continue;
2744 if (pwLogClust[i] == cluster && glyph_start < 0) glyph_start = pwLogClust[i];
2745 if (glyph_start >= 0 && glyph_end < 0 && pwLogClust[i] != cluster) glyph_end = pwLogClust[i];
2746 }
2747 if (glyph_end < 0)
2748 {
2749 if (direction > 0)
2750 glyph_end = cGlyphs;
2751 else
2752 {
2753 /* Don't fully understand multi-glyph reversed clusters yet,
2754 * do they occur for real or just in our test? */
2755 FIXME("multi-glyph reversed clusters found\n");
2756 glyph_end = glyph_start + 1;
2757 }
2758 }
2759
2760 /* Check for fClusterStart, finding this generally would mean a malformed set of data */
2761 for (i = glyph_start+1; i< glyph_end; i++)
2762 {
2763 if (psva[i].fClusterStart)
2764 {
2765 glyph_end = i;
2766 break;
2767 }
2768 }
2769
2770 for (advance = 0, i = glyph_start; i < glyph_end; i++)
2771 advance += piAdvance[i];
2772
2773 return advance;
2774 }
2775
2776
2777 /***********************************************************************
2778 * ScriptXtoCP (USP10.@)
2779 *
2780 * Basic algorithm :
2781 * Use piAdvance to find the cluster we are looking at.
2782 * Find the character that is the first character of the cluster.
2783 * That is our base piCP.
2784 * If the script snaps to cluster boundaries (Hebrew, Indic, Thai) then we
2785 * are good. Otherwise if the cluster is larger than 1 glyph we need to
2786 * determine how far through the cluster to advance the cursor.
2787 */
2788 HRESULT WINAPI ScriptXtoCP(int iX,
2789 int cChars,
2790 int cGlyphs,
2791 const WORD *pwLogClust,
2792 const SCRIPT_VISATTR *psva,
2793 const int *piAdvance,
2794 const SCRIPT_ANALYSIS *psa,
2795 int *piCP,
2796 int *piTrailing)
2797 {
2798 int direction = 1;
2799 int iPosX;
2800 int i;
2801 int glyph_index, cluster_index;
2802 int cluster_size;
2803
2804 TRACE("(%d,%d,%d,%p,%p,%p,%p,%p,%p)\n",
2805 iX, cChars, cGlyphs, pwLogClust, psva, piAdvance,
2806 psa, piCP, piTrailing);
2807
2808 if (psa->fRTL && ! psa->fLogicalOrder)
2809 direction = -1;
2810
2811 /* Handle an iX < 0 */
2812 if (iX < 0)
2813 {
2814 if (direction < 0)
2815 {
2816 *piCP = cChars;
2817 *piTrailing = 0;
2818 }
2819 else
2820 {
2821 *piCP = -1;
2822 *piTrailing = 1;
2823 }
2824 return S_OK;
2825 }
2826
2827 /* Looking for non-reversed clusters in a reversed string */
2828 if (direction < 0)
2829 {
2830 int max_clust = pwLogClust[0];
2831 for (i=0; i< cChars; i++)
2832 if (pwLogClust[i] > max_clust)
2833 {
2834 FIXME("We do not handle non reversed clusters properly\n");
2835 break;
2836 }
2837 }
2838
2839 /* find the glyph_index based in iX */
2840 if (direction > 0)
2841 {
2842 for (glyph_index = -1, iPosX = iX; iPosX >=0 && glyph_index < cGlyphs; iPosX -= piAdvance[glyph_index+1], glyph_index++)
2843 ;
2844 }
2845 else
2846 {
2847 for (glyph_index = -1, iPosX = iX; iPosX > 0 && glyph_index < cGlyphs; iPosX -= piAdvance[glyph_index+1], glyph_index++)
2848 ;
2849 }
2850
2851 TRACE("iPosX %i -> glyph_index %i (%i)\n", iPosX, glyph_index, cGlyphs);
2852
2853 *piTrailing = 0;
2854 if (glyph_index >= 0 && glyph_index < cGlyphs)
2855 {
2856 /* find the cluster */
2857 if (direction > 0 )
2858 for (i = 0, cluster_index = pwLogClust[0]; i < cChars && pwLogClust[i] <= glyph_index; cluster_index=pwLogClust[i++])
2859 ;
2860 else
2861 for (i = 0, cluster_index = pwLogClust[0]; i < cChars && pwLogClust[i] >= glyph_index; cluster_index=pwLogClust[i++])
2862 ;
2863
2864 TRACE("cluster_index %i\n", cluster_index);
2865
2866 if (direction < 0 && iPosX >= 0 && glyph_index != cluster_index)
2867 {
2868 /* We are off the end of the string */
2869 *piCP = -1;
2870 *piTrailing = 1;
2871 return S_OK;
2872 }
2873
2874 get_cluster_data(pwLogClust, cChars, cluster_index, &cluster_size, &i);
2875
2876 TRACE("first char index %i\n",i);
2877 if (scriptInformation[psa->eScript].props.fNeedsCaretInfo)
2878 {
2879 /* Check trailing */
2880 if (glyph_index != cluster_index ||
2881 (direction > 0 && abs(iPosX) <= (piAdvance[glyph_index] / 2)) ||
2882 (direction < 0 && abs(iPosX) >= (piAdvance[glyph_index] / 2)))
2883 *piTrailing = cluster_size;
2884 }
2885 else
2886 {
2887 if (cluster_size > 1)
2888 {
2889 /* Be part way through the glyph cluster based on size and position */
2890 int cluster_advance = get_cluster_advance(piAdvance, psva, pwLogClust, cGlyphs, cChars, cluster_index, direction);
2891 double cluster_part_width = cluster_advance / (float)cluster_size;
2892 double adv;
2893 int part_index;
2894
2895 /* back up to the beginning of the cluster */
2896 for (adv = iPosX, part_index = cluster_index; part_index <= glyph_index; part_index++)
2897 adv += piAdvance[part_index];
2898 if (adv > iX) adv = iX;
2899
2900 TRACE("Multi-char cluster, no snap\n");
2901 TRACE("cluster size %i, pre-cluster iPosX %f\n",cluster_size, adv);
2902 TRACE("advance %i divides into %f per char\n", cluster_advance, cluster_part_width);
2903 if (direction > 0)
2904 {
2905 for (part_index = 0; adv >= 0; adv-=cluster_part_width, part_index++)
2906 ;
2907 if (part_index) part_index--;
2908 }
2909 else
2910 {
2911 for (part_index = 0; adv > 0; adv-=cluster_part_width, part_index++)
2912 ;
2913 if (part_index > cluster_size)
2914 {
2915 adv += cluster_part_width;
2916 part_index=cluster_size;
2917 }
2918 }
2919
2920 TRACE("base_char %i part_index %i, leftover advance %f\n",i, part_index, adv);
2921
2922 if (direction > 0)
2923 i += part_index;
2924 else
2925 i += (cluster_size - part_index);
2926
2927 /* Check trailing */
2928 if ((direction > 0 && fabs(adv) <= (cluster_part_width / 2.0)) ||
2929 (direction < 0 && adv && fabs(adv) >= (cluster_part_width / 2.0)))
2930 *piTrailing = 1;
2931 }
2932 else
2933 {
2934 /* Check trailing */
2935 if ((direction > 0 && abs(iPosX) <= (piAdvance[glyph_index] / 2)) ||
2936 (direction < 0 && abs(iPosX) >= (piAdvance[glyph_index] / 2)))
2937 *piTrailing = 1;
2938 }
2939 }
2940 }
2941 else
2942 {
2943 TRACE("Point falls outside of string\n");
2944 if (glyph_index < 0)
2945 i = cChars-1;
2946 else /* (glyph_index >= cGlyphs) */
2947 i = cChars;
2948
2949 /* If not snaping in the reverse direction (such as Hebrew) Then 0
2950 point flow to the next character */
2951 if (direction < 0)
2952 {
2953 if (!scriptInformation[psa->eScript].props.fNeedsCaretInfo && abs(iPosX) == piAdvance[glyph_index])
2954 i++;
2955 else
2956 *piTrailing = 1;
2957 }
2958 }
2959
2960 *piCP = i;
2961
2962 TRACE("*piCP=%d\n", *piCP);
2963 TRACE("*piTrailing=%d\n", *piTrailing);
2964 return S_OK;
2965 }
2966
2967 /***********************************************************************
2968 * ScriptBreak (USP10.@)
2969 *
2970 * Retrieve line break information.
2971 *
2972 * PARAMS
2973 * chars [I] Array of characters.
2974 * sa [I] Script analysis.
2975 * la [I] Array of logical attribute structures.
2976 *
2977 * RETURNS
2978 * Success: S_OK
2979 * Failure: S_FALSE
2980 */
2981 HRESULT WINAPI ScriptBreak(const WCHAR *chars, int count, const SCRIPT_ANALYSIS *sa, SCRIPT_LOGATTR *la)
2982 {
2983 TRACE("(%s, %d, %p, %p)\n", debugstr_wn(chars, count), count, sa, la);
2984
2985 if (count < 0 || !la) return E_INVALIDARG;
2986 if (count == 0) return E_FAIL;
2987
2988 BREAK_line(chars, count, sa, la);
2989
2990 return S_OK;
2991 }
2992
2993 /***********************************************************************
2994 * ScriptIsComplex (USP10.@)
2995 *
2996 * Determine if a string is complex.
2997 *
2998 * PARAMS
2999 * chars [I] Array of characters to test.
3000 * len [I] Length in characters.
3001 * flag [I] Flag.
3002 *
3003 * RETURNS
3004 * Success: S_OK
3005 * Failure: S_FALSE
3006 *
3007 */
3008 HRESULT WINAPI ScriptIsComplex(const WCHAR *chars, int len, DWORD flag)
3009 {
3010 enum usp10_script script;
3011 unsigned int i, consumed;
3012
3013 TRACE("(%s,%d,0x%x)\n", debugstr_wn(chars, len), len, flag);
3014
3015 if (!chars || len < 0)
3016 return E_INVALIDARG;
3017
3018 for (i = 0; i < len; i+=consumed)
3019 {
3020 if ((flag & SIC_ASCIIDIGIT) && chars[i] >= 0x30 && chars[i] <= 0x39)
3021 return S_OK;
3022
3023 script = get_char_script(chars,i,len, &consumed);
3024 if ((scriptInformation[script].props.fComplex && (flag & SIC_COMPLEX))||
3025 (!scriptInformation[script].props.fComplex && (flag & SIC_NEUTRAL)))
3026 return S_OK;
3027 }
3028 return S_FALSE;
3029 }
3030
3031 /***********************************************************************
3032 * ScriptShapeOpenType (USP10.@)
3033 *
3034 * Produce glyphs and visual attributes for a run.
3035 *
3036 * PARAMS
3037 * hdc [I] Device context.
3038 * psc [I/O] Opaque pointer to a script cache.
3039 * psa [I/O] Script analysis.
3040 * tagScript [I] The OpenType tag for the Script
3041 * tagLangSys [I] The OpenType tag for the Language
3042 * rcRangeChars[I] Array of Character counts in each range
3043 * rpRangeProperties [I] Array of TEXTRANGE_PROPERTIES structures
3044 * cRanges [I] Count of ranges
3045 * pwcChars [I] Array of characters specifying the run.
3046 * cChars [I] Number of characters in pwcChars.
3047 * cMaxGlyphs [I] Length of pwOutGlyphs.
3048 * pwLogClust [O] Array of logical cluster info.
3049 * pCharProps [O] Array of character property values
3050 * pwOutGlyphs [O] Array of glyphs.
3051 * pOutGlyphProps [O] Array of attributes for the retrieved glyphs
3052 * pcGlyphs [O] Number of glyphs returned.
3053 *
3054 * RETURNS
3055 * Success: S_OK
3056 * Failure: Non-zero HRESULT value.
3057 */
3058 HRESULT WINAPI ScriptShapeOpenType( HDC hdc, SCRIPT_CACHE *psc,
3059 SCRIPT_ANALYSIS *psa, OPENTYPE_TAG tagScript,
3060 OPENTYPE_TAG tagLangSys, int *rcRangeChars,
3061 TEXTRANGE_PROPERTIES **rpRangeProperties,
3062 int cRanges, const WCHAR *pwcChars, int cChars,
3063 int cMaxGlyphs, WORD *pwLogClust,
3064 SCRIPT_CHARPROP *pCharProps, WORD *pwOutGlyphs,
3065 SCRIPT_GLYPHPROP *pOutGlyphProps, int *pcGlyphs)
3066 {
3067 HRESULT hr;
3068 int i;
3069 unsigned int g;
3070 BOOL rtl;
3071 int cluster;
3072 static int once = 0;
3073
3074 TRACE("(%p, %p, %p, %s, %s, %p, %p, %d, %s, %d, %d, %p, %p, %p, %p, %p )\n",
3075 hdc, psc, psa,
3076 debugstr_an((char*)&tagScript,4), debugstr_an((char*)&tagLangSys,4),
3077 rcRangeChars, rpRangeProperties, cRanges, debugstr_wn(pwcChars, cChars),
3078 cChars, cMaxGlyphs, pwLogClust, pCharProps, pwOutGlyphs, pOutGlyphProps, pcGlyphs);
3079
3080 if (psa) TRACE("psa values: %d, %d, %d, %d, %d, %d, %d\n", psa->eScript, psa->fRTL, psa->fLayoutRTL,
3081 psa->fLinkBefore, psa->fLinkAfter, psa->fLogicalOrder, psa->fNoGlyphIndex);
3082
3083 if (!pOutGlyphProps || !pcGlyphs || !pCharProps) return E_INVALIDARG;
3084 if (cChars > cMaxGlyphs) return E_OUTOFMEMORY;
3085
3086 if (cRanges)
3087 if(!once++) FIXME("Ranges not supported yet\n");
3088
3089 rtl = (psa && !psa->fLogicalOrder && psa->fRTL);
3090
3091 *pcGlyphs = cChars;
3092 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
3093 if (!pwLogClust) return E_FAIL;
3094
3095 ((ScriptCache *)*psc)->userScript = tagScript;
3096 ((ScriptCache *)*psc)->userLang = tagLangSys;
3097
3098 /* Initialize a SCRIPT_VISATTR and LogClust for each char in this run */
3099 for (i = 0; i < cChars; i++)
3100 {
3101 int idx = i;
3102 if (rtl) idx = cChars - 1 - i;
3103 /* FIXME: set to better values */
3104 pOutGlyphProps[i].sva.uJustification = (pwcChars[idx] == ' ') ? SCRIPT_JUSTIFY_BLANK : SCRIPT_JUSTIFY_CHARACTER;
3105 pOutGlyphProps[i].sva.fClusterStart = 1;
3106 pOutGlyphProps[i].sva.fDiacritic = 0;
3107 pOutGlyphProps[i].sva.fZeroWidth = 0;
3108 pOutGlyphProps[i].sva.fReserved = 0;
3109 pOutGlyphProps[i].sva.fShapeReserved = 0;
3110
3111 /* FIXME: have the shaping engine set this */
3112 pCharProps[i].fCanGlyphAlone = 0;
3113
3114 pwLogClust[i] = idx;
3115 }
3116
3117 if (psa && !psa->fNoGlyphIndex && ((ScriptCache *)*psc)->sfnt)
3118 {
3119 WCHAR *rChars;
3120 if ((hr = SHAPE_CheckFontForRequiredFeatures(hdc, (ScriptCache *)*psc, psa)) != S_OK) return hr;
3121
3122 rChars = heap_alloc(sizeof(WCHAR) * cChars);
3123 if (!rChars) return E_OUTOFMEMORY;
3124 for (i = 0, g = 0, cluster = 0; i < cChars; i++)
3125 {
3126 int idx = i;
3127 DWORD chInput;
3128
3129 if (rtl) idx = cChars - 1 - i;
3130 if (!cluster)
3131 {
3132 chInput = decode_surrogate_pair(pwcChars, idx, cChars);
3133 if (!chInput)
3134 {
3135 if (psa->fRTL)
3136 chInput = mirror_char(pwcChars[idx]);
3137 else
3138 chInput = pwcChars[idx];
3139 rChars[i] = chInput;
3140 }
3141 else
3142 {
3143 rChars[i] = pwcChars[idx];
3144 rChars[i+1] = pwcChars[(rtl)?idx-1:idx+1];
3145 cluster = 1;
3146 }
3147 if (!(pwOutGlyphs[g] = get_cache_glyph(psc, chInput)))
3148 {
3149 WORD glyph;
3150 if (!hdc)
3151 {
3152 heap_free(rChars);
3153 return E_PENDING;
3154 }
3155 if (OpenType_CMAP_GetGlyphIndex(hdc, (ScriptCache *)*psc, chInput, &glyph, 0) == GDI_ERROR)
3156 {
3157 heap_free(rChars);
3158 return S_FALSE;
3159 }
3160 pwOutGlyphs[g] = set_cache_glyph(psc, chInput, glyph);
3161 }
3162 g++;
3163 }
3164 else
3165 {
3166 int k;
3167 cluster--;
3168 pwLogClust[idx] = (rtl)?pwLogClust[idx+1]:pwLogClust[idx-1];
3169 for (k = (rtl)?idx-1:idx+1; k >= 0 && k < cChars; (rtl)?k--:k++)
3170 pwLogClust[k]--;
3171 }
3172 }
3173 *pcGlyphs = g;
3174
3175 SHAPE_ContextualShaping(hdc, (ScriptCache *)*psc, psa, rChars, cChars, pwOutGlyphs, pcGlyphs, cMaxGlyphs, pwLogClust);
3176 SHAPE_ApplyDefaultOpentypeFeatures(hdc, (ScriptCache *)*psc, psa, pwOutGlyphs, pcGlyphs, cMaxGlyphs, cChars, pwLogClust);
3177 SHAPE_CharGlyphProp(hdc, (ScriptCache *)*psc, psa, pwcChars, cChars, pwOutGlyphs, *pcGlyphs, pwLogClust, pCharProps, pOutGlyphProps);
3178
3179 for (i = 0; i < cChars; ++i)
3180 {
3181 /* Special case for tabs and joiners. As control characters, ZWNJ
3182 * and ZWJ would in principle get handled by the corresponding
3183 * shaping functions. However, since ZWNJ and ZWJ can get merged
3184 * into adjoining runs during itemisation, these don't generally
3185 * get classified as Script_Control. */
3186 if (pwcChars[i] == 0x0009 || pwcChars[i] == ZWSP || pwcChars[i] == ZWNJ || pwcChars[i] == ZWJ)
3187 {
3188 pwOutGlyphs[pwLogClust[i]] = ((ScriptCache *)*psc)->sfp.wgBlank;
3189 pOutGlyphProps[pwLogClust[i]].sva.fZeroWidth = 1;
3190 }
3191 }
3192 heap_free(rChars);
3193 }
3194 else
3195 {
3196 TRACE("no glyph translation\n");
3197 for (i = 0; i < cChars; i++)
3198 {
3199 int idx = i;
3200 /* No mirroring done here */
3201 if (rtl) idx = cChars - 1 - i;
3202 pwOutGlyphs[i] = pwcChars[idx];
3203
3204 if (!psa)
3205 continue;
3206
3207 /* overwrite some basic control glyphs to blank */
3208 if (psa->fNoGlyphIndex)
3209 {
3210 if (pwcChars[idx] == ZWSP || pwcChars[idx] == ZWNJ || pwcChars[idx] == ZWJ)
3211 {
3212 pwOutGlyphs[i] = 0x20;
3213 pOutGlyphProps[i].sva.fZeroWidth = 1;
3214 }
3215 }
3216 else if (psa->eScript == Script_Control || pwcChars[idx] == ZWSP
3217 || pwcChars[idx] == ZWNJ || pwcChars[idx] == ZWJ)
3218 {
3219 if (pwcChars[idx] == 0x0009 || pwcChars[idx] == 0x000A ||
3220 pwcChars[idx] == 0x000D || pwcChars[idx] >= 0x001C)
3221 {
3222 pwOutGlyphs[i] = ((ScriptCache *)*psc)->sfp.wgBlank;
3223 pOutGlyphProps[i].sva.fZeroWidth = 1;
3224 }
3225 }
3226 }
3227 }
3228
3229 return S_OK;
3230 }
3231
3232
3233 /***********************************************************************
3234 * ScriptShape (USP10.@)
3235 *
3236 * Produce glyphs and visual attributes for a run.
3237 *
3238 * PARAMS
3239 * hdc [I] Device context.
3240 * psc [I/O] Opaque pointer to a script cache.
3241 * pwcChars [I] Array of characters specifying the run.
3242 * cChars [I] Number of characters in pwcChars.
3243 * cMaxGlyphs [I] Length of pwOutGlyphs.
3244 * psa [I/O] Script analysis.
3245 * pwOutGlyphs [O] Array of glyphs.
3246 * pwLogClust [O] Array of logical cluster info.
3247 * psva [O] Array of visual attributes.
3248 * pcGlyphs [O] Number of glyphs returned.
3249 *
3250 * RETURNS
3251 * Success: S_OK
3252 * Failure: Non-zero HRESULT value.
3253 */
3254 HRESULT WINAPI ScriptShape(HDC hdc, SCRIPT_CACHE *psc, const WCHAR *pwcChars,
3255 int cChars, int cMaxGlyphs,
3256 SCRIPT_ANALYSIS *psa, WORD *pwOutGlyphs, WORD *pwLogClust,
3257 SCRIPT_VISATTR *psva, int *pcGlyphs)
3258 {
3259 HRESULT hr;
3260 int i;
3261 SCRIPT_CHARPROP *charProps;
3262 SCRIPT_GLYPHPROP *glyphProps;
3263
3264 if (!psva || !pcGlyphs) return E_INVALIDARG;
3265 if (cChars > cMaxGlyphs) return E_OUTOFMEMORY;
3266
3267 charProps = heap_alloc_zero(sizeof(SCRIPT_CHARPROP)*cChars);
3268 if (!charProps) return E_OUTOFMEMORY;
3269 glyphProps = heap_alloc_zero(sizeof(SCRIPT_GLYPHPROP)*cMaxGlyphs);
3270 if (!glyphProps)
3271 {
3272 heap_free(charProps);
3273 return E_OUTOFMEMORY;
3274 }
3275
3276 hr = ScriptShapeOpenType(hdc, psc, psa, scriptInformation[psa->eScript].scriptTag, 0, NULL, NULL, 0, pwcChars, cChars, cMaxGlyphs, pwLogClust, charProps, pwOutGlyphs, glyphProps, pcGlyphs);
3277
3278 if (SUCCEEDED(hr))
3279 {
3280 for (i = 0; i < *pcGlyphs; i++)
3281 psva[i] = glyphProps[i].sva;
3282 }
3283
3284 heap_free(charProps);
3285 heap_free(glyphProps);
3286
3287 return hr;
3288 }
3289
3290 /***********************************************************************
3291 * ScriptPlaceOpenType (USP10.@)
3292 *
3293 * Produce advance widths for a run.
3294 *
3295 * PARAMS
3296 * hdc [I] Device context.
3297 * psc [I/O] Opaque pointer to a script cache.
3298 * psa [I/O] Script analysis.
3299 * tagScript [I] The OpenType tag for the Script
3300 * tagLangSys [I] The OpenType tag for the Language
3301 * rcRangeChars[I] Array of Character counts in each range
3302 * rpRangeProperties [I] Array of TEXTRANGE_PROPERTIES structures
3303 * cRanges [I] Count of ranges
3304 * pwcChars [I] Array of characters specifying the run.
3305 * pwLogClust [I] Array of logical cluster info
3306 * pCharProps [I] Array of character property values
3307 * cChars [I] Number of characters in pwcChars.
3308 * pwGlyphs [I] Array of glyphs.
3309 * pGlyphProps [I] Array of attributes for the retrieved glyphs
3310 * cGlyphs [I] Count of Glyphs
3311 * piAdvance [O] Array of advance widths.
3312 * pGoffset [O] Glyph offsets.
3313 * pABC [O] Combined ABC width.
3314 *
3315 * RETURNS
3316 * Success: S_OK
3317 * Failure: Non-zero HRESULT value.
3318 */
3319
3320 HRESULT WINAPI ScriptPlaceOpenType( HDC hdc, SCRIPT_CACHE *psc, SCRIPT_ANALYSIS *psa,
3321 OPENTYPE_TAG tagScript, OPENTYPE_TAG tagLangSys,
3322 int *rcRangeChars, TEXTRANGE_PROPERTIES **rpRangeProperties,
3323 int cRanges, const WCHAR *pwcChars, WORD *pwLogClust,
3324 SCRIPT_CHARPROP *pCharProps, int cChars,
3325 const WORD *pwGlyphs, const SCRIPT_GLYPHPROP *pGlyphProps,
3326 int cGlyphs, int *piAdvance,
3327 GOFFSET *pGoffset, ABC *pABC
3328 )
3329 {
3330 HRESULT hr;
3331 int i;
3332 static int once = 0;
3333
3334 TRACE("(%p, %p, %p, %s, %s, %p, %p, %d, %s, %p, %p, %d, %p, %p, %d, %p %p %p)\n",
3335 hdc, psc, psa,
3336 debugstr_an((char*)&tagScript,4), debugstr_an((char*)&tagLangSys,4),
3337 rcRangeChars, rpRangeProperties, cRanges, debugstr_wn(pwcChars, cChars),
3338 pwLogClust, pCharProps, cChars, pwGlyphs, pGlyphProps, cGlyphs, piAdvance,
3339 pGoffset, pABC);
3340
3341 if (!pGlyphProps) return E_INVALIDARG;
3342 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
3343 if (!pGoffset) return E_FAIL;
3344
3345 if (cRanges)
3346 if (!once++) FIXME("Ranges not supported yet\n");
3347
3348 ((ScriptCache *)*psc)->userScript = tagScript;
3349 ((ScriptCache *)*psc)->userLang = tagLangSys;
3350
3351 if (pABC) memset(pABC, 0, sizeof(ABC));
3352 for (i = 0; i < cGlyphs; i++)
3353 {
3354 ABC abc;
3355 if (pGlyphProps[i].sva.fZeroWidth)
3356 {
3357 abc.abcA = abc.abcB = abc.abcC = 0;
3358 }
3359 else if (!get_cache_glyph_widths(psc, pwGlyphs[i], &abc))
3360 {
3361 BOOL ret;
3362 if (!hdc) return E_PENDING;
3363 if (get_cache_pitch_family(psc) & TMPF_TRUETYPE)
3364 {
3365 if (psa->fNoGlyphIndex)
3366 ret = GetCharABCWidthsW(hdc, pwGlyphs[i], pwGlyphs[i], &abc);
3367 else
3368 ret = GetCharABCWidthsI(hdc, 0, 1, (WORD *)&pwGlyphs[i], &abc);
3369 if (!ret) return S_FALSE;
3370 }
3371 else
3372 {
3373 INT width;
3374 if (psa->fNoGlyphIndex)
3375 ret = GetCharWidth32W(hdc, pwGlyphs[i], pwGlyphs[i], &width);
3376 else
3377 ret = GetCharWidthI(hdc, 0, 1, (WORD *)&pwGlyphs[i], &width);
3378 if (!ret) return S_FALSE;
3379 abc.abcB = width;
3380 abc.abcA = abc.abcC = 0;
3381 }
3382 set_cache_glyph_widths(psc, pwGlyphs[i], &abc);
3383 }
3384 if (pABC)
3385 {
3386 pABC->abcA += abc.abcA;
3387 pABC->abcB += abc.abcB;
3388 pABC->abcC += abc.abcC;
3389 }
3390 /* FIXME: set to more reasonable values */
3391 pGoffset[i].du = pGoffset[i].dv = 0;
3392 if (piAdvance) piAdvance[i] = abc.abcA + abc.abcB + abc.abcC;
3393 }
3394
3395 SHAPE_ApplyOpenTypePositions(hdc, (ScriptCache *)*psc, psa, pwGlyphs, cGlyphs, piAdvance, pGoffset);
3396
3397 if (pABC) TRACE("Total for run: abcA=%d, abcB=%d, abcC=%d\n", pABC->abcA, pABC->abcB, pABC->abcC);
3398 return S_OK;
3399 }
3400
3401 /***********************************************************************
3402 * ScriptPlace (USP10.@)
3403 *
3404 * Produce advance widths for a run.
3405 *
3406 * PARAMS
3407 * hdc [I] Device context.
3408 * psc [I/O] Opaque pointer to a script cache.
3409 * pwGlyphs [I] Array of glyphs.
3410 * cGlyphs [I] Number of glyphs in pwGlyphs.
3411 * psva [I] Array of visual attributes.
3412 * psa [I/O] String analysis.
3413 * piAdvance [O] Array of advance widths.
3414 * pGoffset [O] Glyph offsets.
3415 * pABC [O] Combined ABC width.
3416 *
3417 * RETURNS
3418 * Success: S_OK
3419 * Failure: Non-zero HRESULT value.
3420 */
3421 HRESULT WINAPI ScriptPlace(HDC hdc, SCRIPT_CACHE *psc, const WORD *pwGlyphs,
3422 int cGlyphs, const SCRIPT_VISATTR *psva,
3423 SCRIPT_ANALYSIS *psa, int *piAdvance, GOFFSET *pGoffset, ABC *pABC )
3424 {
3425 HRESULT hr;
3426 SCRIPT_GLYPHPROP *glyphProps;
3427 int i;
3428
3429 TRACE("(%p, %p, %p, %d, %p, %p, %p, %p, %p)\n", hdc, psc, pwGlyphs, cGlyphs, psva, psa,
3430 piAdvance, pGoffset, pABC);
3431
3432 if (!psva) return E_INVALIDARG;
3433 if (!pGoffset) return E_FAIL;
3434
3435 glyphProps = heap_alloc(sizeof(SCRIPT_GLYPHPROP)*cGlyphs);
3436 if (!glyphProps) return E_OUTOFMEMORY;
3437
3438 for (i = 0; i < cGlyphs; i++)
3439 glyphProps[i].sva = psva[i];
3440
3441 hr = ScriptPlaceOpenType(hdc, psc, psa, scriptInformation[psa->eScript].scriptTag, 0, NULL, NULL, 0, NULL, NULL, NULL, 0, pwGlyphs, glyphProps, cGlyphs, piAdvance, pGoffset, pABC);
3442
3443 heap_free(glyphProps);
3444
3445 return hr;
3446 }
3447
3448 /***********************************************************************
3449 * ScriptGetCMap (USP10.@)
3450 *
3451 * Retrieve glyph indices.
3452 *
3453 * PARAMS
3454 * hdc [I] Device context.
3455 * psc [I/O] Opaque pointer to a script cache.
3456 * pwcInChars [I] Array of Unicode characters.
3457 * cChars [I] Number of characters in pwcInChars.
3458 * dwFlags [I] Flags.
3459 * pwOutGlyphs [O] Buffer to receive the array of glyph indices.
3460 *
3461 * RETURNS
3462 * Success: S_OK
3463 * Failure: Non-zero HRESULT value.
3464 */
3465 HRESULT WINAPI ScriptGetCMap(HDC hdc, SCRIPT_CACHE *psc, const WCHAR *pwcInChars,
3466 int cChars, DWORD dwFlags, WORD *pwOutGlyphs)
3467 {
3468 HRESULT hr;
3469 int i;
3470
3471 TRACE("(%p,%p,%s,%d,0x%x,%p)\n", hdc, psc, debugstr_wn(pwcInChars, cChars),
3472 cChars, dwFlags, pwOutGlyphs);
3473
3474 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
3475
3476 hr = S_OK;
3477
3478 if ((get_cache_pitch_family(psc) & TMPF_TRUETYPE))
3479 {
3480 for (i = 0; i < cChars; i++)
3481 {
3482 WCHAR inChar;
3483 if (dwFlags == SGCM_RTL)
3484 inChar = mirror_char(pwcInChars[i]);
3485 else
3486 inChar = pwcInChars[i];
3487 if (!(pwOutGlyphs[i] = get_cache_glyph(psc, inChar)))
3488 {
3489 WORD glyph;
3490 if (!hdc) return E_PENDING;
3491 if (GetGlyphIndicesW(hdc, &inChar, 1, &glyph, GGI_MARK_NONEXISTING_GLYPHS) == GDI_ERROR) return S_FALSE;
3492 if (glyph == 0xffff)
3493 {
3494 hr = S_FALSE;
3495 glyph = 0x0;
3496 }
3497 pwOutGlyphs[i] = set_cache_glyph(psc, inChar, glyph);
3498 }
3499 }
3500 }
3501 else
3502 {
3503 TRACE("no glyph translation\n");
3504 for (i = 0; i < cChars; i++)
3505 {
3506 WCHAR inChar;
3507 if (dwFlags == SGCM_RTL)
3508 inChar = mirror_char(pwcInChars[i]);
3509 else
3510 inChar = pwcInChars[i];
3511 pwOutGlyphs[i] = inChar;
3512 }
3513 }
3514 return hr;
3515 }
3516
3517 /***********************************************************************
3518 * ScriptTextOut (USP10.@)
3519 *
3520 */
3521 HRESULT WINAPI ScriptTextOut(const HDC hdc, SCRIPT_CACHE *psc, int x, int y, UINT fuOptions,
3522 const RECT *lprc, const SCRIPT_ANALYSIS *psa, const WCHAR *pwcReserved,
3523 int iReserved, const WORD *pwGlyphs, int cGlyphs, const int *piAdvance,
3524 const int *piJustify, const GOFFSET *pGoffset)
3525 {
3526 HRESULT hr = S_OK;
3527 INT i, dir = 1;
3528 INT *lpDx;
3529 WORD *reordered_glyphs = (WORD *)pwGlyphs;
3530
3531 TRACE("(%p, %p, %d, %d, %08x, %s, %p, %p, %d, %p, %d, %p, %p, %p)\n",
3532 hdc, psc, x, y, fuOptions, wine_dbgstr_rect(lprc), psa, pwcReserved, iReserved, pwGlyphs, cGlyphs,
3533 piAdvance, piJustify, pGoffset);
3534
3535 if (!hdc || !psc) return E_INVALIDARG;
3536 if (!piAdvance || !psa || !pwGlyphs) return E_INVALIDARG;
3537
3538 fuOptions &= ETO_CLIPPED + ETO_OPAQUE;
3539 fuOptions |= ETO_IGNORELANGUAGE;
3540 if (!psa->fNoGlyphIndex) /* Have Glyphs? */
3541 fuOptions |= ETO_GLYPH_INDEX; /* Say don't do translation to glyph */
3542
3543 lpDx = heap_alloc(cGlyphs * sizeof(INT) * 2);
3544 if (!lpDx) return E_OUTOFMEMORY;
3545 fuOptions |= ETO_PDY;
3546
3547 if (psa->fRTL && psa->fLogicalOrder)
3548 {
3549 reordered_glyphs = heap_alloc( cGlyphs * sizeof(WORD) );
3550 if (!reordered_glyphs)
3551 {
3552 heap_free( lpDx );
3553 return E_OUTOFMEMORY;
3554 }
3555
3556 for (i = 0; i < cGlyphs; i++)
3557 reordered_glyphs[i] = pwGlyphs[cGlyphs - 1 - i];
3558 dir = -1;
3559 }
3560
3561 for (i = 0; i < cGlyphs; i++)
3562 {
3563 int orig_index = (dir > 0) ? i : cGlyphs - 1 - i;
3564 lpDx[i * 2] = piAdvance[orig_index];
3565 lpDx[i * 2 + 1] = 0;
3566
3567 if (pGoffset)
3568 {
3569 if (i == 0)
3570 {
3571 x += pGoffset[orig_index].du * dir;
3572 y += pGoffset[orig_index].dv;
3573 }
3574 else
3575 {
3576 lpDx[(i - 1) * 2] += pGoffset[orig_index].du * dir;
3577 lpDx[(i - 1) * 2 + 1] += pGoffset[orig_index].dv;
3578 }
3579 lpDx[i * 2] -= pGoffset[orig_index].du * dir;
3580 lpDx[i * 2 + 1] -= pGoffset[orig_index].dv;
3581 }
3582 }
3583
3584 if (!ExtTextOutW(hdc, x, y, fuOptions, lprc, reordered_glyphs, cGlyphs, lpDx))
3585 hr = S_FALSE;
3586
3587 if (reordered_glyphs != pwGlyphs) heap_free( reordered_glyphs );
3588 heap_free(lpDx);
3589
3590 return hr;
3591 }
3592
3593 /***********************************************************************
3594 * ScriptCacheGetHeight (USP10.@)
3595 *
3596 * Retrieve the height of the font in the cache.
3597 *
3598 * PARAMS
3599 * hdc [I] Device context.
3600 * psc [I/O] Opaque pointer to a script cache.
3601 * height [O] Receives font height.
3602 *
3603 * RETURNS
3604 * Success: S_OK
3605 * Failure: Non-zero HRESULT value.
3606 */
3607 HRESULT WINAPI ScriptCacheGetHeight(HDC hdc, SCRIPT_CACHE *psc, LONG *height)
3608 {
3609 HRESULT hr;
3610
3611 TRACE("(%p, %p, %p)\n", hdc, psc, height);
3612
3613 if (!height) return E_INVALIDARG;
3614 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
3615
3616 *height = get_cache_height(psc);
3617 return S_OK;
3618 }
3619
3620 /***********************************************************************
3621 * ScriptGetGlyphABCWidth (USP10.@)
3622 *
3623 * Retrieve the width of a glyph.
3624 *
3625 * PARAMS
3626 * hdc [I] Device context.
3627 * psc [I/O] Opaque pointer to a script cache.
3628 * glyph [I] Glyph to retrieve the width for.
3629 * abc [O] ABC widths of the glyph.
3630 *
3631 * RETURNS
3632 * Success: S_OK
3633 * Failure: Non-zero HRESULT value.
3634 */
3635 HRESULT WINAPI ScriptGetGlyphABCWidth(HDC hdc, SCRIPT_CACHE *psc, WORD glyph, ABC *abc)
3636 {
3637 HRESULT hr;
3638
3639 TRACE("(%p, %p, 0x%04x, %p)\n", hdc, psc, glyph, abc);
3640
3641 if (!abc) return E_INVALIDARG;
3642 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
3643
3644 if (!get_cache_glyph_widths(psc, glyph, abc))
3645 {
3646 if (!hdc) return E_PENDING;
3647 if ((get_cache_pitch_family(psc) & TMPF_TRUETYPE))
3648 {
3649 if (!GetCharABCWidthsI(hdc, 0, 1, &glyph, abc)) return S_FALSE;
3650 }
3651 else
3652 {
3653 INT width;
3654 if (!GetCharWidth32W(hdc, glyph, glyph, &width)) return S_FALSE;
3655 abc->abcB = width;
3656 abc->abcA = abc->abcC = 0;
3657 }
3658 set_cache_glyph_widths(psc, glyph, abc);
3659 }
3660 return S_OK;
3661 }
3662
3663 /***********************************************************************
3664 * ScriptLayout (USP10.@)
3665 *
3666 * Map embedding levels to visual and/or logical order.
3667 *
3668 * PARAMS
3669 * runs [I] Size of level array.
3670 * level [I] Array of embedding levels.
3671 * vistolog [O] Map of embedding levels from visual to logical order.
3672 * logtovis [O] Map of embedding levels from logical to visual order.
3673 *
3674 * RETURNS
3675 * Success: S_OK
3676 * Failure: Non-zero HRESULT value.
3677 *
3678 */
3679 HRESULT WINAPI ScriptLayout(int runs, const BYTE *level, int *vistolog, int *logtovis)
3680 {
3681 int* indexs;
3682 int ich;
3683
3684 TRACE("(%d, %p, %p, %p)\n", runs, level, vistolog, logtovis);
3685
3686 if (!level || (!vistolog && !logtovis))
3687 return E_INVALIDARG;
3688
3689 indexs = heap_alloc(sizeof(int) * runs);
3690 if (!indexs)
3691 return E_OUTOFMEMORY;
3692
3693 if (vistolog)
3694 {
3695 for( ich = 0; ich < runs; ich++)
3696 indexs[ich] = ich;
3697
3698 ich = 0;
3699 while (ich < runs)
3700 ich += BIDI_ReorderV2lLevel(0, indexs+ich, level+ich, runs - ich, FALSE);
3701 memcpy(vistolog, indexs, runs * sizeof(*vistolog));
3702 }
3703
3704 if (logtovis)
3705 {
3706 for( ich = 0; ich < runs; ich++)
3707 indexs[ich] = ich;
3708
3709 ich = 0;
3710 while (ich < runs)
3711 ich += BIDI_ReorderL2vLevel(0, indexs+ich, level+ich, runs - ich, FALSE);
3712 memcpy(logtovis, indexs, runs * sizeof(*logtovis));
3713 }
3714 heap_free(indexs);
3715
3716 return S_OK;
3717 }
3718
3719 /***********************************************************************
3720 * ScriptStringGetLogicalWidths (USP10.@)
3721 *
3722 * Returns logical widths from a string analysis.
3723 *
3724 * PARAMS
3725 * ssa [I] string analysis.
3726 * piDx [O] logical widths returned.
3727 *
3728 * RETURNS
3729 * Success: S_OK
3730 * Failure: a non-zero HRESULT.
3731 */
3732 HRESULT WINAPI ScriptStringGetLogicalWidths(SCRIPT_STRING_ANALYSIS ssa, int *piDx)
3733 {
3734 int i, j, next = 0;
3735 StringAnalysis *analysis = ssa;
3736
3737 TRACE("%p, %p\n", ssa, piDx);
3738
3739 if (!analysis) return S_FALSE;
3740 if (!(analysis->ssa_flags & SSA_GLYPHS)) return S_FALSE;
3741
3742 for (i = 0; i < analysis->numItems; i++)
3743 {
3744 int cChar = analysis->pItem[i+1].iCharPos - analysis->pItem[i].iCharPos;
3745 int direction = 1;
3746
3747 if (analysis->pItem[i].a.fRTL && ! analysis->pItem[i].a.fLogicalOrder)
3748 direction = -1;
3749
3750 for (j = 0; j < cChar; j++)
3751 {
3752 int k;
3753 int glyph = analysis->glyphs[i].pwLogClust[j];
3754 int clust_size = get_cluster_size(analysis->glyphs[i].pwLogClust,
3755 cChar, j, direction, NULL, NULL);
3756 int advance = get_glyph_cluster_advance(analysis->glyphs[i].piAdvance, analysis->glyphs[i].psva, analysis->glyphs[i].pwLogClust, analysis->glyphs[i].numGlyphs, cChar, glyph, direction);
3757
3758 for (k = 0; k < clust_size; k++)
3759 {
3760 piDx[next] = advance / clust_size;
3761 next++;
3762 if (k) j++;
3763 }
3764 }
3765 }
3766 return S_OK;
3767 }
3768
3769 /***********************************************************************
3770 * ScriptStringValidate (USP10.@)
3771 *
3772 * Validate a string analysis.
3773 *
3774 * PARAMS
3775 * ssa [I] string analysis.
3776 *
3777 * RETURNS
3778 * Success: S_OK
3779 * Failure: S_FALSE if invalid sequences are found
3780 * or a non-zero HRESULT if it fails.
3781 */
3782 HRESULT WINAPI ScriptStringValidate(SCRIPT_STRING_ANALYSIS ssa)
3783 {
3784 StringAnalysis *analysis = ssa;
3785
3786 TRACE("(%p)\n", ssa);
3787
3788 if (!analysis) return E_INVALIDARG;
3789 return analysis->flags & SCRIPT_STRING_ANALYSIS_FLAGS_INVALID ? S_FALSE : S_OK;
3790 }
3791
3792 /***********************************************************************
3793 * ScriptString_pSize (USP10.@)
3794 *
3795 * Retrieve width and height of an analysed string.
3796 *
3797 * PARAMS
3798 * ssa [I] string analysis.
3799 *
3800 * RETURNS
3801 * Success: Pointer to a SIZE structure.
3802 * Failure: NULL
3803 */
3804 const SIZE * WINAPI ScriptString_pSize(SCRIPT_STRING_ANALYSIS ssa)
3805 {
3806 int i, j;
3807 StringAnalysis *analysis = ssa;
3808
3809 TRACE("(%p)\n", ssa);
3810
3811 if (!analysis) return NULL;
3812 if (!(analysis->ssa_flags & SSA_GLYPHS)) return NULL;
3813
3814 if (!(analysis->flags & SCRIPT_STRING_ANALYSIS_FLAGS_SIZE))
3815 {
3816 analysis->sz.cy = analysis->glyphs[0].sc->tm.tmHeight;
3817
3818 analysis->sz.cx = 0;
3819 for (i = 0; i < analysis->numItems; i++)
3820 {
3821 if (analysis->glyphs[i].sc->tm.tmHeight > analysis->sz.cy)
3822 analysis->sz.cy = analysis->glyphs[i].sc->tm.tmHeight;
3823 for (j = 0; j < analysis->glyphs[i].numGlyphs; j++)
3824 analysis->sz.cx += analysis->glyphs[i].piAdvance[j];
3825 }
3826 }
3827 return &analysis->sz;
3828 }
3829
3830 /***********************************************************************
3831 * ScriptString_pLogAttr (USP10.@)
3832 *
3833 * Retrieve logical attributes of an analysed string.
3834 *
3835 * PARAMS
3836 * ssa [I] string analysis.
3837 *
3838 * RETURNS
3839 * Success: Pointer to an array of SCRIPT_LOGATTR structures.
3840 * Failure: NULL
3841 */
3842 const SCRIPT_LOGATTR * WINAPI ScriptString_pLogAttr(SCRIPT_STRING_ANALYSIS ssa)
3843 {
3844 StringAnalysis *analysis = ssa;
3845
3846 TRACE("(%p)\n", ssa);
3847
3848 if (!analysis) return NULL;
3849 if (!(analysis->ssa_flags & SSA_BREAK)) return NULL;
3850 return analysis->logattrs;
3851 }
3852
3853 /***********************************************************************
3854 * ScriptString_pcOutChars (USP10.@)
3855 *
3856 * Retrieve the length of a string after clipping.
3857 *
3858 * PARAMS
3859 * ssa [I] String analysis.
3860 *
3861 * RETURNS
3862 * Success: Pointer to the length.
3863 * Failure: NULL
3864 */
3865 const int * WINAPI ScriptString_pcOutChars(SCRIPT_STRING_ANALYSIS ssa)
3866 {
3867 StringAnalysis *analysis = ssa;
3868
3869 TRACE("(%p)\n", ssa);
3870
3871 if (!analysis) return NULL;
3872 return &analysis->clip_len;
3873 }
3874
3875 /***********************************************************************
3876 * ScriptStringGetOrder (USP10.@)
3877 *
3878 * Retrieve a glyph order map.
3879 *
3880 * PARAMS
3881 * ssa [I] String analysis.
3882 * order [I/O] Array of glyph positions.
3883 *
3884 * RETURNS
3885 * Success: S_OK
3886 * Failure: a non-zero HRESULT.
3887 */
3888 HRESULT WINAPI ScriptStringGetOrder(SCRIPT_STRING_ANALYSIS ssa, UINT *order)
3889 {
3890 int i, j;
3891 unsigned int k;
3892 StringAnalysis *analysis = ssa;
3893
3894 TRACE("(%p)\n", ssa);
3895
3896 if (!analysis) return S_FALSE;
3897 if (!(analysis->ssa_flags & SSA_GLYPHS)) return S_FALSE;
3898
3899 /* FIXME: handle RTL scripts */
3900 for (i = 0, k = 0; i < analysis->numItems; i++)
3901 for (j = 0; j < analysis->glyphs[i].numGlyphs; j++, k++)
3902 order[k] = k;
3903
3904 return S_OK;
3905 }
3906
3907 /***********************************************************************
3908 * ScriptGetLogicalWidths (USP10.@)
3909 *
3910 * Convert advance widths to logical widths.
3911 *
3912 * PARAMS
3913 * sa [I] Script analysis.
3914 * nbchars [I] Number of characters.
3915 * nbglyphs [I] Number of glyphs.
3916 * glyph_width [I] Array of glyph widths.
3917 * log_clust [I] Array of logical clusters.
3918 * sva [I] Visual attributes.
3919 * widths [O] Array of logical widths.
3920 *
3921 * RETURNS
3922 * Success: S_OK
3923 * Failure: a non-zero HRESULT.
3924 */
3925 HRESULT WINAPI ScriptGetLogicalWidths(const SCRIPT_ANALYSIS *sa, int nbchars, int nbglyphs,
3926 const int *advances, const WORD *log_clust,
3927 const SCRIPT_VISATTR *sva, int *widths)
3928 {
3929 int i, next = 0, direction;
3930
3931 TRACE("(%p, %d, %d, %p, %p, %p, %p)\n",
3932 sa, nbchars, nbglyphs, advances, log_clust, sva, widths);
3933
3934 if (sa->fRTL && !sa->fLogicalOrder)
3935 direction = -1;
3936 else
3937 direction = 1;
3938
3939 for (i = 0; i < nbchars; i++)
3940 {
3941 int clust_size = get_cluster_size(log_clust, nbchars, i, direction, NULL, NULL);
3942 int advance = get_glyph_cluster_advance(advances, sva, log_clust, nbglyphs, nbchars, log_clust[i], direction);
3943 int j;
3944
3945 for (j = 0; j < clust_size; j++)
3946 {
3947 widths[next] = advance / clust_size;
3948 next++;
3949 if (j) i++;
3950 }
3951 }
3952
3953 return S_OK;
3954 }
3955
3956 /***********************************************************************
3957 * ScriptApplyLogicalWidth (USP10.@)
3958 *
3959 * Generate glyph advance widths.
3960 *
3961 * PARAMS
3962 * dx [I] Array of logical advance widths.
3963 * num_chars [I] Number of characters.
3964 * num_glyphs [I] Number of glyphs.
3965 * log_clust [I] Array of logical clusters.
3966 * sva [I] Visual attributes.
3967 * advance [I] Array of glyph advance widths.
3968 * sa [I] Script analysis.
3969 * abc [I/O] Summed ABC widths.
3970 * justify [O] Array of glyph advance widths.
3971 *
3972 * RETURNS
3973 * Success: S_OK
3974 * Failure: a non-zero HRESULT.
3975 */
3976 HRESULT WINAPI ScriptApplyLogicalWidth(const int *dx, int num_chars, int num_glyphs,
3977 const WORD *log_clust, const SCRIPT_VISATTR *sva,
3978 const int *advance, const SCRIPT_ANALYSIS *sa,
3979 ABC *abc, int *justify)
3980 {
3981 int i;
3982
3983 FIXME("(%p, %d, %d, %p, %p, %p, %p, %p, %p)\n",
3984 dx, num_chars, num_glyphs, log_clust, sva, advance, sa, abc, justify);
3985
3986 for (i = 0; i < num_chars; i++) justify[i] = advance[i];
3987 return S_OK;
3988 }
3989
3990 HRESULT WINAPI ScriptJustify(const SCRIPT_VISATTR *sva, const int *advance,
3991 int num_glyphs, int dx, int min_kashida, int *justify)
3992 {
3993 int i;
3994
3995 FIXME("(%p, %p, %d, %d, %d, %p)\n", sva, advance, num_glyphs, dx, min_kashida, justify);
3996
3997 for (i = 0; i < num_glyphs; i++) justify[i] = advance[i];
3998 return S_OK;
3999 }
4000
4001 HRESULT WINAPI ScriptGetFontScriptTags( HDC hdc, SCRIPT_CACHE *psc, SCRIPT_ANALYSIS *psa, int cMaxTags, OPENTYPE_TAG *pScriptTags, int *pcTags)
4002 {
4003 HRESULT hr;
4004 if (!pScriptTags || !pcTags || cMaxTags == 0) return E_INVALIDARG;
4005 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
4006
4007 return SHAPE_GetFontScriptTags(hdc, (ScriptCache *)*psc, psa, cMaxTags, pScriptTags, pcTags);
4008 }
4009
4010 HRESULT WINAPI ScriptGetFontLanguageTags( HDC hdc, SCRIPT_CACHE *psc, SCRIPT_ANALYSIS *psa, OPENTYPE_TAG tagScript, int cMaxTags, OPENTYPE_TAG *pLangSysTags, int *pcTags)
4011 {
4012 HRESULT hr;
4013 if (!pLangSysTags || !pcTags || cMaxTags == 0) return E_INVALIDARG;
4014 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
4015
4016 return SHAPE_GetFontLanguageTags(hdc, (ScriptCache *)*psc, psa, tagScript, cMaxTags, pLangSysTags, pcTags);
4017 }
4018
4019 HRESULT WINAPI ScriptGetFontFeatureTags( HDC hdc, SCRIPT_CACHE *psc, SCRIPT_ANALYSIS *psa, OPENTYPE_TAG tagScript, OPENTYPE_TAG tagLangSys, int cMaxTags, OPENTYPE_TAG *pFeatureTags, int *pcTags)
4020 {
4021 HRESULT hr;
4022 if (!pFeatureTags || !pcTags || cMaxTags == 0) return E_INVALIDARG;
4023 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
4024
4025 return SHAPE_GetFontFeatureTags(hdc, (ScriptCache *)*psc, psa, tagScript, tagLangSys, cMaxTags, pFeatureTags, pcTags);
4026 }
4027
4028 #ifdef __REACTOS__
4029 BOOL gbLpkPresent = FALSE;
4030 VOID WINAPI LpkPresent()
4031 {
4032 gbLpkPresent = TRUE; /* Turn it on this way! Wine is out of control! */
4033 }
4034 #endif