[USP10] Sync with Wine Staging 2.16. CORE-13762
[reactos.git] / reactos / 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 typedef struct {
681 HDC hdc;
682 DWORD dwFlags;
683 BOOL invalid;
684 int clip_len;
685 int cItems;
686 int cMaxGlyphs;
687 SCRIPT_ITEM* pItem;
688 int numItems;
689 StringGlyphs* glyphs;
690 SCRIPT_LOGATTR* logattrs;
691 SIZE* sz;
692 int* logical2visual;
693 } StringAnalysis;
694
695 typedef struct {
696 BOOL ascending;
697 WORD target;
698 } FindGlyph_struct;
699
700 BOOL usp10_array_reserve(void **elements, SIZE_T *capacity, SIZE_T count, SIZE_T size)
701 {
702 SIZE_T max_capacity, new_capacity;
703 void *new_elements;
704
705 if (count <= *capacity)
706 return TRUE;
707
708 max_capacity = ~(SIZE_T)0 / size;
709 if (count > max_capacity)
710 return FALSE;
711
712 new_capacity = max(1, *capacity);
713 while (new_capacity < count && new_capacity <= max_capacity / 2)
714 new_capacity *= 2;
715 if (new_capacity < count)
716 new_capacity = count;
717
718 if (!*elements)
719 new_elements = heap_alloc_zero(new_capacity * size);
720 else
721 new_elements = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *elements, new_capacity * size);
722 if (!new_elements)
723 return FALSE;
724
725 *elements = new_elements;
726 *capacity = new_capacity;
727 return TRUE;
728 }
729
730 /* TODO Fix font properties on Arabic locale */
731 static inline BOOL set_cache_font_properties(const HDC hdc, ScriptCache *sc)
732 {
733 if (!sc->sfnt)
734 {
735 sc->sfp.wgBlank = sc->tm.tmBreakChar;
736 sc->sfp.wgDefault = sc->tm.tmDefaultChar;
737 sc->sfp.wgInvalid = sc->sfp.wgBlank;
738 sc->sfp.wgKashida = 0xFFFF;
739 sc->sfp.iKashidaWidth = 0;
740 }
741 else
742 {
743 static const WCHAR chars[4] = {0x0020, 0x200B, 0xF71B, 0x0640};
744 /* U+0020: numeric space
745 U+200B: zero width space
746 U+F71B: unknown char found by black box testing
747 U+0640: kashida */
748 WORD gi[4];
749
750 if (GetGlyphIndicesW(hdc, chars, 4, gi, GGI_MARK_NONEXISTING_GLYPHS) != GDI_ERROR)
751 {
752 if(gi[0] != 0xFFFF) /* 0xFFFF: index of default non exist char */
753 sc->sfp.wgBlank = gi[0];
754 else
755 sc->sfp.wgBlank = 0;
756
757 sc->sfp.wgDefault = 0;
758
759 if (gi[2] != 0xFFFF)
760 sc->sfp.wgInvalid = gi[2];
761 else if (gi[1] != 0xFFFF)
762 sc->sfp.wgInvalid = gi[1];
763 else if (gi[0] != 0xFFFF)
764 sc->sfp.wgInvalid = gi[0];
765 else
766 sc->sfp.wgInvalid = 0;
767
768 sc->sfp.wgKashida = gi[3];
769
770 sc->sfp.iKashidaWidth = 0; /* TODO */
771 }
772 else
773 return FALSE;
774 }
775 return TRUE;
776 }
777
778 static inline void get_cache_font_properties(SCRIPT_FONTPROPERTIES *sfp, ScriptCache *sc)
779 {
780 sfp->wgBlank = sc->sfp.wgBlank;
781 sfp->wgDefault = sc->sfp.wgDefault;
782 sfp->wgInvalid = sc->sfp.wgInvalid;
783 sfp->wgKashida = sc->sfp.wgKashida;
784 sfp->iKashidaWidth = sc->sfp.iKashidaWidth;
785 }
786
787 static inline LONG get_cache_height(SCRIPT_CACHE *psc)
788 {
789 return ((ScriptCache *)*psc)->tm.tmHeight;
790 }
791
792 static inline BYTE get_cache_pitch_family(SCRIPT_CACHE *psc)
793 {
794 return ((ScriptCache *)*psc)->tm.tmPitchAndFamily;
795 }
796
797 static inline WORD get_cache_glyph(SCRIPT_CACHE *psc, DWORD c)
798 {
799 CacheGlyphPage *page = ((ScriptCache *)*psc)->page[c / 0x10000];
800 WORD *block;
801
802 if (!page) return 0;
803 block = page->glyphs[(c % 0x10000) >> GLYPH_BLOCK_SHIFT];
804 if (!block) return 0;
805 return block[(c % 0x10000) & GLYPH_BLOCK_MASK];
806 }
807
808 static inline WORD set_cache_glyph(SCRIPT_CACHE *psc, WCHAR c, WORD glyph)
809 {
810 CacheGlyphPage **page = &((ScriptCache *)*psc)->page[c / 0x10000];
811 WORD **block;
812 if (!*page && !(*page = heap_alloc_zero(sizeof(CacheGlyphPage)))) return 0;
813
814 block = &(*page)->glyphs[(c % 0x10000) >> GLYPH_BLOCK_SHIFT];
815 if (!*block && !(*block = heap_alloc_zero(sizeof(WORD) * GLYPH_BLOCK_SIZE))) return 0;
816 return ((*block)[(c % 0x10000) & GLYPH_BLOCK_MASK] = glyph);
817 }
818
819 static inline BOOL get_cache_glyph_widths(SCRIPT_CACHE *psc, WORD glyph, ABC *abc)
820 {
821 static const ABC nil;
822 ABC *block = ((ScriptCache *)*psc)->widths[glyph >> GLYPH_BLOCK_SHIFT];
823
824 if (!block || !memcmp(&block[glyph & GLYPH_BLOCK_MASK], &nil, sizeof(ABC))) return FALSE;
825 memcpy(abc, &block[glyph & GLYPH_BLOCK_MASK], sizeof(ABC));
826 return TRUE;
827 }
828
829 static inline BOOL set_cache_glyph_widths(SCRIPT_CACHE *psc, WORD glyph, ABC *abc)
830 {
831 ABC **block = &((ScriptCache *)*psc)->widths[glyph >> GLYPH_BLOCK_SHIFT];
832
833 if (!*block && !(*block = heap_alloc_zero(sizeof(ABC) * GLYPH_BLOCK_SIZE))) return FALSE;
834 memcpy(&(*block)[glyph & GLYPH_BLOCK_MASK], abc, sizeof(ABC));
835 return TRUE;
836 }
837
838 static HRESULT init_script_cache(const HDC hdc, SCRIPT_CACHE *psc)
839 {
840 ScriptCache *sc;
841 int size;
842
843 if (!psc) return E_INVALIDARG;
844 if (*psc) return S_OK;
845 if (!hdc) return E_PENDING;
846
847 if (!(sc = heap_alloc_zero(sizeof(ScriptCache)))) return E_OUTOFMEMORY;
848 if (!GetTextMetricsW(hdc, &sc->tm))
849 {
850 heap_free(sc);
851 return E_INVALIDARG;
852 }
853 size = GetOutlineTextMetricsW(hdc, 0, NULL);
854 if (size)
855 {
856 sc->otm = heap_alloc(size);
857 sc->otm->otmSize = size;
858 GetOutlineTextMetricsW(hdc, size, sc->otm);
859 }
860 if (!GetObjectW(GetCurrentObject(hdc, OBJ_FONT), sizeof(LOGFONTW), &sc->lf))
861 {
862 heap_free(sc);
863 return E_INVALIDARG;
864 }
865 sc->sfnt = (GetFontData(hdc, MS_MAKE_TAG('h','e','a','d'), 0, NULL, 0)!=GDI_ERROR);
866 if (!set_cache_font_properties(hdc, sc))
867 {
868 heap_free(sc);
869 return E_INVALIDARG;
870 }
871 *psc = sc;
872 TRACE("<- %p\n", sc);
873 return S_OK;
874 }
875
876 static WCHAR mirror_char( WCHAR ch )
877 {
878 extern const WCHAR wine_mirror_map[] DECLSPEC_HIDDEN;
879 return ch + wine_mirror_map[wine_mirror_map[ch >> 8] + (ch & 0xff)];
880 }
881
882 static DWORD decode_surrogate_pair(const WCHAR *str, unsigned int index, unsigned int end)
883 {
884 if (index < end-1 && IS_SURROGATE_PAIR(str[index],str[index+1]))
885 {
886 DWORD ch = 0x10000 + ((str[index] - 0xd800) << 10) + (str[index+1] - 0xdc00);
887 TRACE("Surrogate Pair %x %x => %x\n",str[index], str[index+1], ch);
888 return ch;
889 }
890 return 0;
891 }
892
893 static int usp10_compare_script_range(const void *key, const void *value)
894 {
895 const struct usp10_script_range *range = value;
896 const DWORD *ch = key;
897
898 if (*ch < range->rangeFirst)
899 return -1;
900 if (*ch > range->rangeLast)
901 return 1;
902 return 0;
903 }
904
905 static enum usp10_script get_char_script(const WCHAR *str, unsigned int index,
906 unsigned int end, unsigned int *consumed)
907 {
908 static const WCHAR latin_punc[] = {'#','$','&','\'',',',';','<','>','?','@','\\','^','_','`','{','|','}','~', 0x00a0, 0};
909 struct usp10_script_range *range;
910 WORD type = 0, type2 = 0;
911 DWORD ch;
912
913 *consumed = 1;
914
915 if (str[index] == 0xc || str[index] == 0x20 || str[index] == 0x202f)
916 return Script_CR;
917
918 /* These punctuation characters are separated out as Latin punctuation */
919 if (strchrW(latin_punc,str[index]))
920 return Script_Punctuation2;
921
922 /* These chars are itemized as Punctuation by Windows */
923 if (str[index] == 0x2212 || str[index] == 0x2044)
924 return Script_Punctuation;
925
926 /* Currency Symbols by Unicode point */
927 switch (str[index])
928 {
929 case 0x09f2:
930 case 0x09f3: return Script_Bengali_Currency;
931 case 0x0af1: return Script_Gujarati_Currency;
932 case 0x0e3f: return Script_Thai_Currency;
933 case 0x20aa: return Script_Hebrew_Currency;
934 case 0x20ab: return Script_Vietnamese_Currency;
935 case 0xfb29: return Script_Hebrew_Currency;
936 }
937
938 GetStringTypeW(CT_CTYPE1, &str[index], 1, &type);
939 GetStringTypeW(CT_CTYPE2, &str[index], 1, &type2);
940
941 if (type == 0)
942 return SCRIPT_UNDEFINED;
943
944 if (type & C1_CNTRL)
945 return Script_Control;
946
947 ch = decode_surrogate_pair(str, index, end);
948 if (ch)
949 *consumed = 2;
950 else
951 ch = str[index];
952
953 if (!(range = bsearch(&ch, script_ranges, ARRAY_SIZE(script_ranges),
954 sizeof(*script_ranges), usp10_compare_script_range)))
955 return (*consumed == 2) ? Script_Surrogates : Script_Undefined;
956
957 if (range->numericScript && (type & C1_DIGIT || type2 == C2_ARABICNUMBER))
958 return range->numericScript;
959 if (range->punctScript && type & C1_PUNCT)
960 return range->punctScript;
961 return range->script;
962 }
963
964 static int compare_FindGlyph(const void *a, const void* b)
965 {
966 const FindGlyph_struct *find = (FindGlyph_struct*)a;
967 const WORD *idx= (WORD*)b;
968 int rc = 0;
969
970 if ( find->target > *idx)
971 rc = 1;
972 else if (find->target < *idx)
973 rc = -1;
974
975 if (!find->ascending)
976 rc *= -1;
977 return rc;
978 }
979
980 int USP10_FindGlyphInLogClust(const WORD* pwLogClust, int cChars, WORD target)
981 {
982 FindGlyph_struct fgs;
983 WORD *ptr;
984 INT k;
985
986 if (pwLogClust[0] < pwLogClust[cChars-1])
987 fgs.ascending = TRUE;
988 else
989 fgs.ascending = FALSE;
990
991 fgs.target = target;
992 ptr = bsearch(&fgs, pwLogClust, cChars, sizeof(WORD), compare_FindGlyph);
993
994 if (!ptr)
995 return -1;
996
997 for (k = (ptr - pwLogClust)-1; k >= 0 && pwLogClust[k] == target; k--)
998 ;
999 k++;
1000
1001 return k;
1002 }
1003
1004 /***********************************************************************
1005 * ScriptFreeCache (USP10.@)
1006 *
1007 * Free a script cache.
1008 *
1009 * PARAMS
1010 * psc [I/O] Script cache.
1011 *
1012 * RETURNS
1013 * Success: S_OK
1014 * Failure: Non-zero HRESULT value.
1015 */
1016 HRESULT WINAPI ScriptFreeCache(SCRIPT_CACHE *psc)
1017 {
1018 TRACE("%p\n", psc);
1019
1020 if (psc && *psc)
1021 {
1022 unsigned int i;
1023 INT n;
1024 for (i = 0; i < GLYPH_MAX / GLYPH_BLOCK_SIZE; i++)
1025 {
1026 heap_free(((ScriptCache *)*psc)->widths[i]);
1027 }
1028 for (i = 0; i < NUM_PAGES; i++)
1029 {
1030 unsigned int j;
1031 if (((ScriptCache *)*psc)->page[i])
1032 for (j = 0; j < GLYPH_MAX / GLYPH_BLOCK_SIZE; j++)
1033 heap_free(((ScriptCache *)*psc)->page[i]->glyphs[j]);
1034 heap_free(((ScriptCache *)*psc)->page[i]);
1035 }
1036 heap_free(((ScriptCache *)*psc)->GSUB_Table);
1037 heap_free(((ScriptCache *)*psc)->GDEF_Table);
1038 heap_free(((ScriptCache *)*psc)->CMAP_Table);
1039 heap_free(((ScriptCache *)*psc)->GPOS_Table);
1040 for (n = 0; n < ((ScriptCache *)*psc)->script_count; n++)
1041 {
1042 int j;
1043 for (j = 0; j < ((ScriptCache *)*psc)->scripts[n].language_count; j++)
1044 {
1045 int k;
1046 for (k = 0; k < ((ScriptCache *)*psc)->scripts[n].languages[j].feature_count; k++)
1047 heap_free(((ScriptCache *)*psc)->scripts[n].languages[j].features[k].lookups);
1048 heap_free(((ScriptCache *)*psc)->scripts[n].languages[j].features);
1049 }
1050 for (j = 0; j < ((ScriptCache *)*psc)->scripts[n].default_language.feature_count; j++)
1051 heap_free(((ScriptCache *)*psc)->scripts[n].default_language.features[j].lookups);
1052 heap_free(((ScriptCache *)*psc)->scripts[n].default_language.features);
1053 heap_free(((ScriptCache *)*psc)->scripts[n].languages);
1054 }
1055 heap_free(((ScriptCache *)*psc)->scripts);
1056 heap_free(((ScriptCache *)*psc)->otm);
1057 heap_free(*psc);
1058 *psc = NULL;
1059 }
1060 return S_OK;
1061 }
1062
1063 /***********************************************************************
1064 * ScriptGetProperties (USP10.@)
1065 *
1066 * Retrieve a list of script properties.
1067 *
1068 * PARAMS
1069 * props [I] Pointer to an array of SCRIPT_PROPERTIES pointers.
1070 * num [I] Pointer to the number of scripts.
1071 *
1072 * RETURNS
1073 * Success: S_OK
1074 * Failure: Non-zero HRESULT value.
1075 *
1076 * NOTES
1077 * Behaviour matches WinXP.
1078 */
1079 HRESULT WINAPI ScriptGetProperties(const SCRIPT_PROPERTIES ***props, int *num)
1080 {
1081 TRACE("(%p,%p)\n", props, num);
1082
1083 if (!props && !num) return E_INVALIDARG;
1084
1085 if (num) *num = sizeof(script_props)/sizeof(script_props[0]);
1086 if (props) *props = script_props;
1087
1088 return S_OK;
1089 }
1090
1091 /***********************************************************************
1092 * ScriptGetFontProperties (USP10.@)
1093 *
1094 * Get information on special glyphs.
1095 *
1096 * PARAMS
1097 * hdc [I] Device context.
1098 * psc [I/O] Opaque pointer to a script cache.
1099 * sfp [O] Font properties structure.
1100 */
1101 HRESULT WINAPI ScriptGetFontProperties(HDC hdc, SCRIPT_CACHE *psc, SCRIPT_FONTPROPERTIES *sfp)
1102 {
1103 HRESULT hr;
1104
1105 TRACE("%p,%p,%p\n", hdc, psc, sfp);
1106
1107 if (!sfp) return E_INVALIDARG;
1108 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
1109
1110 if (sfp->cBytes != sizeof(SCRIPT_FONTPROPERTIES))
1111 return E_INVALIDARG;
1112
1113 get_cache_font_properties(sfp, *psc);
1114
1115 return S_OK;
1116 }
1117
1118 /***********************************************************************
1119 * ScriptRecordDigitSubstitution (USP10.@)
1120 *
1121 * Record digit substitution settings for a given locale.
1122 *
1123 * PARAMS
1124 * locale [I] Locale identifier.
1125 * sds [I] Structure to record substitution settings.
1126 *
1127 * RETURNS
1128 * Success: S_OK
1129 * Failure: E_POINTER if sds is NULL, E_INVALIDARG otherwise.
1130 *
1131 * SEE ALSO
1132 * http://blogs.msdn.com/michkap/archive/2006/02/22/536877.aspx
1133 */
1134 HRESULT WINAPI ScriptRecordDigitSubstitution(LCID locale, SCRIPT_DIGITSUBSTITUTE *sds)
1135 {
1136 DWORD plgid, sub;
1137
1138 TRACE("0x%x, %p\n", locale, sds);
1139
1140 /* This implementation appears to be correct for all languages, but it's
1141 * not clear if sds->DigitSubstitute is ever set to anything except
1142 * CONTEXT or NONE in reality */
1143
1144 if (!sds) return E_POINTER;
1145
1146 locale = ConvertDefaultLocale(locale);
1147
1148 if (!IsValidLocale(locale, LCID_INSTALLED))
1149 return E_INVALIDARG;
1150
1151 plgid = PRIMARYLANGID(LANGIDFROMLCID(locale));
1152 sds->TraditionalDigitLanguage = plgid;
1153
1154 if (plgid == LANG_ARABIC || plgid == LANG_FARSI)
1155 sds->NationalDigitLanguage = plgid;
1156 else
1157 sds->NationalDigitLanguage = LANG_ENGLISH;
1158
1159 if (!GetLocaleInfoW(locale, LOCALE_IDIGITSUBSTITUTION | LOCALE_RETURN_NUMBER,
1160 (WCHAR *)&sub, sizeof(sub) / sizeof(WCHAR)))
1161 return E_INVALIDARG;
1162
1163 switch (sub)
1164 {
1165 case 0:
1166 if (plgid == LANG_ARABIC || plgid == LANG_FARSI)
1167 sds->DigitSubstitute = SCRIPT_DIGITSUBSTITUTE_CONTEXT;
1168 else
1169 sds->DigitSubstitute = SCRIPT_DIGITSUBSTITUTE_NONE;
1170 break;
1171 case 1:
1172 sds->DigitSubstitute = SCRIPT_DIGITSUBSTITUTE_NONE;
1173 break;
1174 case 2:
1175 sds->DigitSubstitute = SCRIPT_DIGITSUBSTITUTE_NATIONAL;
1176 break;
1177 default:
1178 sds->DigitSubstitute = SCRIPT_DIGITSUBSTITUTE_TRADITIONAL;
1179 break;
1180 }
1181
1182 sds->dwReserved = 0;
1183 return S_OK;
1184 }
1185
1186 /***********************************************************************
1187 * ScriptApplyDigitSubstitution (USP10.@)
1188 *
1189 * Apply digit substitution settings.
1190 *
1191 * PARAMS
1192 * sds [I] Structure with recorded substitution settings.
1193 * sc [I] Script control structure.
1194 * ss [I] Script state structure.
1195 *
1196 * RETURNS
1197 * Success: S_OK
1198 * Failure: E_INVALIDARG if sds is invalid. Otherwise an HRESULT.
1199 */
1200 HRESULT WINAPI ScriptApplyDigitSubstitution(const SCRIPT_DIGITSUBSTITUTE *sds,
1201 SCRIPT_CONTROL *sc, SCRIPT_STATE *ss)
1202 {
1203 SCRIPT_DIGITSUBSTITUTE psds;
1204
1205 TRACE("%p, %p, %p\n", sds, sc, ss);
1206
1207 if (!sc || !ss) return E_POINTER;
1208 if (!sds)
1209 {
1210 sds = &psds;
1211 if (ScriptRecordDigitSubstitution(LOCALE_USER_DEFAULT, &psds) != S_OK)
1212 return E_INVALIDARG;
1213 }
1214
1215 sc->uDefaultLanguage = LANG_ENGLISH;
1216 sc->fContextDigits = 0;
1217 ss->fDigitSubstitute = 0;
1218
1219 switch (sds->DigitSubstitute) {
1220 case SCRIPT_DIGITSUBSTITUTE_CONTEXT:
1221 case SCRIPT_DIGITSUBSTITUTE_NATIONAL:
1222 case SCRIPT_DIGITSUBSTITUTE_NONE:
1223 case SCRIPT_DIGITSUBSTITUTE_TRADITIONAL:
1224 return S_OK;
1225 default:
1226 return E_INVALIDARG;
1227 }
1228 }
1229
1230 static inline BOOL is_indic(enum usp10_script script)
1231 {
1232 return (script >= Script_Devanagari && script <= Script_Malayalam_Numeric);
1233 }
1234
1235 static inline enum usp10_script base_indic(enum usp10_script script)
1236 {
1237 switch (script)
1238 {
1239 case Script_Devanagari:
1240 case Script_Devanagari_Numeric: return Script_Devanagari;
1241 case Script_Bengali:
1242 case Script_Bengali_Numeric:
1243 case Script_Bengali_Currency: return Script_Bengali;
1244 case Script_Gurmukhi:
1245 case Script_Gurmukhi_Numeric: return Script_Gurmukhi;
1246 case Script_Gujarati:
1247 case Script_Gujarati_Numeric:
1248 case Script_Gujarati_Currency: return Script_Gujarati;
1249 case Script_Oriya:
1250 case Script_Oriya_Numeric: return Script_Oriya;
1251 case Script_Tamil:
1252 case Script_Tamil_Numeric: return Script_Tamil;
1253 case Script_Telugu:
1254 case Script_Telugu_Numeric: return Script_Telugu;
1255 case Script_Kannada:
1256 case Script_Kannada_Numeric: return Script_Kannada;
1257 case Script_Malayalam:
1258 case Script_Malayalam_Numeric: return Script_Malayalam;
1259 default:
1260 return Script_Undefined;
1261 };
1262 }
1263
1264 static BOOL script_is_numeric(enum usp10_script script)
1265 {
1266 return scriptInformation[script].props.fNumeric;
1267 }
1268
1269 static HRESULT _ItemizeInternal(const WCHAR *pwcInChars, int cInChars,
1270 int cMaxItems, const SCRIPT_CONTROL *psControl,
1271 const SCRIPT_STATE *psState, SCRIPT_ITEM *pItems,
1272 OPENTYPE_TAG *pScriptTags, int *pcItems)
1273 {
1274
1275 #define Numeric_space 0x0020
1276 #define ZWSP 0x200B
1277 #define ZWNJ 0x200C
1278 #define ZWJ 0x200D
1279
1280 enum usp10_script last_indic = Script_Undefined;
1281 int cnt = 0, index = 0, str = 0;
1282 enum usp10_script New_Script = -1;
1283 int i;
1284 WORD *levels = NULL;
1285 WORD *layout_levels = NULL;
1286 WORD *overrides = NULL;
1287 WORD *strength = NULL;
1288 enum usp10_script *scripts;
1289 WORD baselevel = 0;
1290 WORD baselayout = 0;
1291 BOOL new_run;
1292 WORD layoutRTL = 0;
1293 BOOL forceLevels = FALSE;
1294 unsigned int consumed = 0;
1295 HRESULT res = E_OUTOFMEMORY;
1296
1297 TRACE("%s,%d,%d,%p,%p,%p,%p\n", debugstr_wn(pwcInChars, cInChars), cInChars, cMaxItems,
1298 psControl, psState, pItems, pcItems);
1299
1300 if (!pwcInChars || !cInChars || !pItems || cMaxItems < 2)
1301 return E_INVALIDARG;
1302
1303 if (!(scripts = heap_alloc(cInChars * sizeof(*scripts))))
1304 return E_OUTOFMEMORY;
1305
1306 for (i = 0; i < cInChars; i++)
1307 {
1308 if (!consumed)
1309 {
1310 scripts[i] = get_char_script(pwcInChars,i,cInChars,&consumed);
1311 consumed --;
1312 }
1313 else
1314 {
1315 scripts[i] = scripts[i-1];
1316 consumed --;
1317 }
1318 /* Devanagari danda (U+0964) and double danda (U+0965) are used for
1319 all Indic scripts */
1320 if ((pwcInChars[i] == 0x964 || pwcInChars[i] ==0x965) && last_indic != Script_Undefined)
1321 scripts[i] = last_indic;
1322 else if (is_indic(scripts[i]))
1323 last_indic = base_indic(scripts[i]);
1324
1325 /* Some unicode points :
1326 (Zero Width Space U+200B - Right-to-Left Mark U+200F)
1327 (Left Right Embed U+202A - Left Right Override U+202D)
1328 (Left Right Isolate U+2066 - Pop Directional Isolate U+2069)
1329 will force us into bidi mode */
1330 if (!forceLevels && ((pwcInChars[i] >= 0x200B && pwcInChars[i] <= 0x200F) ||
1331 (pwcInChars[i] >= 0x202A && pwcInChars[i] <= 0x202E) ||
1332 (pwcInChars[i] >= 0x2066 && pwcInChars[i] <= 0x2069)))
1333
1334 forceLevels = TRUE;
1335
1336 /* Diacritical marks merge with other scripts */
1337 if (scripts[i] == Script_Diacritical)
1338 {
1339 if (i > 0)
1340 {
1341 if (pScriptTags)
1342 scripts[i] = scripts[i-1];
1343 else
1344 {
1345 int j;
1346 BOOL asian = FALSE;
1347 enum usp10_script first_script = scripts[i-1];
1348 for (j = i-1; j >= 0 && scripts[j] == first_script && pwcInChars[j] != Numeric_space; j--)
1349 {
1350 enum usp10_script original = scripts[j];
1351 if (original == Script_Ideograph || original == Script_Kana || original == Script_Yi || original == Script_CJK_Han || original == Script_Bopomofo)
1352 {
1353 asian = TRUE;
1354 break;
1355 }
1356 if (original != Script_MathAlpha && scriptInformation[scripts[j]].props.fComplex)
1357 break;
1358 scripts[j] = scripts[i];
1359 if (original == Script_Punctuation2)
1360 break;
1361 }
1362 if (j >= 0 && (scriptInformation[scripts[j]].props.fComplex || asian))
1363 scripts[i] = scripts[j];
1364 }
1365 }
1366 }
1367 }
1368
1369 for (i = 0; i < cInChars; i++)
1370 {
1371 /* Joiners get merged preferencially right */
1372 if (i > 0 && (pwcInChars[i] == ZWJ || pwcInChars[i] == ZWNJ || pwcInChars[i] == ZWSP))
1373 {
1374 int j;
1375 if (i+1 == cInChars)
1376 scripts[i] = scripts[i-1];
1377 else
1378 {
1379 for (j = i+1; j < cInChars; j++)
1380 {
1381 if (pwcInChars[j] != ZWJ && pwcInChars[j] != ZWNJ
1382 && pwcInChars[j] != ZWSP && pwcInChars[j] != Numeric_space)
1383 {
1384 scripts[i] = scripts[j];
1385 break;
1386 }
1387 }
1388 }
1389 }
1390 }
1391
1392 if (psState && psControl)
1393 {
1394 levels = heap_alloc_zero(cInChars * sizeof(WORD));
1395 if (!levels)
1396 goto nomemory;
1397
1398 overrides = heap_alloc_zero(cInChars * sizeof(WORD));
1399 if (!overrides)
1400 goto nomemory;
1401
1402 layout_levels = heap_alloc_zero(cInChars * sizeof(WORD));
1403 if (!layout_levels)
1404 goto nomemory;
1405
1406 if (psState->fOverrideDirection)
1407 {
1408 if (!forceLevels)
1409 {
1410 SCRIPT_STATE s = *psState;
1411 s.fOverrideDirection = FALSE;
1412 BIDI_DetermineLevels(pwcInChars, cInChars, &s, psControl, layout_levels, overrides);
1413 if (odd(layout_levels[0]))
1414 forceLevels = TRUE;
1415 else for (i = 0; i < cInChars; i++)
1416 if (layout_levels[i]!=layout_levels[0])
1417 {
1418 forceLevels = TRUE;
1419 break;
1420 }
1421 }
1422
1423 BIDI_DetermineLevels(pwcInChars, cInChars, psState, psControl, levels, overrides);
1424 }
1425 else
1426 {
1427 BIDI_DetermineLevels(pwcInChars, cInChars, psState, psControl, levels, overrides);
1428 memcpy(layout_levels, levels, cInChars * sizeof(WORD));
1429 }
1430 baselevel = levels[0];
1431 baselayout = layout_levels[0];
1432 for (i = 0; i < cInChars; i++)
1433 if (levels[i]!=levels[0])
1434 break;
1435 if (i >= cInChars && !odd(baselevel) && !odd(psState->uBidiLevel) && !forceLevels)
1436 {
1437 heap_free(levels);
1438 heap_free(overrides);
1439 heap_free(layout_levels);
1440 overrides = NULL;
1441 levels = NULL;
1442 layout_levels = NULL;
1443 }
1444 else
1445 {
1446 static const WCHAR math_punc[] = {'#','$','%','+',',','-','.','/',':',0x2212, 0x2044, 0x00a0,0};
1447 static const WCHAR repeatable_math_punc[] = {'#','$','%','+','-','/',0x2212, 0x2044,0};
1448
1449 strength = heap_alloc_zero(cInChars * sizeof(WORD));
1450 if (!strength)
1451 goto nomemory;
1452 BIDI_GetStrengths(pwcInChars, cInChars, psControl, strength);
1453
1454 /* We currently mis-level leading Diacriticals */
1455 if (scripts[0] == Script_Diacritical)
1456 for (i = 0; i < cInChars && scripts[0] == Script_Diacritical; i++)
1457 {
1458 levels[i] = odd(levels[i])?levels[i]+1:levels[i];
1459 strength[i] = BIDI_STRONG;
1460 }
1461
1462 /* Math punctuation bordered on both sides by numbers can be
1463 merged into the number */
1464 for (i = 0; i < cInChars; i++)
1465 {
1466 if (i > 0 && i < cInChars-1 &&
1467 script_is_numeric(scripts[i-1]) &&
1468 strchrW(math_punc, pwcInChars[i]))
1469 {
1470 if (script_is_numeric(scripts[i+1]))
1471 {
1472 scripts[i] = scripts[i+1];
1473 levels[i] = levels[i-1];
1474 strength[i] = strength[i-1];
1475 i++;
1476 }
1477 else if (strchrW(repeatable_math_punc, pwcInChars[i]))
1478 {
1479 int j;
1480 for (j = i+1; j < cInChars; j++)
1481 {
1482 if (script_is_numeric(scripts[j]))
1483 {
1484 for(;i<j; i++)
1485 {
1486 scripts[i] = scripts[j];
1487 levels[i] = levels[i-1];
1488 strength[i] = strength[i-1];
1489 }
1490 }
1491 else if (pwcInChars[i] != pwcInChars[j]) break;
1492 }
1493 }
1494 }
1495 }
1496
1497 for (i = 0; i < cInChars; i++)
1498 {
1499 /* Numerics at level 0 get bumped to level 2 */
1500 if (!overrides[i] && (levels[i] == 0 || (odd(psState->uBidiLevel)
1501 && levels[i] == psState->uBidiLevel + 1)) && script_is_numeric(scripts[i]))
1502 {
1503 levels[i] = 2;
1504 }
1505
1506 /* Joiners get merged preferencially right */
1507 if (i > 0 && (pwcInChars[i] == ZWJ || pwcInChars[i] == ZWNJ || pwcInChars[i] == ZWSP))
1508 {
1509 int j;
1510 if (i+1 == cInChars && levels[i-1] == levels[i])
1511 strength[i] = strength[i-1];
1512 else
1513 for (j = i+1; j < cInChars && levels[i] == levels[j]; j++)
1514 if (pwcInChars[j] != ZWJ && pwcInChars[j] != ZWNJ
1515 && pwcInChars[j] != ZWSP && pwcInChars[j] != Numeric_space)
1516 {
1517 strength[i] = strength[j];
1518 break;
1519 }
1520 }
1521 }
1522 if (psControl->fMergeNeutralItems)
1523 {
1524 /* Merge the neutrals */
1525 for (i = 0; i < cInChars; i++)
1526 {
1527 if (strength[i] == BIDI_NEUTRAL || strength[i] == BIDI_WEAK)
1528 {
1529 int j;
1530 for (j = i; j > 0; j--)
1531 {
1532 if (levels[i] != levels[j])
1533 break;
1534 if ((strength[j] == BIDI_STRONG) || (strength[i] == BIDI_NEUTRAL && strength[j] == BIDI_WEAK))
1535 {
1536 scripts[i] = scripts[j];
1537 strength[i] = strength[j];
1538 break;
1539 }
1540 }
1541 }
1542 /* Try going the other way */
1543 if (strength[i] == BIDI_NEUTRAL || strength[i] == BIDI_WEAK)
1544 {
1545 int j;
1546 for (j = i; j < cInChars; j++)
1547 {
1548 if (levels[i] != levels[j])
1549 break;
1550 if ((strength[j] == BIDI_STRONG) || (strength[i] == BIDI_NEUTRAL && strength[j] == BIDI_WEAK))
1551 {
1552 scripts[i] = scripts[j];
1553 strength[i] = strength[j];
1554 break;
1555 }
1556 }
1557 }
1558 }
1559 }
1560 }
1561 }
1562
1563 while ((!levels || (levels && cnt+1 < cInChars && levels[cnt+1] == levels[0]))
1564 && (cnt < cInChars && pwcInChars[cnt] == Numeric_space))
1565 cnt++;
1566
1567 if (cnt == cInChars) /* All Spaces */
1568 {
1569 cnt = 0;
1570 New_Script = scripts[cnt];
1571 }
1572
1573 pItems[index].iCharPos = 0;
1574 pItems[index].a = scriptInformation[scripts[cnt]].a;
1575 if (pScriptTags)
1576 pScriptTags[index] = scriptInformation[scripts[cnt]].scriptTag;
1577
1578 if (strength && strength[cnt] == BIDI_STRONG)
1579 str = strength[cnt];
1580 else if (strength)
1581 str = strength[0];
1582
1583 cnt = 0;
1584
1585 if (levels)
1586 {
1587 if (strength[cnt] == BIDI_STRONG)
1588 layoutRTL = odd(layout_levels[cnt]);
1589 else
1590 layoutRTL = (psState->uBidiLevel || odd(layout_levels[cnt]));
1591 if (overrides)
1592 pItems[index].a.s.fOverrideDirection = (overrides[cnt] != 0);
1593 pItems[index].a.fRTL = odd(levels[cnt]);
1594 if (script_is_numeric(pItems[index].a.eScript))
1595 pItems[index].a.fLayoutRTL = layoutRTL;
1596 else
1597 pItems[index].a.fLayoutRTL = pItems[index].a.fRTL;
1598 pItems[index].a.s.uBidiLevel = levels[cnt];
1599 }
1600 else if (!pItems[index].a.s.uBidiLevel || (overrides && overrides[cnt]))
1601 {
1602 if (pItems[index].a.s.uBidiLevel != baselevel)
1603 pItems[index].a.s.fOverrideDirection = TRUE;
1604 layoutRTL = odd(baselayout);
1605 pItems[index].a.s.uBidiLevel = baselevel;
1606 pItems[index].a.fRTL = odd(baselevel);
1607 if (script_is_numeric(pItems[index].a.eScript))
1608 pItems[index].a.fLayoutRTL = odd(baselayout);
1609 else
1610 pItems[index].a.fLayoutRTL = pItems[index].a.fRTL;
1611 }
1612
1613 TRACE("New_Level=%i New_Strength=%i New_Script=%d, eScript=%d index=%d cnt=%d iCharPos=%d\n",
1614 levels?levels[cnt]:-1, str, New_Script, pItems[index].a.eScript, index, cnt,
1615 pItems[index].iCharPos);
1616
1617 for (cnt=1; cnt < cInChars; cnt++)
1618 {
1619 if(pwcInChars[cnt] != Numeric_space)
1620 New_Script = scripts[cnt];
1621 else if (levels)
1622 {
1623 int j = 1;
1624 while (cnt + j < cInChars - 1 && pwcInChars[cnt+j] == Numeric_space && levels[cnt] == levels[cnt+j])
1625 j++;
1626 if (cnt + j < cInChars && levels[cnt] == levels[cnt+j])
1627 New_Script = scripts[cnt+j];
1628 else
1629 New_Script = scripts[cnt];
1630 }
1631
1632 new_run = FALSE;
1633 /* merge space strengths*/
1634 if (strength && strength[cnt] == BIDI_STRONG && str != BIDI_STRONG && New_Script == pItems[index].a.eScript)
1635 str = BIDI_STRONG;
1636
1637 if (strength && strength[cnt] == BIDI_NEUTRAL && str == BIDI_STRONG && pwcInChars[cnt] != Numeric_space && New_Script == pItems[index].a.eScript)
1638 str = BIDI_NEUTRAL;
1639
1640 /* changes in level */
1641 if (levels && (levels[cnt] != pItems[index].a.s.uBidiLevel))
1642 {
1643 TRACE("Level break(%i/%i)\n",pItems[index].a.s.uBidiLevel,levels[cnt]);
1644 new_run = TRUE;
1645 }
1646 /* changes in strength */
1647 else if (strength && pwcInChars[cnt] != Numeric_space && str != strength[cnt])
1648 {
1649 TRACE("Strength break (%i/%i)\n",str,strength[cnt]);
1650 new_run = TRUE;
1651 }
1652 /* changes in script */
1653 else if (((pwcInChars[cnt] != Numeric_space) && (New_Script != -1) && (New_Script != pItems[index].a.eScript)) || (New_Script == Script_Control))
1654 {
1655 TRACE("Script break(%i/%i)\n",pItems[index].a.eScript,New_Script);
1656 new_run = TRUE;
1657 }
1658
1659 if (!new_run && strength && str == BIDI_STRONG)
1660 {
1661 layoutRTL = odd(layout_levels[cnt]);
1662 if (script_is_numeric(pItems[index].a.eScript))
1663 pItems[index].a.fLayoutRTL = layoutRTL;
1664 }
1665
1666 if (new_run)
1667 {
1668 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);
1669
1670 index++;
1671 if (index+1 > cMaxItems)
1672 goto nomemory;
1673
1674 if (strength)
1675 str = strength[cnt];
1676
1677 pItems[index].iCharPos = cnt;
1678 memset(&pItems[index].a, 0, sizeof(SCRIPT_ANALYSIS));
1679
1680 pItems[index].a = scriptInformation[New_Script].a;
1681 if (pScriptTags)
1682 pScriptTags[index] = scriptInformation[New_Script].scriptTag;
1683 if (levels)
1684 {
1685 if (overrides)
1686 pItems[index].a.s.fOverrideDirection = (overrides[cnt] != 0);
1687 if (layout_levels[cnt] == 0)
1688 layoutRTL = 0;
1689 else
1690 layoutRTL = (layoutRTL || odd(layout_levels[cnt]));
1691 pItems[index].a.fRTL = odd(levels[cnt]);
1692 if (script_is_numeric(pItems[index].a.eScript))
1693 pItems[index].a.fLayoutRTL = layoutRTL;
1694 else
1695 pItems[index].a.fLayoutRTL = pItems[index].a.fRTL;
1696 pItems[index].a.s.uBidiLevel = levels[cnt];
1697 }
1698 else if (!pItems[index].a.s.uBidiLevel || (overrides && overrides[cnt]))
1699 {
1700 if (pItems[index].a.s.uBidiLevel != baselevel)
1701 pItems[index].a.s.fOverrideDirection = TRUE;
1702 pItems[index].a.s.uBidiLevel = baselevel;
1703 pItems[index].a.fRTL = odd(baselevel);
1704 if (script_is_numeric(pItems[index].a.eScript))
1705 pItems[index].a.fLayoutRTL = layoutRTL;
1706 else
1707 pItems[index].a.fLayoutRTL = pItems[index].a.fRTL;
1708 }
1709
1710 TRACE("index=%d cnt=%d iCharPos=%d\n", index, cnt, pItems[index].iCharPos);
1711 }
1712 }
1713
1714 /* While not strictly necessary according to the spec, make sure the n+1
1715 * item is set up to prevent random behaviour if the caller erroneously
1716 * checks the n+1 structure */
1717 index++;
1718 if (index + 1 > cMaxItems) goto nomemory;
1719 memset(&pItems[index].a, 0, sizeof(SCRIPT_ANALYSIS));
1720
1721 TRACE("index=%d cnt=%d iCharPos=%d\n", index, cnt, pItems[index].iCharPos);
1722
1723 /* Set one SCRIPT_STATE item being returned */
1724 if (pcItems) *pcItems = index;
1725
1726 /* Set SCRIPT_ITEM */
1727 pItems[index].iCharPos = cnt; /* the last item contains the ptr to the lastchar */
1728 res = S_OK;
1729 nomemory:
1730 heap_free(levels);
1731 heap_free(overrides);
1732 heap_free(layout_levels);
1733 heap_free(strength);
1734 heap_free(scripts);
1735 return res;
1736 }
1737
1738 /***********************************************************************
1739 * ScriptItemizeOpenType (USP10.@)
1740 *
1741 * Split a Unicode string into shapeable parts.
1742 *
1743 * PARAMS
1744 * pwcInChars [I] String to split.
1745 * cInChars [I] Number of characters in pwcInChars.
1746 * cMaxItems [I] Maximum number of items to return.
1747 * psControl [I] Pointer to a SCRIPT_CONTROL structure.
1748 * psState [I] Pointer to a SCRIPT_STATE structure.
1749 * pItems [O] Buffer to receive SCRIPT_ITEM structures.
1750 * pScriptTags [O] Buffer to receive OPENTYPE_TAGs.
1751 * pcItems [O] Number of script items returned.
1752 *
1753 * RETURNS
1754 * Success: S_OK
1755 * Failure: Non-zero HRESULT value.
1756 */
1757 HRESULT WINAPI ScriptItemizeOpenType(const WCHAR *pwcInChars, int cInChars, int cMaxItems,
1758 const SCRIPT_CONTROL *psControl, const SCRIPT_STATE *psState,
1759 SCRIPT_ITEM *pItems, OPENTYPE_TAG *pScriptTags, int *pcItems)
1760 {
1761 return _ItemizeInternal(pwcInChars, cInChars, cMaxItems, psControl, psState, pItems, pScriptTags, pcItems);
1762 }
1763
1764 /***********************************************************************
1765 * ScriptItemize (USP10.@)
1766 *
1767 * Split a Unicode string into shapeable parts.
1768 *
1769 * PARAMS
1770 * pwcInChars [I] String to split.
1771 * cInChars [I] Number of characters in pwcInChars.
1772 * cMaxItems [I] Maximum number of items to return.
1773 * psControl [I] Pointer to a SCRIPT_CONTROL structure.
1774 * psState [I] Pointer to a SCRIPT_STATE structure.
1775 * pItems [O] Buffer to receive SCRIPT_ITEM structures.
1776 * pcItems [O] Number of script items returned.
1777 *
1778 * RETURNS
1779 * Success: S_OK
1780 * Failure: Non-zero HRESULT value.
1781 */
1782 HRESULT WINAPI ScriptItemize(const WCHAR *pwcInChars, int cInChars, int cMaxItems,
1783 const SCRIPT_CONTROL *psControl, const SCRIPT_STATE *psState,
1784 SCRIPT_ITEM *pItems, int *pcItems)
1785 {
1786 return _ItemizeInternal(pwcInChars, cInChars, cMaxItems, psControl, psState, pItems, NULL, pcItems);
1787 }
1788
1789 static inline int getGivenTabWidth(ScriptCache *psc, SCRIPT_TABDEF *pTabdef, int charPos, int current_x)
1790 {
1791 int defWidth;
1792 int cTabStops=0;
1793 INT *lpTabPos = NULL;
1794 INT nTabOrg = 0;
1795 INT x = 0;
1796
1797 if (pTabdef)
1798 lpTabPos = pTabdef->pTabStops;
1799
1800 if (pTabdef && pTabdef->iTabOrigin)
1801 {
1802 if (pTabdef->iScale)
1803 nTabOrg = (pTabdef->iTabOrigin * pTabdef->iScale)/4;
1804 else
1805 nTabOrg = pTabdef->iTabOrigin * psc->tm.tmAveCharWidth;
1806 }
1807
1808 if (pTabdef)
1809 cTabStops = pTabdef->cTabStops;
1810
1811 if (cTabStops == 1)
1812 {
1813 if (pTabdef->iScale)
1814 defWidth = ((pTabdef->pTabStops[0])*pTabdef->iScale) / 4;
1815 else
1816 defWidth = (pTabdef->pTabStops[0])*psc->tm.tmAveCharWidth;
1817 cTabStops = 0;
1818 }
1819 else
1820 {
1821 if (pTabdef->iScale)
1822 defWidth = (32 * pTabdef->iScale) / 4;
1823 else
1824 defWidth = 8 * psc->tm.tmAveCharWidth;
1825 }
1826
1827 for (; cTabStops>0 ; lpTabPos++, cTabStops--)
1828 {
1829 int position = *lpTabPos;
1830 if (position < 0)
1831 position = -1 * position;
1832 if (pTabdef->iScale)
1833 position = (position * pTabdef->iScale) / 4;
1834 else
1835 position = position * psc->tm.tmAveCharWidth;
1836
1837 if( nTabOrg + position > current_x)
1838 {
1839 if( position >= 0)
1840 {
1841 /* a left aligned tab */
1842 x = (nTabOrg + position) - current_x;
1843 break;
1844 }
1845 else
1846 {
1847 FIXME("Negative tabstop\n");
1848 break;
1849 }
1850 }
1851 }
1852 if ((!cTabStops) && (defWidth > 0))
1853 x =((((current_x - nTabOrg) / defWidth)+1) * defWidth) - current_x;
1854 else if ((!cTabStops) && (defWidth < 0))
1855 FIXME("TODO: Negative defWidth\n");
1856
1857 return x;
1858 }
1859
1860 /***********************************************************************
1861 * Helper function for ScriptStringAnalyse
1862 */
1863 static BOOL requires_fallback(HDC hdc, SCRIPT_CACHE *psc, SCRIPT_ANALYSIS *psa,
1864 const WCHAR *pwcInChars, int cChars )
1865 {
1866 /* FIXME: When to properly fallback is still a bit of a mystery */
1867 WORD *glyphs;
1868
1869 if (psa->fNoGlyphIndex)
1870 return FALSE;
1871
1872 if (init_script_cache(hdc, psc) != S_OK)
1873 return FALSE;
1874
1875 if (SHAPE_CheckFontForRequiredFeatures(hdc, (ScriptCache *)*psc, psa) != S_OK)
1876 return TRUE;
1877
1878 glyphs = heap_alloc(sizeof(WORD) * cChars);
1879 if (!glyphs)
1880 return FALSE;
1881 if (ScriptGetCMap(hdc, psc, pwcInChars, cChars, 0, glyphs) != S_OK)
1882 {
1883 heap_free(glyphs);
1884 return TRUE;
1885 }
1886 heap_free(glyphs);
1887
1888 return FALSE;
1889 }
1890
1891 static void find_fallback_font(enum usp10_script scriptid, WCHAR *FaceName)
1892 {
1893 HKEY hkey;
1894
1895 if (!RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Uniscribe\\Fallback", &hkey))
1896 {
1897 static const WCHAR szFmt[] = {'%','x',0};
1898 WCHAR value[10];
1899 DWORD count = LF_FACESIZE * sizeof(WCHAR);
1900 DWORD type;
1901
1902 sprintfW(value, szFmt, scriptInformation[scriptid].scriptTag);
1903 if (RegQueryValueExW(hkey, value, 0, &type, (BYTE *)FaceName, &count))
1904 lstrcpyW(FaceName,scriptInformation[scriptid].fallbackFont);
1905 RegCloseKey(hkey);
1906 }
1907 else
1908 lstrcpyW(FaceName,scriptInformation[scriptid].fallbackFont);
1909 }
1910
1911 /***********************************************************************
1912 * ScriptStringAnalyse (USP10.@)
1913 *
1914 */
1915 HRESULT WINAPI ScriptStringAnalyse(HDC hdc, const void *pString, int cString,
1916 int cGlyphs, int iCharset, DWORD dwFlags,
1917 int iReqWidth, SCRIPT_CONTROL *psControl,
1918 SCRIPT_STATE *psState, const int *piDx,
1919 SCRIPT_TABDEF *pTabdef, const BYTE *pbInClass,
1920 SCRIPT_STRING_ANALYSIS *pssa)
1921 {
1922 HRESULT hr = E_OUTOFMEMORY;
1923 StringAnalysis *analysis = NULL;
1924 SCRIPT_CONTROL sControl;
1925 SCRIPT_STATE sState;
1926 int i, num_items = 255;
1927 BYTE *BidiLevel;
1928 WCHAR *iString = NULL;
1929
1930 TRACE("(%p,%p,%d,%d,%d,0x%x,%d,%p,%p,%p,%p,%p,%p)\n",
1931 hdc, pString, cString, cGlyphs, iCharset, dwFlags, iReqWidth,
1932 psControl, psState, piDx, pTabdef, pbInClass, pssa);
1933
1934 if (iCharset != -1)
1935 {
1936 FIXME("Only Unicode strings are supported\n");
1937 return E_INVALIDARG;
1938 }
1939 if (cString < 1 || !pString) return E_INVALIDARG;
1940 if ((dwFlags & SSA_GLYPHS) && !hdc) return E_PENDING;
1941
1942 if (!(analysis = heap_alloc_zero(sizeof(StringAnalysis)))) return E_OUTOFMEMORY;
1943 if (!(analysis->pItem = heap_alloc_zero(num_items * sizeof(SCRIPT_ITEM) + 1))) goto error;
1944
1945 /* FIXME: handle clipping */
1946 analysis->clip_len = cString;
1947 analysis->hdc = hdc;
1948 analysis->dwFlags = dwFlags;
1949
1950 if (psState)
1951 sState = *psState;
1952 else
1953 memset(&sState, 0, sizeof(SCRIPT_STATE));
1954
1955 if (psControl)
1956 sControl = *psControl;
1957 else
1958 memset(&sControl, 0, sizeof(SCRIPT_CONTROL));
1959
1960 if (dwFlags & SSA_PASSWORD)
1961 {
1962 iString = heap_alloc(sizeof(WCHAR)*cString);
1963 if (!iString)
1964 {
1965 hr = E_OUTOFMEMORY;
1966 goto error;
1967 }
1968 for (i = 0; i < cString; i++)
1969 iString[i] = *((const WCHAR *)pString);
1970 pString = iString;
1971 }
1972
1973 hr = ScriptItemize(pString, cString, num_items, &sControl, &sState, analysis->pItem,
1974 &analysis->numItems);
1975
1976 if (FAILED(hr))
1977 {
1978 if (hr == E_OUTOFMEMORY)
1979 hr = E_INVALIDARG;
1980 goto error;
1981 }
1982
1983 /* set back to out of memory for default goto error behaviour */
1984 hr = E_OUTOFMEMORY;
1985
1986 if (dwFlags & SSA_BREAK)
1987 {
1988 if ((analysis->logattrs = heap_alloc(sizeof(SCRIPT_LOGATTR) * cString)))
1989 {
1990 for (i = 0; i < analysis->numItems; i++)
1991 ScriptBreak(&((WCHAR *)pString)[analysis->pItem[i].iCharPos],
1992 analysis->pItem[i + 1].iCharPos - analysis->pItem[i].iCharPos,
1993 &analysis->pItem[i].a, &analysis->logattrs[analysis->pItem[i].iCharPos]);
1994 }
1995 else
1996 goto error;
1997 }
1998
1999 if (!(analysis->logical2visual = heap_alloc_zero(sizeof(int) * analysis->numItems)))
2000 goto error;
2001 if (!(BidiLevel = heap_alloc_zero(analysis->numItems)))
2002 goto error;
2003
2004 if (dwFlags & SSA_GLYPHS)
2005 {
2006 int tab_x = 0;
2007 if (!(analysis->glyphs = heap_alloc_zero(sizeof(StringGlyphs) * analysis->numItems)))
2008 {
2009 heap_free(BidiLevel);
2010 goto error;
2011 }
2012
2013 for (i = 0; i < analysis->numItems; i++)
2014 {
2015 SCRIPT_CACHE *sc = (SCRIPT_CACHE*)&analysis->glyphs[i].sc;
2016 int cChar = analysis->pItem[i+1].iCharPos - analysis->pItem[i].iCharPos;
2017 int numGlyphs = 1.5 * cChar + 16;
2018 WORD *glyphs = heap_alloc_zero(sizeof(WORD) * numGlyphs);
2019 WORD *pwLogClust = heap_alloc_zero(sizeof(WORD) * cChar);
2020 int *piAdvance = heap_alloc_zero(sizeof(int) * numGlyphs);
2021 SCRIPT_VISATTR *psva = heap_alloc_zero(sizeof(SCRIPT_VISATTR) * numGlyphs);
2022 GOFFSET *pGoffset = heap_alloc_zero(sizeof(GOFFSET) * numGlyphs);
2023 ABC *abc = heap_alloc_zero(sizeof(ABC));
2024 int numGlyphsReturned;
2025 HFONT originalFont = 0x0;
2026
2027 /* FIXME: non unicode strings */
2028 const WCHAR* pStr = (const WCHAR*)pString;
2029 analysis->glyphs[i].fallbackFont = NULL;
2030
2031 if (!glyphs || !pwLogClust || !piAdvance || !psva || !pGoffset || !abc)
2032 {
2033 heap_free (BidiLevel);
2034 heap_free (glyphs);
2035 heap_free (pwLogClust);
2036 heap_free (piAdvance);
2037 heap_free (psva);
2038 heap_free (pGoffset);
2039 heap_free (abc);
2040 hr = E_OUTOFMEMORY;
2041 goto error;
2042 }
2043
2044 if ((dwFlags & SSA_FALLBACK) && requires_fallback(hdc, sc, &analysis->pItem[i].a, &pStr[analysis->pItem[i].iCharPos], cChar))
2045 {
2046 LOGFONTW lf;
2047 GetObjectW(GetCurrentObject(hdc, OBJ_FONT), sizeof(lf), & lf);
2048 lf.lfCharSet = scriptInformation[analysis->pItem[i].a.eScript].props.bCharSet;
2049 lf.lfFaceName[0] = 0;
2050 find_fallback_font(analysis->pItem[i].a.eScript, lf.lfFaceName);
2051 if (lf.lfFaceName[0])
2052 {
2053 analysis->glyphs[i].fallbackFont = CreateFontIndirectW(&lf);
2054 if (analysis->glyphs[i].fallbackFont)
2055 {
2056 ScriptFreeCache(sc);
2057 originalFont = SelectObject(hdc, analysis->glyphs[i].fallbackFont);
2058 }
2059 }
2060 }
2061
2062 /* FIXME: When we properly shape Hangul remove this check */
2063 if ((dwFlags & SSA_LINK) && !analysis->glyphs[i].fallbackFont && analysis->pItem[i].a.eScript == Script_Hangul)
2064 analysis->pItem[i].a.fNoGlyphIndex = TRUE;
2065
2066 if ((dwFlags & SSA_LINK) && !analysis->glyphs[i].fallbackFont && !scriptInformation[analysis->pItem[i].a.eScript].props.fComplex && !analysis->pItem[i].a.fRTL)
2067 analysis->pItem[i].a.fNoGlyphIndex = TRUE;
2068
2069 ScriptShape(hdc, sc, &pStr[analysis->pItem[i].iCharPos], cChar, numGlyphs,
2070 &analysis->pItem[i].a, glyphs, pwLogClust, psva, &numGlyphsReturned);
2071 hr = ScriptPlace(hdc, sc, glyphs, numGlyphsReturned, psva, &analysis->pItem[i].a,
2072 piAdvance, pGoffset, abc);
2073 if (originalFont)
2074 SelectObject(hdc,originalFont);
2075
2076 if (dwFlags & SSA_TAB)
2077 {
2078 int tabi = 0;
2079 for (tabi = 0; tabi < cChar; tabi++)
2080 {
2081 if (pStr[analysis->pItem[i].iCharPos+tabi] == 0x0009)
2082 piAdvance[tabi] = getGivenTabWidth(analysis->glyphs[i].sc, pTabdef, analysis->pItem[i].iCharPos+tabi, tab_x);
2083 tab_x+=piAdvance[tabi];
2084 }
2085 }
2086
2087 analysis->glyphs[i].numGlyphs = numGlyphsReturned;
2088 analysis->glyphs[i].glyphs = glyphs;
2089 analysis->glyphs[i].pwLogClust = pwLogClust;
2090 analysis->glyphs[i].piAdvance = piAdvance;
2091 analysis->glyphs[i].psva = psva;
2092 analysis->glyphs[i].pGoffset = pGoffset;
2093 analysis->glyphs[i].abc = abc;
2094 analysis->glyphs[i].iMaxPosX= -1;
2095
2096 BidiLevel[i] = analysis->pItem[i].a.s.uBidiLevel;
2097 }
2098 }
2099 else
2100 {
2101 for (i = 0; i < analysis->numItems; i++)
2102 BidiLevel[i] = analysis->pItem[i].a.s.uBidiLevel;
2103 }
2104
2105 ScriptLayout(analysis->numItems, BidiLevel, NULL, analysis->logical2visual);
2106 heap_free(BidiLevel);
2107
2108 *pssa = analysis;
2109 heap_free(iString);
2110 return S_OK;
2111
2112 error:
2113 heap_free(iString);
2114 heap_free(analysis->glyphs);
2115 heap_free(analysis->logattrs);
2116 heap_free(analysis->pItem);
2117 heap_free(analysis->logical2visual);
2118 heap_free(analysis);
2119 return hr;
2120 }
2121
2122 static inline BOOL does_glyph_start_cluster(const SCRIPT_VISATTR *pva, const WORD *pwLogClust, int cChars, int glyph, int direction)
2123 {
2124 if (pva[glyph].fClusterStart)
2125 return TRUE;
2126 if (USP10_FindGlyphInLogClust(pwLogClust, cChars, glyph) >= 0)
2127 return TRUE;
2128
2129 return FALSE;
2130 }
2131
2132
2133 static HRESULT SS_ItemOut( SCRIPT_STRING_ANALYSIS ssa,
2134 int iX,
2135 int iY,
2136 int iItem,
2137 int cStart,
2138 int cEnd,
2139 UINT uOptions,
2140 const RECT *prc,
2141 BOOL fSelected,
2142 BOOL fDisabled)
2143 {
2144 StringAnalysis *analysis;
2145 int off_x = 0;
2146 HRESULT hr;
2147 COLORREF BkColor = 0x0;
2148 COLORREF TextColor = 0x0;
2149 INT BkMode = 0;
2150 INT runStart, runEnd;
2151 INT iGlyph, cGlyphs;
2152 HFONT oldFont = 0x0;
2153 RECT crc;
2154 int i;
2155
2156 TRACE("(%p,%d,%d,%d,%d,%d, 0x%1x, %d, %d)\n",
2157 ssa, iX, iY, iItem, cStart, cEnd, uOptions, fSelected, fDisabled);
2158
2159 if (!(analysis = ssa)) return E_INVALIDARG;
2160
2161 if ((cStart >= 0 && analysis->pItem[iItem+1].iCharPos <= cStart) ||
2162 (cEnd >= 0 && analysis->pItem[iItem].iCharPos >= cEnd))
2163 return S_OK;
2164
2165 CopyRect(&crc,prc);
2166 if (fSelected)
2167 {
2168 BkMode = GetBkMode(analysis->hdc);
2169 SetBkMode( analysis->hdc, OPAQUE);
2170 BkColor = GetBkColor(analysis->hdc);
2171 SetBkColor(analysis->hdc, GetSysColor(COLOR_HIGHLIGHT));
2172 if (!fDisabled)
2173 {
2174 TextColor = GetTextColor(analysis->hdc);
2175 SetTextColor(analysis->hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
2176 }
2177 }
2178 if (analysis->glyphs[iItem].fallbackFont)
2179 oldFont = SelectObject(analysis->hdc, analysis->glyphs[iItem].fallbackFont);
2180
2181 if (cStart >= 0 && analysis->pItem[iItem+1].iCharPos > cStart && analysis->pItem[iItem].iCharPos <= cStart)
2182 runStart = cStart - analysis->pItem[iItem].iCharPos;
2183 else
2184 runStart = 0;
2185 if (cEnd >= 0 && analysis->pItem[iItem+1].iCharPos > cEnd && analysis->pItem[iItem].iCharPos <= cEnd)
2186 runEnd = (cEnd-1) - analysis->pItem[iItem].iCharPos;
2187 else
2188 runEnd = (analysis->pItem[iItem+1].iCharPos - analysis->pItem[iItem].iCharPos) - 1;
2189
2190 if (analysis->pItem[iItem].a.fRTL)
2191 {
2192 if (cEnd >= 0 && cEnd < analysis->pItem[iItem+1].iCharPos)
2193 ScriptStringCPtoX(ssa, cEnd, FALSE, &off_x);
2194 else
2195 ScriptStringCPtoX(ssa, analysis->pItem[iItem+1].iCharPos-1, TRUE, &off_x);
2196 crc.left = iX + off_x;
2197 }
2198 else
2199 {
2200 if (cStart >=0 && runStart)
2201 ScriptStringCPtoX(ssa, cStart, FALSE, &off_x);
2202 else
2203 ScriptStringCPtoX(ssa, analysis->pItem[iItem].iCharPos, FALSE, &off_x);
2204 crc.left = iX + off_x;
2205 }
2206
2207 if (analysis->pItem[iItem].a.fRTL)
2208 iGlyph = analysis->glyphs[iItem].pwLogClust[runEnd];
2209 else
2210 iGlyph = analysis->glyphs[iItem].pwLogClust[runStart];
2211
2212 if (analysis->pItem[iItem].a.fRTL)
2213 cGlyphs = analysis->glyphs[iItem].pwLogClust[runStart] - iGlyph;
2214 else
2215 cGlyphs = analysis->glyphs[iItem].pwLogClust[runEnd] - iGlyph;
2216
2217 cGlyphs++;
2218
2219 /* adjust for cluster glyphs when starting */
2220 if (analysis->pItem[iItem].a.fRTL)
2221 i = analysis->pItem[iItem+1].iCharPos - 1;
2222 else
2223 i = analysis->pItem[iItem].iCharPos;
2224
2225 for (; i >=analysis->pItem[iItem].iCharPos && i < analysis->pItem[iItem+1].iCharPos; (analysis->pItem[iItem].a.fRTL)?i--:i++)
2226 {
2227 if (analysis->glyphs[iItem].pwLogClust[i - analysis->pItem[iItem].iCharPos] == iGlyph)
2228 {
2229 if (analysis->pItem[iItem].a.fRTL)
2230 ScriptStringCPtoX(ssa, i, TRUE, &off_x);
2231 else
2232 ScriptStringCPtoX(ssa, i, FALSE, &off_x);
2233 break;
2234 }
2235 }
2236
2237 if (cEnd < 0 || scriptInformation[analysis->pItem[iItem].a.eScript].props.fNeedsCaretInfo)
2238 {
2239 INT direction;
2240 INT clust_glyph;
2241
2242 clust_glyph = iGlyph + cGlyphs;
2243 if (analysis->pItem[iItem].a.fRTL)
2244 direction = -1;
2245 else
2246 direction = 1;
2247
2248 while(clust_glyph < analysis->glyphs[iItem].numGlyphs &&
2249 !does_glyph_start_cluster(analysis->glyphs[iItem].psva, analysis->glyphs[iItem].pwLogClust, (analysis->pItem[iItem+1].iCharPos - analysis->pItem[iItem].iCharPos), clust_glyph, direction))
2250 {
2251 cGlyphs++;
2252 clust_glyph++;
2253 }
2254 }
2255
2256 hr = ScriptTextOut(analysis->hdc,
2257 (SCRIPT_CACHE *)&analysis->glyphs[iItem].sc, iX + off_x,
2258 iY, uOptions, &crc, &analysis->pItem[iItem].a, NULL, 0,
2259 &analysis->glyphs[iItem].glyphs[iGlyph], cGlyphs,
2260 &analysis->glyphs[iItem].piAdvance[iGlyph], NULL,
2261 &analysis->glyphs[iItem].pGoffset[iGlyph]);
2262
2263 TRACE("ScriptTextOut hr=%08x\n", hr);
2264
2265 if (fSelected)
2266 {
2267 SetBkColor(analysis->hdc, BkColor);
2268 SetBkMode( analysis->hdc, BkMode);
2269 if (!fDisabled)
2270 SetTextColor(analysis->hdc, TextColor);
2271 }
2272 if (analysis->glyphs[iItem].fallbackFont)
2273 SelectObject(analysis->hdc, oldFont);
2274
2275 return hr;
2276 }
2277
2278 /***********************************************************************
2279 * ScriptStringOut (USP10.@)
2280 *
2281 * This function takes the output of ScriptStringAnalyse and joins the segments
2282 * of glyphs and passes the resulting string to ScriptTextOut. ScriptStringOut
2283 * only processes glyphs.
2284 *
2285 * Parameters:
2286 * ssa [I] buffer to hold the analysed string components
2287 * iX [I] X axis displacement for output
2288 * iY [I] Y axis displacement for output
2289 * uOptions [I] flags controlling output processing
2290 * prc [I] rectangle coordinates
2291 * iMinSel [I] starting pos for substringing output string
2292 * iMaxSel [I] ending pos for substringing output string
2293 * fDisabled [I] controls text highlighting
2294 *
2295 * RETURNS
2296 * Success: S_OK
2297 * Failure: is the value returned by ScriptTextOut
2298 */
2299 HRESULT WINAPI ScriptStringOut(SCRIPT_STRING_ANALYSIS ssa,
2300 int iX,
2301 int iY,
2302 UINT uOptions,
2303 const RECT *prc,
2304 int iMinSel,
2305 int iMaxSel,
2306 BOOL fDisabled)
2307 {
2308 StringAnalysis *analysis;
2309 int item;
2310 HRESULT hr;
2311
2312 TRACE("(%p,%d,%d,0x%08x,%s,%d,%d,%d)\n",
2313 ssa, iX, iY, uOptions, wine_dbgstr_rect(prc), iMinSel, iMaxSel, fDisabled);
2314
2315 if (!(analysis = ssa)) return E_INVALIDARG;
2316 if (!(analysis->dwFlags & SSA_GLYPHS)) return E_INVALIDARG;
2317
2318 for (item = 0; item < analysis->numItems; item++)
2319 {
2320 hr = SS_ItemOut( ssa, iX, iY, analysis->logical2visual[item], -1, -1, uOptions, prc, FALSE, fDisabled);
2321 if (FAILED(hr))
2322 return hr;
2323 }
2324
2325 if (iMinSel < iMaxSel && (iMinSel > 0 || iMaxSel > 0))
2326 {
2327 if (iMaxSel > 0 && iMinSel < 0)
2328 iMinSel = 0;
2329 for (item = 0; item < analysis->numItems; item++)
2330 {
2331 hr = SS_ItemOut( ssa, iX, iY, analysis->logical2visual[item], iMinSel, iMaxSel, uOptions, prc, TRUE, fDisabled);
2332 if (FAILED(hr))
2333 return hr;
2334 }
2335 }
2336
2337 return S_OK;
2338 }
2339
2340 /***********************************************************************
2341 * ScriptStringCPtoX (USP10.@)
2342 *
2343 */
2344 HRESULT WINAPI ScriptStringCPtoX(SCRIPT_STRING_ANALYSIS ssa, int icp, BOOL fTrailing, int* pX)
2345 {
2346 int item;
2347 int runningX = 0;
2348 StringAnalysis* analysis = ssa;
2349
2350 TRACE("(%p), %d, %d, (%p)\n", ssa, icp, fTrailing, pX);
2351
2352 if (!ssa || !pX) return S_FALSE;
2353 if (!(analysis->dwFlags & SSA_GLYPHS)) return S_FALSE;
2354
2355 /* icp out of range */
2356 if(icp < 0)
2357 {
2358 analysis->invalid = TRUE;
2359 return E_INVALIDARG;
2360 }
2361
2362 for(item=0; item<analysis->numItems; item++)
2363 {
2364 int CP, i;
2365 int offset;
2366
2367 i = analysis->logical2visual[item];
2368 CP = analysis->pItem[i+1].iCharPos - analysis->pItem[i].iCharPos;
2369 /* initialize max extents for uninitialized runs */
2370 if (analysis->glyphs[i].iMaxPosX == -1)
2371 {
2372 if (analysis->pItem[i].a.fRTL)
2373 ScriptCPtoX(0, FALSE, CP, analysis->glyphs[i].numGlyphs, analysis->glyphs[i].pwLogClust,
2374 analysis->glyphs[i].psva, analysis->glyphs[i].piAdvance,
2375 &analysis->pItem[i].a, &analysis->glyphs[i].iMaxPosX);
2376 else
2377 ScriptCPtoX(CP, TRUE, CP, analysis->glyphs[i].numGlyphs, analysis->glyphs[i].pwLogClust,
2378 analysis->glyphs[i].psva, analysis->glyphs[i].piAdvance,
2379 &analysis->pItem[i].a, &analysis->glyphs[i].iMaxPosX);
2380 }
2381
2382 if (icp >= analysis->pItem[i+1].iCharPos || icp < analysis->pItem[i].iCharPos)
2383 {
2384 runningX += analysis->glyphs[i].iMaxPosX;
2385 continue;
2386 }
2387
2388 icp -= analysis->pItem[i].iCharPos;
2389 ScriptCPtoX(icp, fTrailing, CP, analysis->glyphs[i].numGlyphs, analysis->glyphs[i].pwLogClust,
2390 analysis->glyphs[i].psva, analysis->glyphs[i].piAdvance,
2391 &analysis->pItem[i].a, &offset);
2392 runningX += offset;
2393
2394 *pX = runningX;
2395 return S_OK;
2396 }
2397
2398 /* icp out of range */
2399 analysis->invalid = TRUE;
2400 return E_INVALIDARG;
2401 }
2402
2403 /***********************************************************************
2404 * ScriptStringXtoCP (USP10.@)
2405 *
2406 */
2407 HRESULT WINAPI ScriptStringXtoCP(SCRIPT_STRING_ANALYSIS ssa, int iX, int* piCh, int* piTrailing)
2408 {
2409 StringAnalysis* analysis = ssa;
2410 int item;
2411
2412 TRACE("(%p), %d, (%p), (%p)\n", ssa, iX, piCh, piTrailing);
2413
2414 if (!ssa || !piCh || !piTrailing) return S_FALSE;
2415 if (!(analysis->dwFlags & SSA_GLYPHS)) return S_FALSE;
2416
2417 /* out of range */
2418 if(iX < 0)
2419 {
2420 if (analysis->pItem[0].a.fRTL)
2421 {
2422 *piCh = 1;
2423 *piTrailing = FALSE;
2424 }
2425 else
2426 {
2427 *piCh = -1;
2428 *piTrailing = TRUE;
2429 }
2430 return S_OK;
2431 }
2432
2433 for(item=0; item<analysis->numItems; item++)
2434 {
2435 int i;
2436 int CP;
2437
2438 for (i = 0; i < analysis->numItems && analysis->logical2visual[i] != item; i++)
2439 /* nothing */;
2440
2441 CP = analysis->pItem[i+1].iCharPos - analysis->pItem[i].iCharPos;
2442 /* initialize max extents for uninitialized runs */
2443 if (analysis->glyphs[i].iMaxPosX == -1)
2444 {
2445 if (analysis->pItem[i].a.fRTL)
2446 ScriptCPtoX(0, FALSE, CP, analysis->glyphs[i].numGlyphs, analysis->glyphs[i].pwLogClust,
2447 analysis->glyphs[i].psva, analysis->glyphs[i].piAdvance,
2448 &analysis->pItem[i].a, &analysis->glyphs[i].iMaxPosX);
2449 else
2450 ScriptCPtoX(CP, TRUE, CP, analysis->glyphs[i].numGlyphs, analysis->glyphs[i].pwLogClust,
2451 analysis->glyphs[i].psva, analysis->glyphs[i].piAdvance,
2452 &analysis->pItem[i].a, &analysis->glyphs[i].iMaxPosX);
2453 }
2454
2455 if (iX > analysis->glyphs[i].iMaxPosX)
2456 {
2457 iX -= analysis->glyphs[i].iMaxPosX;
2458 continue;
2459 }
2460
2461 ScriptXtoCP(iX, CP, analysis->glyphs[i].numGlyphs, analysis->glyphs[i].pwLogClust,
2462 analysis->glyphs[i].psva, analysis->glyphs[i].piAdvance,
2463 &analysis->pItem[i].a, piCh, piTrailing);
2464 *piCh += analysis->pItem[i].iCharPos;
2465
2466 return S_OK;
2467 }
2468
2469 /* out of range */
2470 *piCh = analysis->pItem[analysis->numItems].iCharPos;
2471 *piTrailing = FALSE;
2472
2473 return S_OK;
2474 }
2475
2476
2477 /***********************************************************************
2478 * ScriptStringFree (USP10.@)
2479 *
2480 * Free a string analysis.
2481 *
2482 * PARAMS
2483 * pssa [I] string analysis.
2484 *
2485 * RETURNS
2486 * Success: S_OK
2487 * Failure: Non-zero HRESULT value.
2488 */
2489 HRESULT WINAPI ScriptStringFree(SCRIPT_STRING_ANALYSIS *pssa)
2490 {
2491 StringAnalysis* analysis;
2492 BOOL invalid;
2493 int i;
2494
2495 TRACE("(%p)\n", pssa);
2496
2497 if (!pssa || !(analysis = *pssa)) return E_INVALIDARG;
2498
2499 invalid = analysis->invalid;
2500
2501 if (analysis->glyphs)
2502 {
2503 for (i = 0; i < analysis->numItems; i++)
2504 {
2505 heap_free(analysis->glyphs[i].glyphs);
2506 heap_free(analysis->glyphs[i].pwLogClust);
2507 heap_free(analysis->glyphs[i].piAdvance);
2508 heap_free(analysis->glyphs[i].psva);
2509 heap_free(analysis->glyphs[i].pGoffset);
2510 heap_free(analysis->glyphs[i].abc);
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->sz);
2522 heap_free(analysis->logical2visual);
2523 heap_free(analysis);
2524
2525 if (invalid) return E_INVALIDARG;
2526 return S_OK;
2527 }
2528
2529 static inline int get_cluster_size(const WORD *pwLogClust, int cChars, int item,
2530 int direction, int* iCluster, int *check_out)
2531 {
2532 int clust_size = 1;
2533 int check;
2534 WORD clust = pwLogClust[item];
2535
2536 for (check = item+direction; check < cChars && check >= 0; check+=direction)
2537 {
2538 if (pwLogClust[check] == clust)
2539 {
2540 clust_size ++;
2541 if (iCluster && *iCluster == -1)
2542 *iCluster = item;
2543 }
2544 else break;
2545 }
2546
2547 if (check_out)
2548 *check_out = check;
2549
2550 return clust_size;
2551 }
2552
2553 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)
2554 {
2555 int advance;
2556 int log_clust_max;
2557
2558 advance = piAdvance[glyph];
2559
2560 if (pwLogClust[0] > pwLogClust[cChars-1])
2561 log_clust_max = pwLogClust[0];
2562 else
2563 log_clust_max = pwLogClust[cChars-1];
2564
2565 if (glyph > log_clust_max)
2566 return advance;
2567
2568 for (glyph+=direction; glyph < cGlyphs && glyph >= 0; glyph +=direction)
2569 {
2570
2571 if (does_glyph_start_cluster(pva, pwLogClust, cChars, glyph, direction))
2572 break;
2573 if (glyph > log_clust_max)
2574 break;
2575 advance += piAdvance[glyph];
2576 }
2577
2578 return advance;
2579 }
2580
2581 /***********************************************************************
2582 * ScriptCPtoX (USP10.@)
2583 *
2584 */
2585 HRESULT WINAPI ScriptCPtoX(int iCP,
2586 BOOL fTrailing,
2587 int cChars,
2588 int cGlyphs,
2589 const WORD *pwLogClust,
2590 const SCRIPT_VISATTR *psva,
2591 const int *piAdvance,
2592 const SCRIPT_ANALYSIS *psa,
2593 int *piX)
2594 {
2595 int item;
2596 float iPosX;
2597 int iSpecial = -1;
2598 int iCluster = -1;
2599 int clust_size = 1;
2600 float special_size = 0.0;
2601 int iMaxPos = 0;
2602 int advance = 0;
2603 BOOL rtl = FALSE;
2604
2605 TRACE("(%d,%d,%d,%d,%p,%p,%p,%p,%p)\n",
2606 iCP, fTrailing, cChars, cGlyphs, pwLogClust, psva, piAdvance,
2607 psa, piX);
2608
2609 if (psa->fRTL && ! psa->fLogicalOrder)
2610 rtl = TRUE;
2611
2612 if (fTrailing)
2613 iCP++;
2614
2615 if (rtl)
2616 {
2617 int max_clust = pwLogClust[0];
2618
2619 for (item=0; item < cGlyphs; item++)
2620 if (pwLogClust[item] > max_clust)
2621 {
2622 ERR("We do not handle non reversed clusters properly\n");
2623 break;
2624 }
2625
2626 iMaxPos = 0;
2627 for (item = max_clust; item >=0; item --)
2628 iMaxPos += piAdvance[item];
2629 }
2630
2631 iPosX = 0.0;
2632 for (item=0; item < iCP && item < cChars; item++)
2633 {
2634 if (iSpecial == -1 && (iCluster == -1 || (iCluster != -1 && iCluster+clust_size <= item)))
2635 {
2636 int check;
2637 int clust = pwLogClust[item];
2638
2639 iCluster = -1;
2640 clust_size = get_cluster_size(pwLogClust, cChars, item, 1, &iCluster,
2641 &check);
2642
2643 advance = get_glyph_cluster_advance(piAdvance, psva, pwLogClust, cGlyphs, cChars, clust, 1);
2644
2645 if (check >= cChars && !iMaxPos)
2646 {
2647 int glyph;
2648 for (glyph = clust; glyph < cGlyphs; glyph++)
2649 special_size += get_glyph_cluster_advance(piAdvance, psva, pwLogClust, cGlyphs, cChars, glyph, 1);
2650 iSpecial = item;
2651 special_size /= (cChars - item);
2652 iPosX += special_size;
2653 }
2654 else
2655 {
2656 if (scriptInformation[psa->eScript].props.fNeedsCaretInfo)
2657 {
2658 clust_size --;
2659 if (clust_size == 0)
2660 iPosX += advance;
2661 }
2662 else
2663 iPosX += advance / (float)clust_size;
2664 }
2665 }
2666 else if (iSpecial != -1)
2667 iPosX += special_size;
2668 else /* (iCluster != -1) */
2669 {
2670 int adv = get_glyph_cluster_advance(piAdvance, psva, pwLogClust, cGlyphs, cChars, pwLogClust[iCluster], 1);
2671 if (scriptInformation[psa->eScript].props.fNeedsCaretInfo)
2672 {
2673 clust_size --;
2674 if (clust_size == 0)
2675 iPosX += adv;
2676 }
2677 else
2678 iPosX += adv / (float)clust_size;
2679 }
2680 }
2681
2682 if (iMaxPos > 0)
2683 {
2684 iPosX = iMaxPos - iPosX;
2685 if (iPosX < 0)
2686 iPosX = 0;
2687 }
2688
2689 *piX = iPosX;
2690 TRACE("*piX=%d\n", *piX);
2691 return S_OK;
2692 }
2693
2694 /* Count the number of characters in a cluster and its starting index*/
2695 static inline BOOL get_cluster_data(const WORD *pwLogClust, int cChars, int cluster_index, int *cluster_size, int *start_index)
2696 {
2697 int size = 0;
2698 int i;
2699
2700 for (i = 0; i < cChars; i++)
2701 {
2702 if (pwLogClust[i] == cluster_index)
2703 {
2704 if (!size && start_index)
2705 {
2706 *start_index = i;
2707 if (!cluster_size)
2708 return TRUE;
2709 }
2710 size++;
2711 }
2712 else if (size) break;
2713 }
2714 if (cluster_size)
2715 *cluster_size = size;
2716
2717 return (size > 0);
2718 }
2719
2720 /*
2721 To handle multi-glyph clusters we need to find all the glyphs that are
2722 represented in the cluster. This involves finding the glyph whose
2723 index is the cluster index as well as whose glyph indices are greater than
2724 our cluster index but not part of a new cluster.
2725
2726 Then we sum all those glyphs' advances.
2727 */
2728 static inline int get_cluster_advance(const int* piAdvance,
2729 const SCRIPT_VISATTR *psva,
2730 const WORD *pwLogClust, int cGlyphs,
2731 int cChars, int cluster, int direction)
2732 {
2733 int glyph_start;
2734 int glyph_end;
2735 int i, advance;
2736
2737 if (direction > 0)
2738 i = 0;
2739 else
2740 i = (cChars - 1);
2741
2742 for (glyph_start = -1, glyph_end = -1; i < cChars && i >= 0 && (glyph_start < 0 || glyph_end < 0); i+=direction)
2743 {
2744 if (glyph_start < 0 && pwLogClust[i] != cluster) continue;
2745 if (pwLogClust[i] == cluster && glyph_start < 0) glyph_start = pwLogClust[i];
2746 if (glyph_start >= 0 && glyph_end < 0 && pwLogClust[i] != cluster) glyph_end = pwLogClust[i];
2747 }
2748 if (glyph_end < 0)
2749 {
2750 if (direction > 0)
2751 glyph_end = cGlyphs;
2752 else
2753 {
2754 /* Don't fully understand multi-glyph reversed clusters yet,
2755 * do they occur for real or just in our test? */
2756 FIXME("multi-glyph reversed clusters found\n");
2757 glyph_end = glyph_start + 1;
2758 }
2759 }
2760
2761 /* Check for fClusterStart, finding this generally would mean a malformed set of data */
2762 for (i = glyph_start+1; i< glyph_end; i++)
2763 {
2764 if (psva[i].fClusterStart)
2765 {
2766 glyph_end = i;
2767 break;
2768 }
2769 }
2770
2771 for (advance = 0, i = glyph_start; i < glyph_end; i++)
2772 advance += piAdvance[i];
2773
2774 return advance;
2775 }
2776
2777
2778 /***********************************************************************
2779 * ScriptXtoCP (USP10.@)
2780 *
2781 * Basic algorithm :
2782 * Use piAdvance to find the cluster we are looking at.
2783 * Find the character that is the first character of the cluster.
2784 * That is our base piCP.
2785 * If the script snaps to cluster boundaries (Hebrew, Indic, Thai) then we
2786 * are good. Otherwise if the cluster is larger than 1 glyph we need to
2787 * determine how far through the cluster to advance the cursor.
2788 */
2789 HRESULT WINAPI ScriptXtoCP(int iX,
2790 int cChars,
2791 int cGlyphs,
2792 const WORD *pwLogClust,
2793 const SCRIPT_VISATTR *psva,
2794 const int *piAdvance,
2795 const SCRIPT_ANALYSIS *psa,
2796 int *piCP,
2797 int *piTrailing)
2798 {
2799 int direction = 1;
2800 int iPosX;
2801 int i;
2802 int glyph_index, cluster_index;
2803 int cluster_size;
2804
2805 TRACE("(%d,%d,%d,%p,%p,%p,%p,%p,%p)\n",
2806 iX, cChars, cGlyphs, pwLogClust, psva, piAdvance,
2807 psa, piCP, piTrailing);
2808
2809 if (psa->fRTL && ! psa->fLogicalOrder)
2810 direction = -1;
2811
2812 /* Handle an iX < 0 */
2813 if (iX < 0)
2814 {
2815 if (direction < 0)
2816 {
2817 *piCP = cChars;
2818 *piTrailing = 0;
2819 }
2820 else
2821 {
2822 *piCP = -1;
2823 *piTrailing = 1;
2824 }
2825 return S_OK;
2826 }
2827
2828 /* Looking for non-reversed clusters in a reversed string */
2829 if (direction < 0)
2830 {
2831 int max_clust = pwLogClust[0];
2832 for (i=0; i< cChars; i++)
2833 if (pwLogClust[i] > max_clust)
2834 {
2835 FIXME("We do not handle non reversed clusters properly\n");
2836 break;
2837 }
2838 }
2839
2840 /* find the glyph_index based in iX */
2841 if (direction > 0)
2842 {
2843 for (glyph_index = -1, iPosX = iX; iPosX >=0 && glyph_index < cGlyphs; iPosX -= piAdvance[glyph_index+1], glyph_index++)
2844 ;
2845 }
2846 else
2847 {
2848 for (glyph_index = -1, iPosX = iX; iPosX > 0 && glyph_index < cGlyphs; iPosX -= piAdvance[glyph_index+1], glyph_index++)
2849 ;
2850 }
2851
2852 TRACE("iPosX %i -> glyph_index %i (%i)\n", iPosX, glyph_index, cGlyphs);
2853
2854 *piTrailing = 0;
2855 if (glyph_index >= 0 && glyph_index < cGlyphs)
2856 {
2857 /* find the cluster */
2858 if (direction > 0 )
2859 for (i = 0, cluster_index = pwLogClust[0]; i < cChars && pwLogClust[i] <= glyph_index; cluster_index=pwLogClust[i++])
2860 ;
2861 else
2862 for (i = 0, cluster_index = pwLogClust[0]; i < cChars && pwLogClust[i] >= glyph_index; cluster_index=pwLogClust[i++])
2863 ;
2864
2865 TRACE("cluster_index %i\n", cluster_index);
2866
2867 if (direction < 0 && iPosX >= 0 && glyph_index != cluster_index)
2868 {
2869 /* We are off the end of the string */
2870 *piCP = -1;
2871 *piTrailing = 1;
2872 return S_OK;
2873 }
2874
2875 get_cluster_data(pwLogClust, cChars, cluster_index, &cluster_size, &i);
2876
2877 TRACE("first char index %i\n",i);
2878 if (scriptInformation[psa->eScript].props.fNeedsCaretInfo)
2879 {
2880 /* Check trailing */
2881 if (glyph_index != cluster_index ||
2882 (direction > 0 && abs(iPosX) <= (piAdvance[glyph_index] / 2)) ||
2883 (direction < 0 && abs(iPosX) >= (piAdvance[glyph_index] / 2)))
2884 *piTrailing = cluster_size;
2885 }
2886 else
2887 {
2888 if (cluster_size > 1)
2889 {
2890 /* Be part way through the glyph cluster based on size and position */
2891 int cluster_advance = get_cluster_advance(piAdvance, psva, pwLogClust, cGlyphs, cChars, cluster_index, direction);
2892 double cluster_part_width = cluster_advance / (float)cluster_size;
2893 double adv;
2894 int part_index;
2895
2896 /* back up to the beginning of the cluster */
2897 for (adv = iPosX, part_index = cluster_index; part_index <= glyph_index; part_index++)
2898 adv += piAdvance[part_index];
2899 if (adv > iX) adv = iX;
2900
2901 TRACE("Multi-char cluster, no snap\n");
2902 TRACE("cluster size %i, pre-cluster iPosX %f\n",cluster_size, adv);
2903 TRACE("advance %i divides into %f per char\n", cluster_advance, cluster_part_width);
2904 if (direction > 0)
2905 {
2906 for (part_index = 0; adv >= 0; adv-=cluster_part_width, part_index++)
2907 ;
2908 if (part_index) part_index--;
2909 }
2910 else
2911 {
2912 for (part_index = 0; adv > 0; adv-=cluster_part_width, part_index++)
2913 ;
2914 if (part_index > cluster_size)
2915 {
2916 adv += cluster_part_width;
2917 part_index=cluster_size;
2918 }
2919 }
2920
2921 TRACE("base_char %i part_index %i, leftover advance %f\n",i, part_index, adv);
2922
2923 if (direction > 0)
2924 i += part_index;
2925 else
2926 i += (cluster_size - part_index);
2927
2928 /* Check trailing */
2929 if ((direction > 0 && fabs(adv) <= (cluster_part_width / 2.0)) ||
2930 (direction < 0 && adv && fabs(adv) >= (cluster_part_width / 2.0)))
2931 *piTrailing = 1;
2932 }
2933 else
2934 {
2935 /* Check trailing */
2936 if ((direction > 0 && abs(iPosX) <= (piAdvance[glyph_index] / 2)) ||
2937 (direction < 0 && abs(iPosX) >= (piAdvance[glyph_index] / 2)))
2938 *piTrailing = 1;
2939 }
2940 }
2941 }
2942 else
2943 {
2944 TRACE("Point falls outside of string\n");
2945 if (glyph_index < 0)
2946 i = cChars-1;
2947 else /* (glyph_index >= cGlyphs) */
2948 i = cChars;
2949
2950 /* If not snaping in the reverse direction (such as Hebrew) Then 0
2951 point flow to the next character */
2952 if (direction < 0)
2953 {
2954 if (!scriptInformation[psa->eScript].props.fNeedsCaretInfo && abs(iPosX) == piAdvance[glyph_index])
2955 i++;
2956 else
2957 *piTrailing = 1;
2958 }
2959 }
2960
2961 *piCP = i;
2962
2963 TRACE("*piCP=%d\n", *piCP);
2964 TRACE("*piTrailing=%d\n", *piTrailing);
2965 return S_OK;
2966 }
2967
2968 /***********************************************************************
2969 * ScriptBreak (USP10.@)
2970 *
2971 * Retrieve line break information.
2972 *
2973 * PARAMS
2974 * chars [I] Array of characters.
2975 * sa [I] Script analysis.
2976 * la [I] Array of logical attribute structures.
2977 *
2978 * RETURNS
2979 * Success: S_OK
2980 * Failure: S_FALSE
2981 */
2982 HRESULT WINAPI ScriptBreak(const WCHAR *chars, int count, const SCRIPT_ANALYSIS *sa, SCRIPT_LOGATTR *la)
2983 {
2984 TRACE("(%s, %d, %p, %p)\n", debugstr_wn(chars, count), count, sa, la);
2985
2986 if (count < 0 || !la) return E_INVALIDARG;
2987 if (count == 0) return E_FAIL;
2988
2989 BREAK_line(chars, count, sa, la);
2990
2991 return S_OK;
2992 }
2993
2994 /***********************************************************************
2995 * ScriptIsComplex (USP10.@)
2996 *
2997 * Determine if a string is complex.
2998 *
2999 * PARAMS
3000 * chars [I] Array of characters to test.
3001 * len [I] Length in characters.
3002 * flag [I] Flag.
3003 *
3004 * RETURNS
3005 * Success: S_OK
3006 * Failure: S_FALSE
3007 *
3008 */
3009 HRESULT WINAPI ScriptIsComplex(const WCHAR *chars, int len, DWORD flag)
3010 {
3011 enum usp10_script script;
3012 unsigned int i, consumed;
3013
3014 TRACE("(%s,%d,0x%x)\n", debugstr_wn(chars, len), len, flag);
3015
3016 if (!chars || len < 0)
3017 return E_INVALIDARG;
3018
3019 for (i = 0; i < len; i+=consumed)
3020 {
3021 if (i >= len)
3022 break;
3023
3024 if ((flag & SIC_ASCIIDIGIT) && chars[i] >= 0x30 && chars[i] <= 0x39)
3025 return S_OK;
3026
3027 script = get_char_script(chars,i,len, &consumed);
3028 if ((scriptInformation[script].props.fComplex && (flag & SIC_COMPLEX))||
3029 (!scriptInformation[script].props.fComplex && (flag & SIC_NEUTRAL)))
3030 return S_OK;
3031 }
3032 return S_FALSE;
3033 }
3034
3035 /***********************************************************************
3036 * ScriptShapeOpenType (USP10.@)
3037 *
3038 * Produce glyphs and visual attributes for a run.
3039 *
3040 * PARAMS
3041 * hdc [I] Device context.
3042 * psc [I/O] Opaque pointer to a script cache.
3043 * psa [I/O] Script analysis.
3044 * tagScript [I] The OpenType tag for the Script
3045 * tagLangSys [I] The OpenType tag for the Language
3046 * rcRangeChars[I] Array of Character counts in each range
3047 * rpRangeProperties [I] Array of TEXTRANGE_PROPERTIES structures
3048 * cRanges [I] Count of ranges
3049 * pwcChars [I] Array of characters specifying the run.
3050 * cChars [I] Number of characters in pwcChars.
3051 * cMaxGlyphs [I] Length of pwOutGlyphs.
3052 * pwLogClust [O] Array of logical cluster info.
3053 * pCharProps [O] Array of character property values
3054 * pwOutGlyphs [O] Array of glyphs.
3055 * pOutGlyphProps [O] Array of attributes for the retrieved glyphs
3056 * pcGlyphs [O] Number of glyphs returned.
3057 *
3058 * RETURNS
3059 * Success: S_OK
3060 * Failure: Non-zero HRESULT value.
3061 */
3062 HRESULT WINAPI ScriptShapeOpenType( HDC hdc, SCRIPT_CACHE *psc,
3063 SCRIPT_ANALYSIS *psa, OPENTYPE_TAG tagScript,
3064 OPENTYPE_TAG tagLangSys, int *rcRangeChars,
3065 TEXTRANGE_PROPERTIES **rpRangeProperties,
3066 int cRanges, const WCHAR *pwcChars, int cChars,
3067 int cMaxGlyphs, WORD *pwLogClust,
3068 SCRIPT_CHARPROP *pCharProps, WORD *pwOutGlyphs,
3069 SCRIPT_GLYPHPROP *pOutGlyphProps, int *pcGlyphs)
3070 {
3071 HRESULT hr;
3072 int i;
3073 unsigned int g;
3074 BOOL rtl;
3075 int cluster;
3076 static int once = 0;
3077
3078 TRACE("(%p, %p, %p, %s, %s, %p, %p, %d, %s, %d, %d, %p, %p, %p, %p, %p )\n",
3079 hdc, psc, psa,
3080 debugstr_an((char*)&tagScript,4), debugstr_an((char*)&tagLangSys,4),
3081 rcRangeChars, rpRangeProperties, cRanges, debugstr_wn(pwcChars, cChars),
3082 cChars, cMaxGlyphs, pwLogClust, pCharProps, pwOutGlyphs, pOutGlyphProps, pcGlyphs);
3083
3084 if (psa) TRACE("psa values: %d, %d, %d, %d, %d, %d, %d\n", psa->eScript, psa->fRTL, psa->fLayoutRTL,
3085 psa->fLinkBefore, psa->fLinkAfter, psa->fLogicalOrder, psa->fNoGlyphIndex);
3086
3087 if (!pOutGlyphProps || !pcGlyphs || !pCharProps) return E_INVALIDARG;
3088 if (cChars > cMaxGlyphs) return E_OUTOFMEMORY;
3089
3090 if (cRanges)
3091 if(!once++) FIXME("Ranges not supported yet\n");
3092
3093 rtl = (psa && !psa->fLogicalOrder && psa->fRTL);
3094
3095 *pcGlyphs = cChars;
3096 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
3097 if (!pwLogClust) return E_FAIL;
3098
3099 ((ScriptCache *)*psc)->userScript = tagScript;
3100 ((ScriptCache *)*psc)->userLang = tagLangSys;
3101
3102 /* Initialize a SCRIPT_VISATTR and LogClust for each char in this run */
3103 for (i = 0; i < cChars; i++)
3104 {
3105 int idx = i;
3106 if (rtl) idx = cChars - 1 - i;
3107 /* FIXME: set to better values */
3108 pOutGlyphProps[i].sva.uJustification = (pwcChars[idx] == ' ') ? SCRIPT_JUSTIFY_BLANK : SCRIPT_JUSTIFY_CHARACTER;
3109 pOutGlyphProps[i].sva.fClusterStart = 1;
3110 pOutGlyphProps[i].sva.fDiacritic = 0;
3111 pOutGlyphProps[i].sva.fZeroWidth = 0;
3112 pOutGlyphProps[i].sva.fReserved = 0;
3113 pOutGlyphProps[i].sva.fShapeReserved = 0;
3114
3115 /* FIXME: have the shaping engine set this */
3116 pCharProps[i].fCanGlyphAlone = 0;
3117
3118 pwLogClust[i] = idx;
3119 }
3120
3121 if (psa && !psa->fNoGlyphIndex && ((ScriptCache *)*psc)->sfnt)
3122 {
3123 WCHAR *rChars;
3124 if ((hr = SHAPE_CheckFontForRequiredFeatures(hdc, (ScriptCache *)*psc, psa)) != S_OK) return hr;
3125
3126 rChars = heap_alloc(sizeof(WCHAR) * cChars);
3127 if (!rChars) return E_OUTOFMEMORY;
3128 for (i = 0, g = 0, cluster = 0; i < cChars; i++)
3129 {
3130 int idx = i;
3131 DWORD chInput;
3132
3133 if (rtl) idx = cChars - 1 - i;
3134 if (!cluster)
3135 {
3136 chInput = decode_surrogate_pair(pwcChars, idx, cChars);
3137 if (!chInput)
3138 {
3139 if (psa->fRTL)
3140 chInput = mirror_char(pwcChars[idx]);
3141 else
3142 chInput = pwcChars[idx];
3143 rChars[i] = chInput;
3144 }
3145 else
3146 {
3147 rChars[i] = pwcChars[idx];
3148 rChars[i+1] = pwcChars[(rtl)?idx-1:idx+1];
3149 cluster = 1;
3150 }
3151 if (!(pwOutGlyphs[g] = get_cache_glyph(psc, chInput)))
3152 {
3153 WORD glyph;
3154 if (!hdc)
3155 {
3156 heap_free(rChars);
3157 return E_PENDING;
3158 }
3159 if (OpenType_CMAP_GetGlyphIndex(hdc, (ScriptCache *)*psc, chInput, &glyph, 0) == GDI_ERROR)
3160 {
3161 heap_free(rChars);
3162 return S_FALSE;
3163 }
3164 pwOutGlyphs[g] = set_cache_glyph(psc, chInput, glyph);
3165 }
3166 g++;
3167 }
3168 else
3169 {
3170 int k;
3171 cluster--;
3172 pwLogClust[idx] = (rtl)?pwLogClust[idx+1]:pwLogClust[idx-1];
3173 for (k = (rtl)?idx-1:idx+1; k >= 0 && k < cChars; (rtl)?k--:k++)
3174 pwLogClust[k]--;
3175 }
3176 }
3177 *pcGlyphs = g;
3178
3179 SHAPE_ContextualShaping(hdc, (ScriptCache *)*psc, psa, rChars, cChars, pwOutGlyphs, pcGlyphs, cMaxGlyphs, pwLogClust);
3180 SHAPE_ApplyDefaultOpentypeFeatures(hdc, (ScriptCache *)*psc, psa, pwOutGlyphs, pcGlyphs, cMaxGlyphs, cChars, pwLogClust);
3181 SHAPE_CharGlyphProp(hdc, (ScriptCache *)*psc, psa, pwcChars, cChars, pwOutGlyphs, *pcGlyphs, pwLogClust, pCharProps, pOutGlyphProps);
3182
3183 for (i = 0; i < cChars; ++i)
3184 {
3185 /* Special case for tabs and joiners. As control characters, ZWNJ
3186 * and ZWJ would in principle get handled by the corresponding
3187 * shaping functions. However, since ZWNJ and ZWJ can get merged
3188 * into adjoining runs during itemisation, these don't generally
3189 * get classified as Script_Control. */
3190 if (pwcChars[i] == 0x0009 || pwcChars[i] == ZWSP || pwcChars[i] == ZWNJ || pwcChars[i] == ZWJ)
3191 {
3192 pwOutGlyphs[pwLogClust[i]] = ((ScriptCache *)*psc)->sfp.wgBlank;
3193 pOutGlyphProps[pwLogClust[i]].sva.fZeroWidth = 1;
3194 }
3195 }
3196 heap_free(rChars);
3197 }
3198 else
3199 {
3200 TRACE("no glyph translation\n");
3201 for (i = 0; i < cChars; i++)
3202 {
3203 int idx = i;
3204 /* No mirroring done here */
3205 if (rtl) idx = cChars - 1 - i;
3206 pwOutGlyphs[i] = pwcChars[idx];
3207
3208 if (!psa)
3209 continue;
3210
3211 /* overwrite some basic control glyphs to blank */
3212 if (psa->fNoGlyphIndex)
3213 {
3214 if (pwcChars[idx] == ZWSP || pwcChars[idx] == ZWNJ || pwcChars[idx] == ZWJ)
3215 {
3216 pwOutGlyphs[i] = 0x20;
3217 pOutGlyphProps[i].sva.fZeroWidth = 1;
3218 }
3219 }
3220 else if (psa->eScript == Script_Control || pwcChars[idx] == ZWSP
3221 || pwcChars[idx] == ZWNJ || pwcChars[idx] == ZWJ)
3222 {
3223 if (pwcChars[idx] == 0x0009 || pwcChars[idx] == 0x000A ||
3224 pwcChars[idx] == 0x000D || pwcChars[idx] >= 0x001C)
3225 {
3226 pwOutGlyphs[i] = ((ScriptCache *)*psc)->sfp.wgBlank;
3227 pOutGlyphProps[i].sva.fZeroWidth = 1;
3228 }
3229 }
3230 }
3231 }
3232
3233 return S_OK;
3234 }
3235
3236
3237 /***********************************************************************
3238 * ScriptShape (USP10.@)
3239 *
3240 * Produce glyphs and visual attributes for a run.
3241 *
3242 * PARAMS
3243 * hdc [I] Device context.
3244 * psc [I/O] Opaque pointer to a script cache.
3245 * pwcChars [I] Array of characters specifying the run.
3246 * cChars [I] Number of characters in pwcChars.
3247 * cMaxGlyphs [I] Length of pwOutGlyphs.
3248 * psa [I/O] Script analysis.
3249 * pwOutGlyphs [O] Array of glyphs.
3250 * pwLogClust [O] Array of logical cluster info.
3251 * psva [O] Array of visual attributes.
3252 * pcGlyphs [O] Number of glyphs returned.
3253 *
3254 * RETURNS
3255 * Success: S_OK
3256 * Failure: Non-zero HRESULT value.
3257 */
3258 HRESULT WINAPI ScriptShape(HDC hdc, SCRIPT_CACHE *psc, const WCHAR *pwcChars,
3259 int cChars, int cMaxGlyphs,
3260 SCRIPT_ANALYSIS *psa, WORD *pwOutGlyphs, WORD *pwLogClust,
3261 SCRIPT_VISATTR *psva, int *pcGlyphs)
3262 {
3263 HRESULT hr;
3264 int i;
3265 SCRIPT_CHARPROP *charProps;
3266 SCRIPT_GLYPHPROP *glyphProps;
3267
3268 if (!psva || !pcGlyphs) return E_INVALIDARG;
3269 if (cChars > cMaxGlyphs) return E_OUTOFMEMORY;
3270
3271 charProps = heap_alloc_zero(sizeof(SCRIPT_CHARPROP)*cChars);
3272 if (!charProps) return E_OUTOFMEMORY;
3273 glyphProps = heap_alloc_zero(sizeof(SCRIPT_GLYPHPROP)*cMaxGlyphs);
3274 if (!glyphProps)
3275 {
3276 heap_free(charProps);
3277 return E_OUTOFMEMORY;
3278 }
3279
3280 hr = ScriptShapeOpenType(hdc, psc, psa, scriptInformation[psa->eScript].scriptTag, 0, NULL, NULL, 0, pwcChars, cChars, cMaxGlyphs, pwLogClust, charProps, pwOutGlyphs, glyphProps, pcGlyphs);
3281
3282 if (SUCCEEDED(hr))
3283 {
3284 for (i = 0; i < *pcGlyphs; i++)
3285 psva[i] = glyphProps[i].sva;
3286 }
3287
3288 heap_free(charProps);
3289 heap_free(glyphProps);
3290
3291 return hr;
3292 }
3293
3294 /***********************************************************************
3295 * ScriptPlaceOpenType (USP10.@)
3296 *
3297 * Produce advance widths for a run.
3298 *
3299 * PARAMS
3300 * hdc [I] Device context.
3301 * psc [I/O] Opaque pointer to a script cache.
3302 * psa [I/O] Script analysis.
3303 * tagScript [I] The OpenType tag for the Script
3304 * tagLangSys [I] The OpenType tag for the Language
3305 * rcRangeChars[I] Array of Character counts in each range
3306 * rpRangeProperties [I] Array of TEXTRANGE_PROPERTIES structures
3307 * cRanges [I] Count of ranges
3308 * pwcChars [I] Array of characters specifying the run.
3309 * pwLogClust [I] Array of logical cluster info
3310 * pCharProps [I] Array of character property values
3311 * cChars [I] Number of characters in pwcChars.
3312 * pwGlyphs [I] Array of glyphs.
3313 * pGlyphProps [I] Array of attributes for the retrieved glyphs
3314 * cGlyphs [I] Count of Glyphs
3315 * piAdvance [O] Array of advance widths.
3316 * pGoffset [O] Glyph offsets.
3317 * pABC [O] Combined ABC width.
3318 *
3319 * RETURNS
3320 * Success: S_OK
3321 * Failure: Non-zero HRESULT value.
3322 */
3323
3324 HRESULT WINAPI ScriptPlaceOpenType( HDC hdc, SCRIPT_CACHE *psc, SCRIPT_ANALYSIS *psa,
3325 OPENTYPE_TAG tagScript, OPENTYPE_TAG tagLangSys,
3326 int *rcRangeChars, TEXTRANGE_PROPERTIES **rpRangeProperties,
3327 int cRanges, const WCHAR *pwcChars, WORD *pwLogClust,
3328 SCRIPT_CHARPROP *pCharProps, int cChars,
3329 const WORD *pwGlyphs, const SCRIPT_GLYPHPROP *pGlyphProps,
3330 int cGlyphs, int *piAdvance,
3331 GOFFSET *pGoffset, ABC *pABC
3332 )
3333 {
3334 HRESULT hr;
3335 int i;
3336 static int once = 0;
3337
3338 TRACE("(%p, %p, %p, %s, %s, %p, %p, %d, %s, %p, %p, %d, %p, %p, %d, %p %p %p)\n",
3339 hdc, psc, psa,
3340 debugstr_an((char*)&tagScript,4), debugstr_an((char*)&tagLangSys,4),
3341 rcRangeChars, rpRangeProperties, cRanges, debugstr_wn(pwcChars, cChars),
3342 pwLogClust, pCharProps, cChars, pwGlyphs, pGlyphProps, cGlyphs, piAdvance,
3343 pGoffset, pABC);
3344
3345 if (!pGlyphProps) return E_INVALIDARG;
3346 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
3347 if (!pGoffset) return E_FAIL;
3348
3349 if (cRanges)
3350 if (!once++) FIXME("Ranges not supported yet\n");
3351
3352 ((ScriptCache *)*psc)->userScript = tagScript;
3353 ((ScriptCache *)*psc)->userLang = tagLangSys;
3354
3355 if (pABC) memset(pABC, 0, sizeof(ABC));
3356 for (i = 0; i < cGlyphs; i++)
3357 {
3358 ABC abc;
3359 if (pGlyphProps[i].sva.fZeroWidth)
3360 {
3361 abc.abcA = abc.abcB = abc.abcC = 0;
3362 }
3363 else if (!get_cache_glyph_widths(psc, pwGlyphs[i], &abc))
3364 {
3365 BOOL ret;
3366 if (!hdc) return E_PENDING;
3367 if (get_cache_pitch_family(psc) & TMPF_TRUETYPE)
3368 {
3369 if (psa->fNoGlyphIndex)
3370 ret = GetCharABCWidthsW(hdc, pwGlyphs[i], pwGlyphs[i], &abc);
3371 else
3372 ret = GetCharABCWidthsI(hdc, 0, 1, (WORD *)&pwGlyphs[i], &abc);
3373 if (!ret) return S_FALSE;
3374 }
3375 else
3376 {
3377 INT width;
3378 if (psa->fNoGlyphIndex)
3379 ret = GetCharWidth32W(hdc, pwGlyphs[i], pwGlyphs[i], &width);
3380 else
3381 ret = GetCharWidthI(hdc, 0, 1, (WORD *)&pwGlyphs[i], &width);
3382 if (!ret) return S_FALSE;
3383 abc.abcB = width;
3384 abc.abcA = abc.abcC = 0;
3385 }
3386 set_cache_glyph_widths(psc, pwGlyphs[i], &abc);
3387 }
3388 if (pABC)
3389 {
3390 pABC->abcA += abc.abcA;
3391 pABC->abcB += abc.abcB;
3392 pABC->abcC += abc.abcC;
3393 }
3394 /* FIXME: set to more reasonable values */
3395 pGoffset[i].du = pGoffset[i].dv = 0;
3396 if (piAdvance) piAdvance[i] = abc.abcA + abc.abcB + abc.abcC;
3397 }
3398
3399 SHAPE_ApplyOpenTypePositions(hdc, (ScriptCache *)*psc, psa, pwGlyphs, cGlyphs, piAdvance, pGoffset);
3400
3401 if (pABC) TRACE("Total for run: abcA=%d, abcB=%d, abcC=%d\n", pABC->abcA, pABC->abcB, pABC->abcC);
3402 return S_OK;
3403 }
3404
3405 /***********************************************************************
3406 * ScriptPlace (USP10.@)
3407 *
3408 * Produce advance widths for a run.
3409 *
3410 * PARAMS
3411 * hdc [I] Device context.
3412 * psc [I/O] Opaque pointer to a script cache.
3413 * pwGlyphs [I] Array of glyphs.
3414 * cGlyphs [I] Number of glyphs in pwGlyphs.
3415 * psva [I] Array of visual attributes.
3416 * psa [I/O] String analysis.
3417 * piAdvance [O] Array of advance widths.
3418 * pGoffset [O] Glyph offsets.
3419 * pABC [O] Combined ABC width.
3420 *
3421 * RETURNS
3422 * Success: S_OK
3423 * Failure: Non-zero HRESULT value.
3424 */
3425 HRESULT WINAPI ScriptPlace(HDC hdc, SCRIPT_CACHE *psc, const WORD *pwGlyphs,
3426 int cGlyphs, const SCRIPT_VISATTR *psva,
3427 SCRIPT_ANALYSIS *psa, int *piAdvance, GOFFSET *pGoffset, ABC *pABC )
3428 {
3429 HRESULT hr;
3430 SCRIPT_GLYPHPROP *glyphProps;
3431 int i;
3432
3433 TRACE("(%p, %p, %p, %d, %p, %p, %p, %p, %p)\n", hdc, psc, pwGlyphs, cGlyphs, psva, psa,
3434 piAdvance, pGoffset, pABC);
3435
3436 if (!psva) return E_INVALIDARG;
3437 if (!pGoffset) return E_FAIL;
3438
3439 glyphProps = heap_alloc(sizeof(SCRIPT_GLYPHPROP)*cGlyphs);
3440 if (!glyphProps) return E_OUTOFMEMORY;
3441
3442 for (i = 0; i < cGlyphs; i++)
3443 glyphProps[i].sva = psva[i];
3444
3445 hr = ScriptPlaceOpenType(hdc, psc, psa, scriptInformation[psa->eScript].scriptTag, 0, NULL, NULL, 0, NULL, NULL, NULL, 0, pwGlyphs, glyphProps, cGlyphs, piAdvance, pGoffset, pABC);
3446
3447 heap_free(glyphProps);
3448
3449 return hr;
3450 }
3451
3452 /***********************************************************************
3453 * ScriptGetCMap (USP10.@)
3454 *
3455 * Retrieve glyph indices.
3456 *
3457 * PARAMS
3458 * hdc [I] Device context.
3459 * psc [I/O] Opaque pointer to a script cache.
3460 * pwcInChars [I] Array of Unicode characters.
3461 * cChars [I] Number of characters in pwcInChars.
3462 * dwFlags [I] Flags.
3463 * pwOutGlyphs [O] Buffer to receive the array of glyph indices.
3464 *
3465 * RETURNS
3466 * Success: S_OK
3467 * Failure: Non-zero HRESULT value.
3468 */
3469 HRESULT WINAPI ScriptGetCMap(HDC hdc, SCRIPT_CACHE *psc, const WCHAR *pwcInChars,
3470 int cChars, DWORD dwFlags, WORD *pwOutGlyphs)
3471 {
3472 HRESULT hr;
3473 int i;
3474
3475 TRACE("(%p,%p,%s,%d,0x%x,%p)\n", hdc, psc, debugstr_wn(pwcInChars, cChars),
3476 cChars, dwFlags, pwOutGlyphs);
3477
3478 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
3479
3480 hr = S_OK;
3481
3482 if ((get_cache_pitch_family(psc) & TMPF_TRUETYPE))
3483 {
3484 for (i = 0; i < cChars; i++)
3485 {
3486 WCHAR inChar;
3487 if (dwFlags == SGCM_RTL)
3488 inChar = mirror_char(pwcInChars[i]);
3489 else
3490 inChar = pwcInChars[i];
3491 if (!(pwOutGlyphs[i] = get_cache_glyph(psc, inChar)))
3492 {
3493 WORD glyph;
3494 if (!hdc) return E_PENDING;
3495 if (GetGlyphIndicesW(hdc, &inChar, 1, &glyph, GGI_MARK_NONEXISTING_GLYPHS) == GDI_ERROR) return S_FALSE;
3496 if (glyph == 0xffff)
3497 {
3498 hr = S_FALSE;
3499 glyph = 0x0;
3500 }
3501 pwOutGlyphs[i] = set_cache_glyph(psc, inChar, glyph);
3502 }
3503 }
3504 }
3505 else
3506 {
3507 TRACE("no glyph translation\n");
3508 for (i = 0; i < cChars; i++)
3509 {
3510 WCHAR inChar;
3511 if (dwFlags == SGCM_RTL)
3512 inChar = mirror_char(pwcInChars[i]);
3513 else
3514 inChar = pwcInChars[i];
3515 pwOutGlyphs[i] = inChar;
3516 }
3517 }
3518 return hr;
3519 }
3520
3521 /***********************************************************************
3522 * ScriptTextOut (USP10.@)
3523 *
3524 */
3525 HRESULT WINAPI ScriptTextOut(const HDC hdc, SCRIPT_CACHE *psc, int x, int y, UINT fuOptions,
3526 const RECT *lprc, const SCRIPT_ANALYSIS *psa, const WCHAR *pwcReserved,
3527 int iReserved, const WORD *pwGlyphs, int cGlyphs, const int *piAdvance,
3528 const int *piJustify, const GOFFSET *pGoffset)
3529 {
3530 HRESULT hr = S_OK;
3531 INT i, dir = 1;
3532 INT *lpDx;
3533 WORD *reordered_glyphs = (WORD *)pwGlyphs;
3534
3535 TRACE("(%p, %p, %d, %d, %08x, %s, %p, %p, %d, %p, %d, %p, %p, %p)\n",
3536 hdc, psc, x, y, fuOptions, wine_dbgstr_rect(lprc), psa, pwcReserved, iReserved, pwGlyphs, cGlyphs,
3537 piAdvance, piJustify, pGoffset);
3538
3539 if (!hdc || !psc) return E_INVALIDARG;
3540 if (!piAdvance || !psa || !pwGlyphs) return E_INVALIDARG;
3541
3542 fuOptions &= ETO_CLIPPED + ETO_OPAQUE;
3543 fuOptions |= ETO_IGNORELANGUAGE;
3544 if (!psa->fNoGlyphIndex) /* Have Glyphs? */
3545 fuOptions |= ETO_GLYPH_INDEX; /* Say don't do translation to glyph */
3546
3547 lpDx = heap_alloc(cGlyphs * sizeof(INT) * 2);
3548 if (!lpDx) return E_OUTOFMEMORY;
3549 fuOptions |= ETO_PDY;
3550
3551 if (psa->fRTL && psa->fLogicalOrder)
3552 {
3553 reordered_glyphs = heap_alloc( cGlyphs * sizeof(WORD) );
3554 if (!reordered_glyphs)
3555 {
3556 heap_free( lpDx );
3557 return E_OUTOFMEMORY;
3558 }
3559
3560 for (i = 0; i < cGlyphs; i++)
3561 reordered_glyphs[i] = pwGlyphs[cGlyphs - 1 - i];
3562 dir = -1;
3563 }
3564
3565 for (i = 0; i < cGlyphs; i++)
3566 {
3567 int orig_index = (dir > 0) ? i : cGlyphs - 1 - i;
3568 lpDx[i * 2] = piAdvance[orig_index];
3569 lpDx[i * 2 + 1] = 0;
3570
3571 if (pGoffset)
3572 {
3573 if (i == 0)
3574 {
3575 x += pGoffset[orig_index].du * dir;
3576 y += pGoffset[orig_index].dv;
3577 }
3578 else
3579 {
3580 lpDx[(i - 1) * 2] += pGoffset[orig_index].du * dir;
3581 lpDx[(i - 1) * 2 + 1] += pGoffset[orig_index].dv;
3582 }
3583 lpDx[i * 2] -= pGoffset[orig_index].du * dir;
3584 lpDx[i * 2 + 1] -= pGoffset[orig_index].dv;
3585 }
3586 }
3587
3588 if (!ExtTextOutW(hdc, x, y, fuOptions, lprc, reordered_glyphs, cGlyphs, lpDx))
3589 hr = S_FALSE;
3590
3591 if (reordered_glyphs != pwGlyphs) heap_free( reordered_glyphs );
3592 heap_free(lpDx);
3593
3594 return hr;
3595 }
3596
3597 /***********************************************************************
3598 * ScriptCacheGetHeight (USP10.@)
3599 *
3600 * Retrieve the height of the font in the cache.
3601 *
3602 * PARAMS
3603 * hdc [I] Device context.
3604 * psc [I/O] Opaque pointer to a script cache.
3605 * height [O] Receives font height.
3606 *
3607 * RETURNS
3608 * Success: S_OK
3609 * Failure: Non-zero HRESULT value.
3610 */
3611 HRESULT WINAPI ScriptCacheGetHeight(HDC hdc, SCRIPT_CACHE *psc, LONG *height)
3612 {
3613 HRESULT hr;
3614
3615 TRACE("(%p, %p, %p)\n", hdc, psc, height);
3616
3617 if (!height) return E_INVALIDARG;
3618 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
3619
3620 *height = get_cache_height(psc);
3621 return S_OK;
3622 }
3623
3624 /***********************************************************************
3625 * ScriptGetGlyphABCWidth (USP10.@)
3626 *
3627 * Retrieve the width of a glyph.
3628 *
3629 * PARAMS
3630 * hdc [I] Device context.
3631 * psc [I/O] Opaque pointer to a script cache.
3632 * glyph [I] Glyph to retrieve the width for.
3633 * abc [O] ABC widths of the glyph.
3634 *
3635 * RETURNS
3636 * Success: S_OK
3637 * Failure: Non-zero HRESULT value.
3638 */
3639 HRESULT WINAPI ScriptGetGlyphABCWidth(HDC hdc, SCRIPT_CACHE *psc, WORD glyph, ABC *abc)
3640 {
3641 HRESULT hr;
3642
3643 TRACE("(%p, %p, 0x%04x, %p)\n", hdc, psc, glyph, abc);
3644
3645 if (!abc) return E_INVALIDARG;
3646 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
3647
3648 if (!get_cache_glyph_widths(psc, glyph, abc))
3649 {
3650 if (!hdc) return E_PENDING;
3651 if ((get_cache_pitch_family(psc) & TMPF_TRUETYPE))
3652 {
3653 if (!GetCharABCWidthsI(hdc, 0, 1, &glyph, abc)) return S_FALSE;
3654 }
3655 else
3656 {
3657 INT width;
3658 if (!GetCharWidth32W(hdc, glyph, glyph, &width)) return S_FALSE;
3659 abc->abcB = width;
3660 abc->abcA = abc->abcC = 0;
3661 }
3662 set_cache_glyph_widths(psc, glyph, abc);
3663 }
3664 return S_OK;
3665 }
3666
3667 /***********************************************************************
3668 * ScriptLayout (USP10.@)
3669 *
3670 * Map embedding levels to visual and/or logical order.
3671 *
3672 * PARAMS
3673 * runs [I] Size of level array.
3674 * level [I] Array of embedding levels.
3675 * vistolog [O] Map of embedding levels from visual to logical order.
3676 * logtovis [O] Map of embedding levels from logical to visual order.
3677 *
3678 * RETURNS
3679 * Success: S_OK
3680 * Failure: Non-zero HRESULT value.
3681 *
3682 */
3683 HRESULT WINAPI ScriptLayout(int runs, const BYTE *level, int *vistolog, int *logtovis)
3684 {
3685 int* indexs;
3686 int ich;
3687
3688 TRACE("(%d, %p, %p, %p)\n", runs, level, vistolog, logtovis);
3689
3690 if (!level || (!vistolog && !logtovis))
3691 return E_INVALIDARG;
3692
3693 indexs = heap_alloc(sizeof(int) * runs);
3694 if (!indexs)
3695 return E_OUTOFMEMORY;
3696
3697 if (vistolog)
3698 {
3699 for( ich = 0; ich < runs; ich++)
3700 indexs[ich] = ich;
3701
3702 ich = 0;
3703 while (ich < runs)
3704 ich += BIDI_ReorderV2lLevel(0, indexs+ich, level+ich, runs - ich, FALSE);
3705 memcpy(vistolog, indexs, runs * sizeof(*vistolog));
3706 }
3707
3708 if (logtovis)
3709 {
3710 for( ich = 0; ich < runs; ich++)
3711 indexs[ich] = ich;
3712
3713 ich = 0;
3714 while (ich < runs)
3715 ich += BIDI_ReorderL2vLevel(0, indexs+ich, level+ich, runs - ich, FALSE);
3716 memcpy(logtovis, indexs, runs * sizeof(*logtovis));
3717 }
3718 heap_free(indexs);
3719
3720 return S_OK;
3721 }
3722
3723 /***********************************************************************
3724 * ScriptStringGetLogicalWidths (USP10.@)
3725 *
3726 * Returns logical widths from a string analysis.
3727 *
3728 * PARAMS
3729 * ssa [I] string analysis.
3730 * piDx [O] logical widths returned.
3731 *
3732 * RETURNS
3733 * Success: S_OK
3734 * Failure: a non-zero HRESULT.
3735 */
3736 HRESULT WINAPI ScriptStringGetLogicalWidths(SCRIPT_STRING_ANALYSIS ssa, int *piDx)
3737 {
3738 int i, j, next = 0;
3739 StringAnalysis *analysis = ssa;
3740
3741 TRACE("%p, %p\n", ssa, piDx);
3742
3743 if (!analysis) return S_FALSE;
3744 if (!(analysis->dwFlags & SSA_GLYPHS)) return S_FALSE;
3745
3746 for (i = 0; i < analysis->numItems; i++)
3747 {
3748 int cChar = analysis->pItem[i+1].iCharPos - analysis->pItem[i].iCharPos;
3749 int direction = 1;
3750
3751 if (analysis->pItem[i].a.fRTL && ! analysis->pItem[i].a.fLogicalOrder)
3752 direction = -1;
3753
3754 for (j = 0; j < cChar; j++)
3755 {
3756 int k;
3757 int glyph = analysis->glyphs[i].pwLogClust[j];
3758 int clust_size = get_cluster_size(analysis->glyphs[i].pwLogClust,
3759 cChar, j, direction, NULL, NULL);
3760 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);
3761
3762 for (k = 0; k < clust_size; k++)
3763 {
3764 piDx[next] = advance / clust_size;
3765 next++;
3766 if (k) j++;
3767 }
3768 }
3769 }
3770 return S_OK;
3771 }
3772
3773 /***********************************************************************
3774 * ScriptStringValidate (USP10.@)
3775 *
3776 * Validate a string analysis.
3777 *
3778 * PARAMS
3779 * ssa [I] string analysis.
3780 *
3781 * RETURNS
3782 * Success: S_OK
3783 * Failure: S_FALSE if invalid sequences are found
3784 * or a non-zero HRESULT if it fails.
3785 */
3786 HRESULT WINAPI ScriptStringValidate(SCRIPT_STRING_ANALYSIS ssa)
3787 {
3788 StringAnalysis *analysis = ssa;
3789
3790 TRACE("(%p)\n", ssa);
3791
3792 if (!analysis) return E_INVALIDARG;
3793 return (analysis->invalid) ? S_FALSE : S_OK;
3794 }
3795
3796 /***********************************************************************
3797 * ScriptString_pSize (USP10.@)
3798 *
3799 * Retrieve width and height of an analysed string.
3800 *
3801 * PARAMS
3802 * ssa [I] string analysis.
3803 *
3804 * RETURNS
3805 * Success: Pointer to a SIZE structure.
3806 * Failure: NULL
3807 */
3808 const SIZE * WINAPI ScriptString_pSize(SCRIPT_STRING_ANALYSIS ssa)
3809 {
3810 int i, j;
3811 StringAnalysis *analysis = ssa;
3812
3813 TRACE("(%p)\n", ssa);
3814
3815 if (!analysis) return NULL;
3816 if (!(analysis->dwFlags & SSA_GLYPHS)) return NULL;
3817
3818 if (!analysis->sz)
3819 {
3820 if (!(analysis->sz = heap_alloc(sizeof(SIZE)))) return NULL;
3821 analysis->sz->cy = analysis->glyphs[0].sc->tm.tmHeight;
3822
3823 analysis->sz->cx = 0;
3824 for (i = 0; i < analysis->numItems; i++)
3825 {
3826 if (analysis->glyphs[i].sc->tm.tmHeight > analysis->sz->cy)
3827 analysis->sz->cy = analysis->glyphs[i].sc->tm.tmHeight;
3828 for (j = 0; j < analysis->glyphs[i].numGlyphs; j++)
3829 analysis->sz->cx += analysis->glyphs[i].piAdvance[j];
3830 }
3831 }
3832 return analysis->sz;
3833 }
3834
3835 /***********************************************************************
3836 * ScriptString_pLogAttr (USP10.@)
3837 *
3838 * Retrieve logical attributes of an analysed string.
3839 *
3840 * PARAMS
3841 * ssa [I] string analysis.
3842 *
3843 * RETURNS
3844 * Success: Pointer to an array of SCRIPT_LOGATTR structures.
3845 * Failure: NULL
3846 */
3847 const SCRIPT_LOGATTR * WINAPI ScriptString_pLogAttr(SCRIPT_STRING_ANALYSIS ssa)
3848 {
3849 StringAnalysis *analysis = ssa;
3850
3851 TRACE("(%p)\n", ssa);
3852
3853 if (!analysis) return NULL;
3854 if (!(analysis->dwFlags & SSA_BREAK)) return NULL;
3855 return analysis->logattrs;
3856 }
3857
3858 /***********************************************************************
3859 * ScriptString_pcOutChars (USP10.@)
3860 *
3861 * Retrieve the length of a string after clipping.
3862 *
3863 * PARAMS
3864 * ssa [I] String analysis.
3865 *
3866 * RETURNS
3867 * Success: Pointer to the length.
3868 * Failure: NULL
3869 */
3870 const int * WINAPI ScriptString_pcOutChars(SCRIPT_STRING_ANALYSIS ssa)
3871 {
3872 StringAnalysis *analysis = ssa;
3873
3874 TRACE("(%p)\n", ssa);
3875
3876 if (!analysis) return NULL;
3877 return &analysis->clip_len;
3878 }
3879
3880 /***********************************************************************
3881 * ScriptStringGetOrder (USP10.@)
3882 *
3883 * Retrieve a glyph order map.
3884 *
3885 * PARAMS
3886 * ssa [I] String analysis.
3887 * order [I/O] Array of glyph positions.
3888 *
3889 * RETURNS
3890 * Success: S_OK
3891 * Failure: a non-zero HRESULT.
3892 */
3893 HRESULT WINAPI ScriptStringGetOrder(SCRIPT_STRING_ANALYSIS ssa, UINT *order)
3894 {
3895 int i, j;
3896 unsigned int k;
3897 StringAnalysis *analysis = ssa;
3898
3899 TRACE("(%p)\n", ssa);
3900
3901 if (!analysis) return S_FALSE;
3902 if (!(analysis->dwFlags & SSA_GLYPHS)) return S_FALSE;
3903
3904 /* FIXME: handle RTL scripts */
3905 for (i = 0, k = 0; i < analysis->numItems; i++)
3906 for (j = 0; j < analysis->glyphs[i].numGlyphs; j++, k++)
3907 order[k] = k;
3908
3909 return S_OK;
3910 }
3911
3912 /***********************************************************************
3913 * ScriptGetLogicalWidths (USP10.@)
3914 *
3915 * Convert advance widths to logical widths.
3916 *
3917 * PARAMS
3918 * sa [I] Script analysis.
3919 * nbchars [I] Number of characters.
3920 * nbglyphs [I] Number of glyphs.
3921 * glyph_width [I] Array of glyph widths.
3922 * log_clust [I] Array of logical clusters.
3923 * sva [I] Visual attributes.
3924 * widths [O] Array of logical widths.
3925 *
3926 * RETURNS
3927 * Success: S_OK
3928 * Failure: a non-zero HRESULT.
3929 */
3930 HRESULT WINAPI ScriptGetLogicalWidths(const SCRIPT_ANALYSIS *sa, int nbchars, int nbglyphs,
3931 const int *advances, const WORD *log_clust,
3932 const SCRIPT_VISATTR *sva, int *widths)
3933 {
3934 int i, next = 0, direction;
3935
3936 TRACE("(%p, %d, %d, %p, %p, %p, %p)\n",
3937 sa, nbchars, nbglyphs, advances, log_clust, sva, widths);
3938
3939 if (sa->fRTL && !sa->fLogicalOrder)
3940 direction = -1;
3941 else
3942 direction = 1;
3943
3944 for (i = 0; i < nbchars; i++)
3945 {
3946 int clust_size = get_cluster_size(log_clust, nbchars, i, direction, NULL, NULL);
3947 int advance = get_glyph_cluster_advance(advances, sva, log_clust, nbglyphs, nbchars, log_clust[i], direction);
3948 int j;
3949
3950 for (j = 0; j < clust_size; j++)
3951 {
3952 widths[next] = advance / clust_size;
3953 next++;
3954 if (j) i++;
3955 }
3956 }
3957
3958 return S_OK;
3959 }
3960
3961 /***********************************************************************
3962 * ScriptApplyLogicalWidth (USP10.@)
3963 *
3964 * Generate glyph advance widths.
3965 *
3966 * PARAMS
3967 * dx [I] Array of logical advance widths.
3968 * num_chars [I] Number of characters.
3969 * num_glyphs [I] Number of glyphs.
3970 * log_clust [I] Array of logical clusters.
3971 * sva [I] Visual attributes.
3972 * advance [I] Array of glyph advance widths.
3973 * sa [I] Script analysis.
3974 * abc [I/O] Summed ABC widths.
3975 * justify [O] Array of glyph advance widths.
3976 *
3977 * RETURNS
3978 * Success: S_OK
3979 * Failure: a non-zero HRESULT.
3980 */
3981 HRESULT WINAPI ScriptApplyLogicalWidth(const int *dx, int num_chars, int num_glyphs,
3982 const WORD *log_clust, const SCRIPT_VISATTR *sva,
3983 const int *advance, const SCRIPT_ANALYSIS *sa,
3984 ABC *abc, int *justify)
3985 {
3986 int i;
3987
3988 FIXME("(%p, %d, %d, %p, %p, %p, %p, %p, %p)\n",
3989 dx, num_chars, num_glyphs, log_clust, sva, advance, sa, abc, justify);
3990
3991 for (i = 0; i < num_chars; i++) justify[i] = advance[i];
3992 return S_OK;
3993 }
3994
3995 HRESULT WINAPI ScriptJustify(const SCRIPT_VISATTR *sva, const int *advance,
3996 int num_glyphs, int dx, int min_kashida, int *justify)
3997 {
3998 int i;
3999
4000 FIXME("(%p, %p, %d, %d, %d, %p)\n", sva, advance, num_glyphs, dx, min_kashida, justify);
4001
4002 for (i = 0; i < num_glyphs; i++) justify[i] = advance[i];
4003 return S_OK;
4004 }
4005
4006 HRESULT WINAPI ScriptGetFontScriptTags( HDC hdc, SCRIPT_CACHE *psc, SCRIPT_ANALYSIS *psa, int cMaxTags, OPENTYPE_TAG *pScriptTags, int *pcTags)
4007 {
4008 HRESULT hr;
4009 if (!pScriptTags || !pcTags || cMaxTags == 0) return E_INVALIDARG;
4010 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
4011
4012 return SHAPE_GetFontScriptTags(hdc, (ScriptCache *)*psc, psa, cMaxTags, pScriptTags, pcTags);
4013 }
4014
4015 HRESULT WINAPI ScriptGetFontLanguageTags( HDC hdc, SCRIPT_CACHE *psc, SCRIPT_ANALYSIS *psa, OPENTYPE_TAG tagScript, int cMaxTags, OPENTYPE_TAG *pLangSysTags, int *pcTags)
4016 {
4017 HRESULT hr;
4018 if (!pLangSysTags || !pcTags || cMaxTags == 0) return E_INVALIDARG;
4019 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
4020
4021 return SHAPE_GetFontLanguageTags(hdc, (ScriptCache *)*psc, psa, tagScript, cMaxTags, pLangSysTags, pcTags);
4022 }
4023
4024 HRESULT WINAPI ScriptGetFontFeatureTags( HDC hdc, SCRIPT_CACHE *psc, SCRIPT_ANALYSIS *psa, OPENTYPE_TAG tagScript, OPENTYPE_TAG tagLangSys, int cMaxTags, OPENTYPE_TAG *pFeatureTags, int *pcTags)
4025 {
4026 HRESULT hr;
4027 if (!pFeatureTags || !pcTags || cMaxTags == 0) return E_INVALIDARG;
4028 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
4029
4030 return SHAPE_GetFontFeatureTags(hdc, (ScriptCache *)*psc, psa, tagScript, tagLangSys, cMaxTags, pFeatureTags, pcTags);
4031 }
4032
4033 #ifdef __REACTOS__
4034 BOOL gbLpkPresent = FALSE;
4035 VOID WINAPI LpkPresent()
4036 {
4037 gbLpkPresent = TRUE; /* Turn it on this way! Wine is out of control! */
4038 }
4039 #endif