iphlpapi: Implement some more IpStatistics on BSD.
[wine.git] / dlls / usp10 / usp10.c
blobdd6a854356641a8c9dc874c4d3de66242a37a3c1
1 /*
2 * Implementation of Uniscribe Script Processor (usp10.dll)
4 * Copyright 2005 Steven Edwards for CodeWeavers
5 * Copyright 2006 Hans Leidekker
6 * Copyright 2010 CodeWeavers, Aric Stewart
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.
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.
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
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.
27 #include <stdarg.h>
28 #include <stdlib.h>
29 #include <math.h>
31 #include "windef.h"
32 #include "winbase.h"
33 #include "wingdi.h"
34 #include "winuser.h"
35 #include "winnls.h"
36 #include "winreg.h"
37 #include "usp10.h"
39 #include "usp10_internal.h"
41 #include "wine/debug.h"
42 #include "wine/heap.h"
44 WINE_DEFAULT_DEBUG_CHANNEL(uniscribe);
46 static const struct usp10_script_range
48 enum usp10_script script;
49 DWORD rangeFirst;
50 DWORD rangeLast;
51 enum usp10_script numericScript;
52 enum usp10_script punctScript;
54 script_ranges[] =
56 /* Basic Latin: U+0000–U+007A */
57 { Script_Latin, 0x00, 0x07a , Script_Numeric, Script_Punctuation},
58 /* Latin-1 Supplement: U+0080–U+00FF */
59 /* Latin Extended-A: U+0100–U+017F */
60 /* Latin Extended-B: U+0180–U+024F */
61 /* IPA Extensions: U+0250–U+02AF */
62 /* Spacing Modifier Letters:U+02B0–U+02FF */
63 { Script_Latin, 0x80, 0x2ff , Script_Numeric2, Script_Punctuation},
64 /* Combining Diacritical Marks : U+0300–U+036F */
65 { Script_Diacritical,0x300, 0x36f, 0, 0},
66 /* Greek: U+0370–U+03FF */
67 { Script_Greek, 0x370, 0x3ff, 0, 0},
68 /* Cyrillic: U+0400–U+04FF */
69 /* Cyrillic Supplement: U+0500–U+052F */
70 { Script_Cyrillic, 0x400, 0x52f, 0, 0},
71 /* Armenian: U+0530–U+058F */
72 { Script_Armenian, 0x530, 0x58f, 0, 0},
73 /* Hebrew: U+0590–U+05FF */
74 { Script_Hebrew, 0x590, 0x5ff, 0, 0},
75 /* Arabic: U+0600–U+06FF */
76 { Script_Arabic, 0x600, 0x6ef, Script_Arabic_Numeric, 0},
77 /* Defined by Windows */
78 { Script_Persian, 0x6f0, 0x6f9, 0, 0},
79 /* Continue Arabic: U+0600–U+06FF */
80 { Script_Arabic, 0x6fa, 0x6ff, 0, 0},
81 /* Syriac: U+0700–U+074F*/
82 { Script_Syriac, 0x700, 0x74f, 0, 0},
83 /* Arabic Supplement: U+0750–U+077F */
84 { Script_Arabic, 0x750, 0x77f, 0, 0},
85 /* Thaana: U+0780–U+07BF */
86 { Script_Thaana, 0x780, 0x7bf, 0, 0},
87 /* N’Ko: U+07C0–U+07FF */
88 { Script_NKo, 0x7c0, 0x7ff, 0, 0},
89 /* Devanagari: U+0900–U+097F */
90 { Script_Devanagari, 0x900, 0x97f, Script_Devanagari_Numeric, 0},
91 /* Bengali: U+0980–U+09FF */
92 { Script_Bengali, 0x980, 0x9ff, Script_Bengali_Numeric, 0},
93 /* Gurmukhi: U+0A00–U+0A7F*/
94 { Script_Gurmukhi, 0xa00, 0xa7f, Script_Gurmukhi_Numeric, 0},
95 /* Gujarati: U+0A80–U+0AFF*/
96 { Script_Gujarati, 0xa80, 0xaff, Script_Gujarati_Numeric, 0},
97 /* Oriya: U+0B00–U+0B7F */
98 { Script_Oriya, 0xb00, 0xb7f, Script_Oriya_Numeric, 0},
99 /* Tamil: U+0B80–U+0BFF */
100 { Script_Tamil, 0xb80, 0xbff, Script_Tamil_Numeric, 0},
101 /* Telugu: U+0C00–U+0C7F */
102 { Script_Telugu, 0xc00, 0xc7f, Script_Telugu_Numeric, 0},
103 /* Kannada: U+0C80–U+0CFF */
104 { Script_Kannada, 0xc80, 0xcff, Script_Kannada_Numeric, 0},
105 /* Malayalam: U+0D00–U+0D7F */
106 { Script_Malayalam, 0xd00, 0xd7f, Script_Malayalam_Numeric, 0},
107 /* Sinhala: U+0D80–U+0DFF */
108 { Script_Sinhala, 0xd80, 0xdff, 0, 0},
109 /* Thai: U+0E00–U+0E7F */
110 { Script_Thai, 0xe00, 0xe7f, Script_Thai_Numeric, 0},
111 /* Lao: U+0E80–U+0EFF */
112 { Script_Lao, 0xe80, 0xeff, Script_Lao_Numeric, 0},
113 /* Tibetan: U+0F00–U+0FFF */
114 { Script_Tibetan, 0xf00, 0xfff, 0, 0},
115 /* Myanmar: U+1000–U+109F */
116 { Script_Myanmar, 0x1000, 0x109f, Script_Myanmar_Numeric, 0},
117 /* Georgian: U+10A0–U+10FF */
118 { Script_Georgian, 0x10a0, 0x10ff, 0, 0},
119 /* Hangul Jamo: U+1100–U+11FF */
120 { Script_Hangul, 0x1100, 0x11ff, 0, 0},
121 /* Ethiopic: U+1200–U+137F */
122 /* Ethiopic Extensions: U+1380–U+139F */
123 { Script_Ethiopic, 0x1200, 0x139f, 0, 0},
124 /* Cherokee: U+13A0–U+13FF */
125 { Script_Cherokee, 0x13a0, 0x13ff, 0, 0},
126 /* Canadian Aboriginal Syllabics: U+1400–U+167F */
127 { Script_Canadian, 0x1400, 0x167f, 0, 0},
128 /* Ogham: U+1680–U+169F */
129 { Script_Ogham, 0x1680, 0x169f, 0, 0},
130 /* Runic: U+16A0–U+16F0 */
131 { Script_Runic, 0x16a0, 0x16f0, 0, 0},
132 /* Khmer: U+1780–U+17FF */
133 { Script_Khmer, 0x1780, 0x17ff, Script_Khmer_Numeric, 0},
134 /* Mongolian: U+1800–U+18AF */
135 { Script_Mongolian, 0x1800, 0x18af, Script_Mongolian_Numeric, 0},
136 /* Canadian Aboriginal Syllabics Extended: U+18B0–U+18FF */
137 { Script_Canadian, 0x18b0, 0x18ff, 0, 0},
138 /* Tai Le: U+1950–U+197F */
139 { Script_Tai_Le, 0x1950, 0x197f, 0, 0},
140 /* New Tai Lue: U+1980–U+19DF */
141 { Script_New_Tai_Lue,0x1980, 0x19df, Script_New_Tai_Lue_Numeric, 0},
142 /* Khmer Symbols: U+19E0–U+19FF */
143 { Script_Khmer, 0x19e0, 0x19ff, Script_Khmer_Numeric, 0},
144 /* Vedic Extensions: U+1CD0-U+1CFF */
145 { Script_Devanagari, 0x1cd0, 0x1cff, Script_Devanagari_Numeric, 0},
146 /* Phonetic Extensions: U+1D00–U+1DBF */
147 { Script_Latin, 0x1d00, 0x1dbf, 0, 0},
148 /* Combining Diacritical Marks Supplement: U+1DC0–U+1DFF */
149 { Script_Diacritical,0x1dc0, 0x1dff, 0, 0},
150 /* Latin Extended Additional: U+1E00–U+1EFF */
151 { Script_Latin, 0x1e00, 0x1eff, 0, 0},
152 /* Greek Extended: U+1F00–U+1FFF */
153 { Script_Greek, 0x1f00, 0x1fff, 0, 0},
154 /* General Punctuation: U+2000 –U+206f */
155 { Script_Latin, 0x2000, 0x206f, 0, 0},
156 /* Superscripts and Subscripts : U+2070 –U+209f */
157 /* Currency Symbols : U+20a0 –U+20cf */
158 { Script_Numeric2, 0x2070, 0x2070, 0, 0},
159 { Script_Latin, 0x2071, 0x2073, 0, 0},
160 { Script_Numeric2, 0x2074, 0x2079, 0, 0},
161 { Script_Latin, 0x207a, 0x207f, 0, 0},
162 { Script_Numeric2, 0x2080, 0x2089, 0, 0},
163 { Script_Latin, 0x208a, 0x20cf, 0, 0},
164 /* Letterlike Symbols : U+2100 –U+214f */
165 /* Number Forms : U+2150 –U+218f */
166 /* Arrows : U+2190 –U+21ff */
167 /* Mathematical Operators : U+2200 –U+22ff */
168 /* Miscellaneous Technical : U+2300 –U+23ff */
169 /* Control Pictures : U+2400 –U+243f */
170 /* Optical Character Recognition : U+2440 –U+245f */
171 /* Enclosed Alphanumerics : U+2460 –U+24ff */
172 /* Box Drawing : U+2500 –U+25ff */
173 /* Block Elements : U+2580 –U+259f */
174 /* Geometric Shapes : U+25a0 –U+25ff */
175 /* Miscellaneous Symbols : U+2600 –U+26ff */
176 /* Dingbats : U+2700 –U+27bf */
177 /* Miscellaneous Mathematical Symbols-A : U+27c0 –U+27ef */
178 /* Supplemental Arrows-A : U+27f0 –U+27ff */
179 { Script_Latin, 0x2100, 0x27ff, 0, 0},
180 /* Braille Patterns: U+2800–U+28FF */
181 { Script_Braille, 0x2800, 0x28ff, 0, 0},
182 /* Supplemental Arrows-B : U+2900 –U+297f */
183 /* Miscellaneous Mathematical Symbols-B : U+2980 –U+29ff */
184 /* Supplemental Mathematical Operators : U+2a00 –U+2aff */
185 /* Miscellaneous Symbols and Arrows : U+2b00 –U+2bff */
186 { Script_Latin, 0x2900, 0x2bff, 0, 0},
187 /* Latin Extended-C: U+2C60–U+2C7F */
188 { Script_Latin, 0x2c60, 0x2c7f, 0, 0},
189 /* Georgian: U+2D00–U+2D2F */
190 { Script_Georgian, 0x2d00, 0x2d2f, 0, 0},
191 /* Tifinagh: U+2D30–U+2D7F */
192 { Script_Tifinagh, 0x2d30, 0x2d7f, 0, 0},
193 /* Ethiopic Extensions: U+2D80–U+2DDF */
194 { Script_Ethiopic, 0x2d80, 0x2ddf, 0, 0},
195 /* Cyrillic Extended-A: U+2DE0–U+2DFF */
196 { Script_Cyrillic, 0x2de0, 0x2dff, 0, 0},
197 /* CJK Radicals Supplement: U+2E80–U+2EFF */
198 /* Kangxi Radicals: U+2F00–U+2FDF */
199 { Script_CJK_Han, 0x2e80, 0x2fdf, 0, 0},
200 /* Ideographic Description Characters: U+2FF0–U+2FFF */
201 { Script_Ideograph ,0x2ff0, 0x2fff, 0, 0},
202 /* CJK Symbols and Punctuation: U+3000–U+303F */
203 { Script_Ideograph ,0x3000, 0x3004, 0, 0},
204 { Script_CJK_Han ,0x3005, 0x3005, 0, 0},
205 { Script_Ideograph ,0x3006, 0x3006, 0, 0},
206 { Script_CJK_Han ,0x3007, 0x3007, 0, 0},
207 { Script_Ideograph ,0x3008, 0x3020, 0, 0},
208 { Script_CJK_Han ,0x3021, 0x3029, 0, 0},
209 { Script_Ideograph ,0x302a, 0x3030, 0, 0},
210 /* Kana Marks: */
211 { Script_Kana ,0x3031, 0x3035, 0, 0},
212 { Script_Ideograph ,0x3036, 0x3037, 0, 0},
213 { Script_CJK_Han ,0x3038, 0x303b, 0, 0},
214 { Script_Ideograph ,0x303c, 0x303f, 0, 0},
215 /* Hiragana: U+3040–U+309F */
216 /* Katakana: U+30A0–U+30FF */
217 { Script_Kana ,0x3040, 0x30ff, 0, 0},
218 /* Bopomofo: U+3100–U+312F */
219 { Script_Bopomofo ,0x3100, 0x312f, 0, 0},
220 /* Hangul Compatibility Jamo: U+3130–U+318F */
221 { Script_Hangul ,0x3130, 0x318f, 0, 0},
222 /* Kanbun: U+3190–U+319F */
223 { Script_Ideograph ,0x3190, 0x319f, 0, 0},
224 /* Bopomofo Extended: U+31A0–U+31BF */
225 { Script_Bopomofo ,0x31a0, 0x31bf, 0, 0},
226 /* CJK Strokes: U+31C0–U+31EF */
227 { Script_Ideograph ,0x31c0, 0x31ef, 0, 0},
228 /* Katakana Phonetic Extensions: U+31F0–U+31FF */
229 { Script_Kana ,0x31f0, 0x31ff, 0, 0},
230 /* Enclosed CJK Letters and Months: U+3200–U+32FF */
231 { Script_Hangul ,0x3200, 0x321f, 0, 0},
232 { Script_Ideograph ,0x3220, 0x325f, 0, 0},
233 { Script_Hangul ,0x3260, 0x327f, 0, 0},
234 { Script_Ideograph ,0x3280, 0x32ef, 0, 0},
235 { Script_Kana ,0x32d0, 0x31ff, 0, 0},
236 /* CJK Compatibility: U+3300–U+33FF*/
237 { Script_Kana ,0x3300, 0x3357, 0, 0},
238 { Script_Ideograph ,0x3358, 0x33ff, 0, 0},
239 /* CJK Unified Ideographs Extension A: U+3400–U+4DBF */
240 { Script_CJK_Han ,0x3400, 0x4dbf, 0, 0},
241 /* CJK Unified Ideographs: U+4E00–U+9FFF */
242 { Script_CJK_Han ,0x4e00, 0x9fff, 0, 0},
243 /* Yi: U+A000–U+A4CF */
244 { Script_Yi ,0xa000, 0xa4cf, 0, 0},
245 /* Vai: U+A500–U+A63F */
246 { Script_Vai ,0xa500, 0xa63f, Script_Vai_Numeric, 0},
247 /* Cyrillic Extended-B: U+A640–U+A69F */
248 { Script_Cyrillic, 0xa640, 0xa69f, 0, 0},
249 /* Modifier Tone Letters: U+A700–U+A71F */
250 /* Latin Extended-D: U+A720–U+A7FF */
251 { Script_Latin, 0xa700, 0xa7ff, 0, 0},
252 /* Phags-pa: U+A840–U+A87F */
253 { Script_Phags_pa, 0xa840, 0xa87f, 0, 0},
254 /* Devanagari Extended: U+A8E0-U+A8FF */
255 { Script_Devanagari, 0xa8e0, 0xa8ff, Script_Devanagari_Numeric, 0},
256 /* Myanmar Extended-A: U+AA60–U+AA7F */
257 { Script_Myanmar, 0xaa60, 0xaa7f, Script_Myanmar_Numeric, 0},
258 /* Hangul Jamo Extended-A: U+A960–U+A97F */
259 { Script_Hangul, 0xa960, 0xa97f, 0, 0},
260 /* Hangul Syllables: U+AC00–U+D7A3 */
261 { Script_Hangul, 0xac00, 0xd7a3, 0, 0},
262 /* Hangul Jamo Extended-B: U+D7B0–U+D7FF */
263 { Script_Hangul, 0xd7b0, 0xd7ff, 0, 0},
264 /* Surrogates Area: U+D800–U+DFFF */
265 { Script_Surrogates, 0xd800, 0xdbfe, 0, 0},
266 { Script_Private, 0xdbff, 0xdc00, 0, 0},
267 { Script_Surrogates, 0xdc01, 0xdfff, 0, 0},
268 /* Private Use Area: U+E000–U+F8FF */
269 { Script_Private, 0xe000, 0xf8ff, 0, 0},
270 /* CJK Compatibility Ideographs: U+F900–U+FAFF */
271 { Script_CJK_Han ,0xf900, 0xfaff, 0, 0},
272 /* Latin Ligatures: U+FB00–U+FB06 */
273 { Script_Latin, 0xfb00, 0xfb06, 0, 0},
274 /* Armenian ligatures U+FB13..U+FB17 */
275 { Script_Armenian, 0xfb13, 0xfb17, 0, 0},
276 /* Alphabetic Presentation Forms: U+FB1D–U+FB4F */
277 { Script_Hebrew, 0xfb1d, 0xfb4f, 0, 0},
278 /* Arabic Presentation Forms-A: U+FB50–U+FDFF*/
279 { Script_Arabic, 0xfb50, 0xfdff, 0, 0},
280 /* Vertical Forms: U+FE10–U+FE1F */
281 /* Combining Half Marks: U+FE20–U+FE2F */
282 /* CJK Compatibility Forms: U+FE30–U+FE4F */
283 /* Small Form Variants: U+FE50–U+FE6F */
284 { Script_Ideograph ,0xfe10, 0xfe6f, 0, 0},
285 /* Arabic Presentation Forms-B: U+FE70–U+FEFF*/
286 { Script_Arabic, 0xfe70, 0xfeff, 0, 0},
287 /* Halfwidth and Fullwidth Forms: U+FF00–FFEF */
288 { Script_Ideograph ,0xff00, 0xff64, Script_Numeric2, 0},
289 { Script_Kana ,0xff65, 0xff9f, 0, 0},
290 { Script_Hangul ,0xffa0, 0xffdf, 0, 0},
291 { Script_Ideograph ,0xffe0, 0xffef, 0, 0},
292 /* Plane - 1 */
293 /* Deseret: U+10400–U+1044F */
294 { Script_Deseret, 0x10400, 0x1044F, 0, 0},
295 /* Osmanya: U+10480–U+104AF */
296 { Script_Osmanya, 0x10480, 0x104AF, Script_Osmanya_Numeric, 0},
297 /* Mathematical Alphanumeric Symbols: U+1D400–U+1D7FF */
298 { Script_MathAlpha, 0x1D400, 0x1D7FF, 0, 0},
301 /* this must be in order so that the index matches the Script value */
302 const scriptData scriptInformation[] = {
303 {{SCRIPT_UNDEFINED, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
304 {LANG_NEUTRAL, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
305 0x00000000,
306 {0}},
307 {{Script_Latin, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
308 {LANG_ENGLISH, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
309 MS_MAKE_TAG('l','a','t','n'),
310 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
311 {{Script_CR, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
312 {LANG_NEUTRAL, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
313 0x00000000,
314 {0}},
315 {{Script_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
316 {LANG_ENGLISH, 1, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
317 0x00000000,
318 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
319 {{Script_Control, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
320 {LANG_ENGLISH, 0, 1, 0, 0, ANSI_CHARSET, 1, 0, 0, 0, 0, 0, 1, 0, 0},
321 0x00000000,
322 {0}},
323 {{Script_Punctuation, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
324 {LANG_NEUTRAL, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
325 0x00000000,
326 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
327 {{Script_Arabic, 1, 1, 0, 0, 0, 0, { 1,0,0,0,0,0,0,0,0,0,0}},
328 {LANG_ARABIC, 0, 1, 0, 0, ARABIC_CHARSET, 0, 0, 0, 0, 0, 0, 1, 1, 0},
329 MS_MAKE_TAG('a','r','a','b'),
330 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
331 {{Script_Arabic_Numeric, 0, 1, 0, 0, 0, 0, { 2,0,0,0,0,0,0,0,0,0,0}},
332 {LANG_ARABIC, 1, 1, 0, 0, ARABIC_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
333 MS_MAKE_TAG('a','r','a','b'),
334 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
335 {{Script_Hebrew, 1, 1, 0, 0, 0, 0, { 1,0,0,0,0,0,0,0,0,0,0}},
336 {LANG_HEBREW, 0, 1, 0, 1, HEBREW_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
337 MS_MAKE_TAG('h','e','b','r'),
338 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
339 {{Script_Syriac, 1, 1, 0, 0, 0, 0, { 1,0,0,0,0,0,0,0,0,0,0}},
340 {LANG_SYRIAC, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 1, 0},
341 MS_MAKE_TAG('s','y','r','c'),
342 {'E','s','t','r','a','n','g','e','l','o',' ','E','d','e','s','s','a',0}},
343 {{Script_Persian, 0, 1, 0, 0, 0, 0, { 2,0,0,0,0,0,0,0,0,0,0}},
344 {LANG_PERSIAN, 1, 1, 0, 0, ARABIC_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
345 MS_MAKE_TAG('a','r','a','b'),
346 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
347 {{Script_Thaana, 1, 1, 0, 0, 0, 0, { 1,0,0,0,0,0,0,0,0,0,0}},
348 {LANG_DIVEHI, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
349 MS_MAKE_TAG('t','h','a','a'),
350 {'M','V',' ','B','o','l','i',0}},
351 {{Script_Greek, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
352 {LANG_GREEK, 0, 0, 0, 0, GREEK_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
353 MS_MAKE_TAG('g','r','e','k'),
354 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
355 {{Script_Cyrillic, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
356 {LANG_RUSSIAN, 0, 0, 0, 0, RUSSIAN_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
357 MS_MAKE_TAG('c','y','r','l'),
358 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
359 {{Script_Armenian, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
360 {LANG_ARMENIAN, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
361 MS_MAKE_TAG('a','r','m','n'),
362 {'S','y','l','f','a','e','n',0}},
363 {{Script_Georgian, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
364 {LANG_GEORGIAN, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
365 MS_MAKE_TAG('g','e','o','r'),
366 {'S','y','l','f','a','e','n',0}},
367 {{Script_Sinhala, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
368 {LANG_SINHALESE, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
369 MS_MAKE_TAG('s','i','n','h'),
370 {'I','s','k','o','o','l','a',' ','P','o','t','a',0}},
371 {{Script_Tibetan, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
372 {LANG_TIBETAN, 0, 1, 1, 1, DEFAULT_CHARSET, 0, 0, 1, 0, 1, 0, 0, 0, 0},
373 MS_MAKE_TAG('t','i','b','t'),
374 {'M','i','c','r','o','s','o','f','t',' ','H','i','m','a','l','a','y','a',0}},
375 {{Script_Tibetan_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
376 {LANG_TIBETAN, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
377 MS_MAKE_TAG('t','i','b','t'),
378 {'M','i','c','r','o','s','o','f','t',' ','H','i','m','a','l','a','y','a',0}},
379 {{Script_Phags_pa, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
380 {LANG_MONGOLIAN, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
381 MS_MAKE_TAG('p','h','a','g'),
382 {'M','i','c','r','o','s','o','f','t',' ','P','h','a','g','s','P','a',0}},
383 {{Script_Thai, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
384 {LANG_THAI, 0, 1, 1, 1, THAI_CHARSET, 0, 0, 1, 0, 1, 0, 0, 0, 1},
385 MS_MAKE_TAG('t','h','a','i'),
386 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
387 {{Script_Thai_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
388 {LANG_THAI, 1, 1, 0, 0, THAI_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
389 MS_MAKE_TAG('t','h','a','i'),
390 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
391 {{Script_Lao, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
392 {LANG_LAO, 0, 1, 1, 1, DEFAULT_CHARSET, 0, 0, 1, 0, 1, 0, 0, 0, 0},
393 MS_MAKE_TAG('l','a','o',' '),
394 {'D','o','k','C','h','a','m','p','a',0}},
395 {{Script_Lao_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
396 {LANG_LAO, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
397 MS_MAKE_TAG('l','a','o',' '),
398 {'D','o','k','C','h','a','m','p','a',0}},
399 {{Script_Devanagari, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
400 {LANG_HINDI, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
401 MS_MAKE_TAG('d','e','v','a'),
402 {'M','a','n','g','a','l',0}},
403 {{Script_Devanagari_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
404 {LANG_HINDI, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
405 MS_MAKE_TAG('d','e','v','a'),
406 {'M','a','n','g','a','l',0}},
407 {{Script_Bengali, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
408 {LANG_BENGALI, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
409 MS_MAKE_TAG('b','e','n','g'),
410 {'V','r','i','n','d','a',0}},
411 {{Script_Bengali_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
412 {LANG_BENGALI, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
413 MS_MAKE_TAG('b','e','n','g'),
414 {'V','r','i','n','d','a',0}},
415 {{Script_Bengali_Currency, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
416 {LANG_BENGALI, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
417 MS_MAKE_TAG('b','e','n','g'),
418 {'V','r','i','n','d','a',0}},
419 {{Script_Gurmukhi, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
420 {LANG_PUNJABI, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
421 MS_MAKE_TAG('g','u','r','u'),
422 {'R','a','a','v','i',0}},
423 {{Script_Gurmukhi_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
424 {LANG_PUNJABI, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
425 MS_MAKE_TAG('g','u','r','u'),
426 {'R','a','a','v','i',0}},
427 {{Script_Gujarati, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
428 {LANG_GUJARATI, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
429 MS_MAKE_TAG('g','u','j','r'),
430 {'S','h','r','u','t','i',0}},
431 {{Script_Gujarati_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
432 {LANG_GUJARATI, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
433 MS_MAKE_TAG('g','u','j','r'),
434 {'S','h','r','u','t','i',0}},
435 {{Script_Gujarati_Currency, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
436 {LANG_GUJARATI, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
437 MS_MAKE_TAG('g','u','j','r'),
438 {'S','h','r','u','t','i',0}},
439 {{Script_Oriya, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
440 {LANG_ORIYA, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
441 MS_MAKE_TAG('o','r','y','a'),
442 {'K','a','l','i','n','g','a',0}},
443 {{Script_Oriya_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
444 {LANG_ORIYA, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
445 MS_MAKE_TAG('o','r','y','a'),
446 {'K','a','l','i','n','g','a',0}},
447 {{Script_Tamil, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
448 {LANG_TAMIL, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
449 MS_MAKE_TAG('t','a','m','l'),
450 {'L','a','t','h','a',0}},
451 {{Script_Tamil_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
452 {LANG_TAMIL, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
453 MS_MAKE_TAG('t','a','m','l'),
454 {'L','a','t','h','a',0}},
455 {{Script_Telugu, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
456 {LANG_TELUGU, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
457 MS_MAKE_TAG('t','e','l','u'),
458 {'G','a','u','t','a','m','i',0}},
459 {{Script_Telugu_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
460 {LANG_TELUGU, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
461 MS_MAKE_TAG('t','e','l','u'),
462 {'G','a','u','t','a','m','i',0}},
463 {{Script_Kannada, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
464 {LANG_KANNADA, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
465 MS_MAKE_TAG('k','n','d','a'),
466 {'T','u','n','g','a',0}},
467 {{Script_Kannada_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
468 {LANG_KANNADA, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
469 MS_MAKE_TAG('k','n','d','a'),
470 {'T','u','n','g','a',0}},
471 {{Script_Malayalam, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
472 {LANG_MALAYALAM, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
473 MS_MAKE_TAG('m','l','y','m'),
474 {'K','a','r','t','i','k','a',0}},
475 {{Script_Malayalam_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
476 {LANG_MALAYALAM, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
477 MS_MAKE_TAG('m','l','y','m'),
478 {'K','a','r','t','i','k','a',0}},
479 {{Script_Diacritical, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
480 {LANG_ENGLISH, 0, 1, 0, 1, ANSI_CHARSET, 0, 0, 0, 0, 0, 1, 1, 0, 0},
481 0x00000000,
482 {0}},
483 {{Script_Punctuation2, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
484 {LANG_ENGLISH, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
485 MS_MAKE_TAG('l','a','t','n'),
486 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
487 {{Script_Numeric2, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
488 {LANG_ENGLISH, 1, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
489 0x00000000,
490 {0}},
491 {{Script_Myanmar, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
492 {0x55, 0, 1, 1, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
493 MS_MAKE_TAG('m','y','m','r'),
494 {'M','y','a','n','m','a','r',' ','T','e','x','t',0}},
495 {{Script_Myanmar_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
496 {0x55, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
497 MS_MAKE_TAG('m','y','m','r'),
498 {0}},
499 {{Script_Tai_Le, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
500 {0, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
501 MS_MAKE_TAG('t','a','l','e'),
502 {'M','i','c','r','o','s','o','f','t',' ','T','a','i',' ','L','e',0}},
503 {{Script_New_Tai_Lue, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
504 {0, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
505 MS_MAKE_TAG('t','a','l','u'),
506 {'M','i','c','r','o','s','o','f','t',' ','N','e','w',' ','T','a','i',' ','L','u','e',0}},
507 {{Script_New_Tai_Lue_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
508 {0, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
509 MS_MAKE_TAG('t','a','l','u'),
510 {'M','i','c','r','o','s','o','f','t',' ','N','e','w',' ','T','a','i',' ','L','u','e',0}},
511 {{Script_Khmer, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
512 {0x53, 0, 1, 1, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
513 MS_MAKE_TAG('k','h','m','r'),
514 {'D','a','u','n','P','e','n','h',0}},
515 {{Script_Khmer_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
516 {0x53, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
517 MS_MAKE_TAG('k','h','m','r'),
518 {'D','a','u','n','P','e','n','h',0}},
519 {{Script_CJK_Han, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
520 {LANG_ENGLISH, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
521 MS_MAKE_TAG('h','a','n','i'),
522 {0}},
523 {{Script_Ideograph, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
524 {LANG_ENGLISH, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
525 MS_MAKE_TAG('h','a','n','i'),
526 {0}},
527 {{Script_Bopomofo, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
528 {LANG_ENGLISH, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
529 MS_MAKE_TAG('b','o','p','o'),
530 {0}},
531 {{Script_Kana, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
532 {LANG_ENGLISH, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
533 MS_MAKE_TAG('k','a','n','a'),
534 {0}},
535 {{Script_Hangul, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
536 {LANG_KOREAN, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
537 MS_MAKE_TAG('h','a','n','g'),
538 {0}},
539 {{Script_Yi, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
540 {LANG_ENGLISH, 0, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
541 MS_MAKE_TAG('y','i',' ',' '),
542 {'M','i','c','r','o','s','o','f','t',' ','Y','i',' ','B','a','i','t','i',0}},
543 {{Script_Ethiopic, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
544 {0x5e, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
545 MS_MAKE_TAG('e','t','h','i'),
546 {'N','y','a','l','a',0}},
547 {{Script_Ethiopic_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
548 {0x5e, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
549 MS_MAKE_TAG('e','t','h','i'),
550 {'N','y','a','l','a',0}},
551 {{Script_Mongolian, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
552 {LANG_MONGOLIAN, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
553 MS_MAKE_TAG('m','o','n','g'),
554 {'M','o','n','g','o','l','i','a','n',' ','B','a','i','t','i',0}},
555 {{Script_Mongolian_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
556 {LANG_MONGOLIAN, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
557 MS_MAKE_TAG('m','o','n','g'),
558 {'M','o','n','g','o','l','i','a','n',' ','B','a','i','t','i',0}},
559 {{Script_Tifinagh, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
560 {0, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
561 MS_MAKE_TAG('t','f','n','g'),
562 {'E','b','r','i','m','a',0}},
563 {{Script_NKo, 1, 1, 0, 0, 0, 0, { 1,0,0,0,0,0,0,0,0,0,0}},
564 {0, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
565 MS_MAKE_TAG('n','k','o',' '),
566 {'E','b','r','i','m','a',0}},
567 {{Script_Vai, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
568 {0, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
569 MS_MAKE_TAG('v','a','i',' '),
570 {'E','b','r','i','m','a',0}},
571 {{Script_Vai_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
572 {0, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
573 MS_MAKE_TAG('v','a','i',' '),
574 {'E','b','r','i','m','a',0}},
575 {{Script_Cherokee, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
576 {0x5c, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
577 MS_MAKE_TAG('c','h','e','r'),
578 {'P','l','a','n','t','a','g','e','n','e','t',' ','C','h','e','r','o','k','e','e',0}},
579 {{Script_Canadian, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
580 {0x5d, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
581 MS_MAKE_TAG('c','a','n','s'),
582 {'E','u','p','h','e','m','i','a',0}},
583 {{Script_Ogham, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
584 {0, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
585 MS_MAKE_TAG('o','g','a','m'),
586 {'S','e','g','o','e',' ','U','I',' ','S','y','m','b','o','l',0}},
587 {{Script_Runic, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
588 {0, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
589 MS_MAKE_TAG('r','u','n','r'),
590 {'S','e','g','o','e',' ','U','I',' ','S','y','m','b','o','l',0}},
591 {{Script_Braille, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
592 {LANG_ENGLISH, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
593 MS_MAKE_TAG('b','r','a','i'),
594 {'S','e','g','o','e',' ','U','I',' ','S','y','m','b','o','l',0}},
595 {{Script_Surrogates, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
596 {LANG_ENGLISH, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
597 0x00000000,
598 {0}},
599 {{Script_Private, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
600 {0, 0, 0, 0, 0, DEFAULT_CHARSET, 0, 1, 0, 0, 0, 0, 1, 0, 0},
601 0x00000000,
602 {0}},
603 {{Script_Deseret, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
604 {0, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
605 MS_MAKE_TAG('d','s','r','t'),
606 {'S','e','g','o','e',' ','U','I',' ','S','y','m','b','o','l',0}},
607 {{Script_Osmanya, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
608 {0, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
609 MS_MAKE_TAG('o','s','m','a'),
610 {'E','b','r','i','m','a',0}},
611 {{Script_Osmanya_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
612 {0, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
613 MS_MAKE_TAG('o','s','m','a'),
614 {'E','b','r','i','m','a',0}},
615 {{Script_MathAlpha, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
616 {0, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
617 MS_MAKE_TAG('m','a','t','h'),
618 {'C','a','m','b','r','i','a',' ','M','a','t','h',0}},
619 {{Script_Hebrew_Currency, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
620 {LANG_HEBREW, 0, 1, 0, 0, HEBREW_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
621 MS_MAKE_TAG('h','e','b','r'),
622 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
623 {{Script_Vietnamese_Currency, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
624 {LANG_VIETNAMESE, 0, 0, 0, 0, VIETNAMESE_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
625 MS_MAKE_TAG('l','a','t','n'),
626 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
627 {{Script_Thai_Currency, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
628 {LANG_THAI, 0, 1, 0, 0, THAI_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
629 MS_MAKE_TAG('t','h','a','i'),
630 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
633 static const SCRIPT_PROPERTIES *script_props[] =
635 &scriptInformation[0].props, &scriptInformation[1].props,
636 &scriptInformation[2].props, &scriptInformation[3].props,
637 &scriptInformation[4].props, &scriptInformation[5].props,
638 &scriptInformation[6].props, &scriptInformation[7].props,
639 &scriptInformation[8].props, &scriptInformation[9].props,
640 &scriptInformation[10].props, &scriptInformation[11].props,
641 &scriptInformation[12].props, &scriptInformation[13].props,
642 &scriptInformation[14].props, &scriptInformation[15].props,
643 &scriptInformation[16].props, &scriptInformation[17].props,
644 &scriptInformation[18].props, &scriptInformation[19].props,
645 &scriptInformation[20].props, &scriptInformation[21].props,
646 &scriptInformation[22].props, &scriptInformation[23].props,
647 &scriptInformation[24].props, &scriptInformation[25].props,
648 &scriptInformation[26].props, &scriptInformation[27].props,
649 &scriptInformation[28].props, &scriptInformation[29].props,
650 &scriptInformation[30].props, &scriptInformation[31].props,
651 &scriptInformation[32].props, &scriptInformation[33].props,
652 &scriptInformation[34].props, &scriptInformation[35].props,
653 &scriptInformation[36].props, &scriptInformation[37].props,
654 &scriptInformation[38].props, &scriptInformation[39].props,
655 &scriptInformation[40].props, &scriptInformation[41].props,
656 &scriptInformation[42].props, &scriptInformation[43].props,
657 &scriptInformation[44].props, &scriptInformation[45].props,
658 &scriptInformation[46].props, &scriptInformation[47].props,
659 &scriptInformation[48].props, &scriptInformation[49].props,
660 &scriptInformation[50].props, &scriptInformation[51].props,
661 &scriptInformation[52].props, &scriptInformation[53].props,
662 &scriptInformation[54].props, &scriptInformation[55].props,
663 &scriptInformation[56].props, &scriptInformation[57].props,
664 &scriptInformation[58].props, &scriptInformation[59].props,
665 &scriptInformation[60].props, &scriptInformation[61].props,
666 &scriptInformation[62].props, &scriptInformation[63].props,
667 &scriptInformation[64].props, &scriptInformation[65].props,
668 &scriptInformation[66].props, &scriptInformation[67].props,
669 &scriptInformation[68].props, &scriptInformation[69].props,
670 &scriptInformation[70].props, &scriptInformation[71].props,
671 &scriptInformation[72].props, &scriptInformation[73].props,
672 &scriptInformation[74].props, &scriptInformation[75].props,
673 &scriptInformation[76].props, &scriptInformation[77].props,
674 &scriptInformation[78].props, &scriptInformation[79].props,
675 &scriptInformation[80].props, &scriptInformation[81].props
678 static CRITICAL_SECTION cs_script_cache;
679 static CRITICAL_SECTION_DEBUG cs_script_cache_dbg =
681 0, 0, &cs_script_cache,
682 { &cs_script_cache_dbg.ProcessLocksList, &cs_script_cache_dbg.ProcessLocksList },
683 0, 0, { (DWORD_PTR)(__FILE__ ": script_cache") }
685 static CRITICAL_SECTION cs_script_cache = { &cs_script_cache_dbg, -1, 0, 0, 0, 0 };
686 static struct list script_cache_list = LIST_INIT(script_cache_list);
688 typedef struct {
689 ScriptCache *sc;
690 int numGlyphs;
691 WORD* glyphs;
692 WORD* pwLogClust;
693 int* piAdvance;
694 SCRIPT_VISATTR* psva;
695 GOFFSET* pGoffset;
696 ABC abc;
697 int iMaxPosX;
698 HFONT fallbackFont;
699 } StringGlyphs;
701 enum stringanalysis_flags
703 SCRIPT_STRING_ANALYSIS_FLAGS_SIZE = 0x1,
704 SCRIPT_STRING_ANALYSIS_FLAGS_INVALID = 0x2,
707 typedef struct {
708 HDC hdc;
709 DWORD ssa_flags;
710 DWORD flags;
711 int clip_len;
712 int cItems;
713 int cMaxGlyphs;
714 SCRIPT_ITEM* pItem;
715 int numItems;
716 StringGlyphs* glyphs;
717 SCRIPT_LOGATTR* logattrs;
718 SIZE sz;
719 int* logical2visual;
720 } StringAnalysis;
722 typedef struct {
723 BOOL ascending;
724 WORD target;
725 } FindGlyph_struct;
727 BOOL usp10_array_reserve(void **elements, SIZE_T *capacity, SIZE_T count, SIZE_T size)
729 SIZE_T max_capacity, new_capacity;
730 void *new_elements;
732 if (count <= *capacity)
733 return TRUE;
735 max_capacity = ~(SIZE_T)0 / size;
736 if (count > max_capacity)
737 return FALSE;
739 new_capacity = max(1, *capacity);
740 while (new_capacity < count && new_capacity <= max_capacity / 2)
741 new_capacity *= 2;
742 if (new_capacity < count)
743 new_capacity = count;
745 if (!*elements)
746 new_elements = heap_alloc_zero(new_capacity * size);
747 else
748 new_elements = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *elements, new_capacity * size);
749 if (!new_elements)
750 return FALSE;
752 *elements = new_elements;
753 *capacity = new_capacity;
754 return TRUE;
757 /* TODO Fix font properties on Arabic locale */
758 static inline BOOL set_cache_font_properties(const HDC hdc, ScriptCache *sc)
760 sc->sfp.cBytes = sizeof(sc->sfp);
762 if (!sc->sfnt)
764 sc->sfp.wgBlank = sc->tm.tmBreakChar;
765 sc->sfp.wgDefault = sc->tm.tmDefaultChar;
766 sc->sfp.wgInvalid = sc->sfp.wgBlank;
767 sc->sfp.wgKashida = 0xFFFF;
768 sc->sfp.iKashidaWidth = 0;
770 else
772 static const WCHAR chars[4] = {0x0020, 0x200B, 0xF71B, 0x0640};
773 /* U+0020: numeric space
774 U+200B: zero width space
775 U+F71B: unknown char found by black box testing
776 U+0640: kashida */
777 WORD gi[4];
779 if (GetGlyphIndicesW(hdc, chars, 4, gi, GGI_MARK_NONEXISTING_GLYPHS) != GDI_ERROR)
781 if(gi[0] != 0xFFFF) /* 0xFFFF: index of default non exist char */
782 sc->sfp.wgBlank = gi[0];
783 else
784 sc->sfp.wgBlank = 0;
786 sc->sfp.wgDefault = 0;
788 if (gi[2] != 0xFFFF)
789 sc->sfp.wgInvalid = gi[2];
790 else if (gi[1] != 0xFFFF)
791 sc->sfp.wgInvalid = gi[1];
792 else if (gi[0] != 0xFFFF)
793 sc->sfp.wgInvalid = gi[0];
794 else
795 sc->sfp.wgInvalid = 0;
797 sc->sfp.wgKashida = gi[3];
799 sc->sfp.iKashidaWidth = 0; /* TODO */
801 else
802 return FALSE;
804 return TRUE;
807 static inline void get_cache_font_properties(SCRIPT_FONTPROPERTIES *sfp, ScriptCache *sc)
809 *sfp = sc->sfp;
812 static inline LONG get_cache_height(SCRIPT_CACHE *psc)
814 return ((ScriptCache *)*psc)->tm.tmHeight;
817 static inline BYTE get_cache_pitch_family(SCRIPT_CACHE *psc)
819 return ((ScriptCache *)*psc)->tm.tmPitchAndFamily;
822 static inline WORD get_cache_glyph(SCRIPT_CACHE *psc, DWORD c)
824 CacheGlyphPage *page = ((ScriptCache *)*psc)->page[c / 0x10000];
825 WORD *block;
827 if (!page) return 0;
828 block = page->glyphs[(c % 0x10000) >> GLYPH_BLOCK_SHIFT];
829 if (!block) return 0;
830 return block[(c % 0x10000) & GLYPH_BLOCK_MASK];
833 static inline WORD set_cache_glyph(SCRIPT_CACHE *psc, WCHAR c, WORD glyph)
835 CacheGlyphPage **page = &((ScriptCache *)*psc)->page[c / 0x10000];
836 WORD **block;
837 if (!*page && !(*page = heap_alloc_zero(sizeof(CacheGlyphPage)))) return 0;
839 block = &(*page)->glyphs[(c % 0x10000) >> GLYPH_BLOCK_SHIFT];
840 if (!*block && !(*block = heap_alloc_zero(sizeof(WORD) * GLYPH_BLOCK_SIZE))) return 0;
841 return ((*block)[(c % 0x10000) & GLYPH_BLOCK_MASK] = glyph);
844 static inline BOOL get_cache_glyph_widths(SCRIPT_CACHE *psc, WORD glyph, ABC *abc)
846 static const ABC nil;
847 ABC *block = ((ScriptCache *)*psc)->widths[glyph >> GLYPH_BLOCK_SHIFT];
849 if (!block || !memcmp(&block[glyph & GLYPH_BLOCK_MASK], &nil, sizeof(ABC))) return FALSE;
850 memcpy(abc, &block[glyph & GLYPH_BLOCK_MASK], sizeof(ABC));
851 return TRUE;
854 static inline BOOL set_cache_glyph_widths(SCRIPT_CACHE *psc, WORD glyph, ABC *abc)
856 ABC **block = &((ScriptCache *)*psc)->widths[glyph >> GLYPH_BLOCK_SHIFT];
858 if (!*block && !(*block = heap_alloc_zero(sizeof(ABC) * GLYPH_BLOCK_SIZE))) return FALSE;
859 memcpy(&(*block)[glyph & GLYPH_BLOCK_MASK], abc, sizeof(ABC));
860 return TRUE;
863 static HRESULT init_script_cache(const HDC hdc, SCRIPT_CACHE *psc)
865 ScriptCache *sc;
866 unsigned size;
867 LOGFONTW lf;
869 if (!psc) return E_INVALIDARG;
870 if (*psc) return S_OK;
871 if (!hdc) return E_PENDING;
873 if (!GetObjectW(GetCurrentObject(hdc, OBJ_FONT), sizeof(lf), &lf))
875 return E_INVALIDARG;
877 /* Ensure canonical result by zeroing extra space in lfFaceName */
878 size = lstrlenW(lf.lfFaceName);
879 memset(lf.lfFaceName + size, 0, sizeof(lf.lfFaceName) - size * sizeof(WCHAR));
881 EnterCriticalSection(&cs_script_cache);
882 LIST_FOR_EACH_ENTRY(sc, &script_cache_list, ScriptCache, entry)
884 if (!memcmp(&sc->lf, &lf, sizeof(lf)))
886 sc->refcount++;
887 LeaveCriticalSection(&cs_script_cache);
888 *psc = sc;
889 return S_OK;
892 LeaveCriticalSection(&cs_script_cache);
894 if (!(sc = heap_alloc_zero(sizeof(ScriptCache)))) return E_OUTOFMEMORY;
895 if (!GetTextMetricsW(hdc, &sc->tm))
897 heap_free(sc);
898 return E_INVALIDARG;
900 size = GetOutlineTextMetricsW(hdc, 0, NULL);
901 if (size)
903 sc->otm = heap_alloc(size);
904 sc->otm->otmSize = size;
905 GetOutlineTextMetricsW(hdc, size, sc->otm);
907 sc->sfnt = (GetFontData(hdc, MS_MAKE_TAG('h','e','a','d'), 0, NULL, 0)!=GDI_ERROR);
908 if (!set_cache_font_properties(hdc, sc))
910 heap_free(sc);
911 return E_INVALIDARG;
913 sc->lf = lf;
914 sc->refcount = 1;
915 *psc = sc;
917 EnterCriticalSection(&cs_script_cache);
918 list_add_head(&script_cache_list, &sc->entry);
919 LIST_FOR_EACH_ENTRY(sc, &script_cache_list, ScriptCache, entry)
921 if (sc != *psc && !memcmp(&sc->lf, &lf, sizeof(lf)))
923 /* Another thread won the race. Use their cache instead of ours */
924 list_remove(&sc->entry);
925 sc->refcount++;
926 LeaveCriticalSection(&cs_script_cache);
927 heap_free(*psc);
928 *psc = sc;
929 return S_OK;
932 LeaveCriticalSection(&cs_script_cache);
933 TRACE("<- %p\n", sc);
934 return S_OK;
937 static WCHAR mirror_char( WCHAR ch )
939 extern const WCHAR wine_mirror_map[] DECLSPEC_HIDDEN;
940 return ch + wine_mirror_map[wine_mirror_map[ch >> 8] + (ch & 0xff)];
943 static DWORD decode_surrogate_pair(const WCHAR *str, unsigned int index, unsigned int end)
945 if (index < end-1 && IS_SURROGATE_PAIR(str[index],str[index+1]))
947 DWORD ch = 0x10000 + ((str[index] - 0xd800) << 10) + (str[index+1] - 0xdc00);
948 TRACE("Surrogate Pair %x %x => %x\n",str[index], str[index+1], ch);
949 return ch;
951 return 0;
954 static int __cdecl usp10_compare_script_range(const void *key, const void *value)
956 const struct usp10_script_range *range = value;
957 const DWORD *ch = key;
959 if (*ch < range->rangeFirst)
960 return -1;
961 if (*ch > range->rangeLast)
962 return 1;
963 return 0;
966 static enum usp10_script get_char_script(const WCHAR *str, unsigned int index,
967 unsigned int end, unsigned int *consumed)
969 static const WCHAR latin_punc[] = {'#','$','&','\'',',',';','<','>','?','@','\\','^','_','`','{','|','}','~', 0x00a0, 0};
970 struct usp10_script_range *range;
971 WORD type = 0, type2 = 0;
972 DWORD ch;
974 *consumed = 1;
976 if (str[index] == 0xc || str[index] == 0x20 || str[index] == 0x202f)
977 return Script_CR;
979 /* These punctuation characters are separated out as Latin punctuation */
980 if (wcschr(latin_punc,str[index]))
981 return Script_Punctuation2;
983 /* These chars are itemized as Punctuation by Windows */
984 if (str[index] == 0x2212 || str[index] == 0x2044)
985 return Script_Punctuation;
987 /* Currency Symbols by Unicode point */
988 switch (str[index])
990 case 0x09f2:
991 case 0x09f3: return Script_Bengali_Currency;
992 case 0x0af1: return Script_Gujarati_Currency;
993 case 0x0e3f: return Script_Thai_Currency;
994 case 0x20aa: return Script_Hebrew_Currency;
995 case 0x20ab: return Script_Vietnamese_Currency;
996 case 0xfb29: return Script_Hebrew_Currency;
999 GetStringTypeW(CT_CTYPE1, &str[index], 1, &type);
1000 GetStringTypeW(CT_CTYPE2, &str[index], 1, &type2);
1002 if (type == 0)
1003 return SCRIPT_UNDEFINED;
1005 if (type & C1_CNTRL)
1006 return Script_Control;
1008 ch = decode_surrogate_pair(str, index, end);
1009 if (ch)
1010 *consumed = 2;
1011 else
1012 ch = str[index];
1014 if (!(range = bsearch(&ch, script_ranges, ARRAY_SIZE(script_ranges),
1015 sizeof(*script_ranges), usp10_compare_script_range)))
1016 return (*consumed == 2) ? Script_Surrogates : Script_Undefined;
1018 if (range->numericScript && (type & C1_DIGIT || type2 == C2_ARABICNUMBER))
1019 return range->numericScript;
1020 if (range->punctScript && type & C1_PUNCT)
1021 return range->punctScript;
1022 return range->script;
1025 static int __cdecl compare_FindGlyph(const void *a, const void* b)
1027 const FindGlyph_struct *find = (FindGlyph_struct*)a;
1028 const WORD *idx= (WORD*)b;
1029 int rc = 0;
1031 if ( find->target > *idx)
1032 rc = 1;
1033 else if (find->target < *idx)
1034 rc = -1;
1036 if (!find->ascending)
1037 rc *= -1;
1038 return rc;
1041 int USP10_FindGlyphInLogClust(const WORD* pwLogClust, int cChars, WORD target)
1043 FindGlyph_struct fgs;
1044 WORD *ptr;
1045 INT k;
1047 if (pwLogClust[0] < pwLogClust[cChars-1])
1048 fgs.ascending = TRUE;
1049 else
1050 fgs.ascending = FALSE;
1052 fgs.target = target;
1053 ptr = bsearch(&fgs, pwLogClust, cChars, sizeof(WORD), compare_FindGlyph);
1055 if (!ptr)
1056 return -1;
1058 for (k = (ptr - pwLogClust)-1; k >= 0 && pwLogClust[k] == target; k--)
1060 k++;
1062 return k;
1065 /***********************************************************************
1066 * ScriptFreeCache (USP10.@)
1068 * Free a script cache.
1070 * PARAMS
1071 * psc [I/O] Script cache.
1073 * RETURNS
1074 * Success: S_OK
1075 * Failure: Non-zero HRESULT value.
1077 HRESULT WINAPI ScriptFreeCache(SCRIPT_CACHE *psc)
1079 TRACE("%p\n", psc);
1081 if (psc && *psc)
1083 unsigned int i;
1084 INT n;
1086 EnterCriticalSection(&cs_script_cache);
1087 if (--((ScriptCache *)*psc)->refcount > 0)
1089 LeaveCriticalSection(&cs_script_cache);
1090 *psc = NULL;
1091 return S_OK;
1093 list_remove(&((ScriptCache *)*psc)->entry);
1094 LeaveCriticalSection(&cs_script_cache);
1096 for (i = 0; i < GLYPH_MAX / GLYPH_BLOCK_SIZE; i++)
1098 heap_free(((ScriptCache *)*psc)->widths[i]);
1100 for (i = 0; i < NUM_PAGES; i++)
1102 unsigned int j;
1103 if (((ScriptCache *)*psc)->page[i])
1104 for (j = 0; j < GLYPH_MAX / GLYPH_BLOCK_SIZE; j++)
1105 heap_free(((ScriptCache *)*psc)->page[i]->glyphs[j]);
1106 heap_free(((ScriptCache *)*psc)->page[i]);
1108 heap_free(((ScriptCache *)*psc)->GSUB_Table);
1109 heap_free(((ScriptCache *)*psc)->GDEF_Table);
1110 heap_free(((ScriptCache *)*psc)->CMAP_Table);
1111 heap_free(((ScriptCache *)*psc)->GPOS_Table);
1112 for (n = 0; n < ((ScriptCache *)*psc)->script_count; n++)
1114 int j;
1115 for (j = 0; j < ((ScriptCache *)*psc)->scripts[n].language_count; j++)
1117 int k;
1118 for (k = 0; k < ((ScriptCache *)*psc)->scripts[n].languages[j].feature_count; k++)
1119 heap_free(((ScriptCache *)*psc)->scripts[n].languages[j].features[k].lookups);
1120 heap_free(((ScriptCache *)*psc)->scripts[n].languages[j].features);
1122 for (j = 0; j < ((ScriptCache *)*psc)->scripts[n].default_language.feature_count; j++)
1123 heap_free(((ScriptCache *)*psc)->scripts[n].default_language.features[j].lookups);
1124 heap_free(((ScriptCache *)*psc)->scripts[n].default_language.features);
1125 heap_free(((ScriptCache *)*psc)->scripts[n].languages);
1127 heap_free(((ScriptCache *)*psc)->scripts);
1128 heap_free(((ScriptCache *)*psc)->otm);
1129 heap_free(*psc);
1130 *psc = NULL;
1132 return S_OK;
1135 /***********************************************************************
1136 * ScriptGetProperties (USP10.@)
1138 * Retrieve a list of script properties.
1140 * PARAMS
1141 * props [I] Pointer to an array of SCRIPT_PROPERTIES pointers.
1142 * num [I] Pointer to the number of scripts.
1144 * RETURNS
1145 * Success: S_OK
1146 * Failure: Non-zero HRESULT value.
1148 * NOTES
1149 * Behaviour matches WinXP.
1151 HRESULT WINAPI ScriptGetProperties(const SCRIPT_PROPERTIES ***props, int *num)
1153 TRACE("(%p,%p)\n", props, num);
1155 if (!props && !num) return E_INVALIDARG;
1157 if (num) *num = ARRAY_SIZE(script_props);
1158 if (props) *props = script_props;
1160 return S_OK;
1163 /***********************************************************************
1164 * ScriptGetFontProperties (USP10.@)
1166 * Get information on special glyphs.
1168 * PARAMS
1169 * hdc [I] Device context.
1170 * psc [I/O] Opaque pointer to a script cache.
1171 * sfp [O] Font properties structure.
1173 HRESULT WINAPI ScriptGetFontProperties(HDC hdc, SCRIPT_CACHE *psc, SCRIPT_FONTPROPERTIES *sfp)
1175 HRESULT hr;
1177 TRACE("%p,%p,%p\n", hdc, psc, sfp);
1179 if (!sfp) return E_INVALIDARG;
1180 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
1182 if (sfp->cBytes != sizeof(SCRIPT_FONTPROPERTIES))
1183 return E_INVALIDARG;
1185 get_cache_font_properties(sfp, *psc);
1187 return S_OK;
1190 /***********************************************************************
1191 * ScriptRecordDigitSubstitution (USP10.@)
1193 * Record digit substitution settings for a given locale.
1195 * PARAMS
1196 * locale [I] Locale identifier.
1197 * sds [I] Structure to record substitution settings.
1199 * RETURNS
1200 * Success: S_OK
1201 * Failure: E_POINTER if sds is NULL, E_INVALIDARG otherwise.
1203 * SEE ALSO
1204 * http://blogs.msdn.com/michkap/archive/2006/02/22/536877.aspx
1206 HRESULT WINAPI ScriptRecordDigitSubstitution(LCID locale, SCRIPT_DIGITSUBSTITUTE *sds)
1208 DWORD plgid, sub;
1210 TRACE("0x%x, %p\n", locale, sds);
1212 /* This implementation appears to be correct for all languages, but it's
1213 * not clear if sds->DigitSubstitute is ever set to anything except
1214 * CONTEXT or NONE in reality */
1216 if (!sds) return E_POINTER;
1218 locale = ConvertDefaultLocale(locale);
1220 if (!IsValidLocale(locale, LCID_INSTALLED))
1221 return E_INVALIDARG;
1223 plgid = PRIMARYLANGID(LANGIDFROMLCID(locale));
1224 sds->TraditionalDigitLanguage = plgid;
1226 if (plgid == LANG_ARABIC || plgid == LANG_FARSI)
1227 sds->NationalDigitLanguage = plgid;
1228 else
1229 sds->NationalDigitLanguage = LANG_ENGLISH;
1231 if (!GetLocaleInfoW(locale, LOCALE_IDIGITSUBSTITUTION | LOCALE_RETURN_NUMBER,
1232 (WCHAR *)&sub, sizeof(sub) / sizeof(WCHAR)))
1233 return E_INVALIDARG;
1235 switch (sub)
1237 case 0:
1238 if (plgid == LANG_ARABIC || plgid == LANG_FARSI)
1239 sds->DigitSubstitute = SCRIPT_DIGITSUBSTITUTE_CONTEXT;
1240 else
1241 sds->DigitSubstitute = SCRIPT_DIGITSUBSTITUTE_NONE;
1242 break;
1243 case 1:
1244 sds->DigitSubstitute = SCRIPT_DIGITSUBSTITUTE_NONE;
1245 break;
1246 case 2:
1247 sds->DigitSubstitute = SCRIPT_DIGITSUBSTITUTE_NATIONAL;
1248 break;
1249 default:
1250 sds->DigitSubstitute = SCRIPT_DIGITSUBSTITUTE_TRADITIONAL;
1251 break;
1254 sds->dwReserved = 0;
1255 return S_OK;
1258 /***********************************************************************
1259 * ScriptApplyDigitSubstitution (USP10.@)
1261 * Apply digit substitution settings.
1263 * PARAMS
1264 * sds [I] Structure with recorded substitution settings.
1265 * sc [I] Script control structure.
1266 * ss [I] Script state structure.
1268 * RETURNS
1269 * Success: S_OK
1270 * Failure: E_INVALIDARG if sds is invalid. Otherwise an HRESULT.
1272 HRESULT WINAPI ScriptApplyDigitSubstitution(const SCRIPT_DIGITSUBSTITUTE *sds,
1273 SCRIPT_CONTROL *sc, SCRIPT_STATE *ss)
1275 SCRIPT_DIGITSUBSTITUTE psds;
1277 TRACE("%p, %p, %p\n", sds, sc, ss);
1279 if (!sc || !ss) return E_POINTER;
1280 if (!sds)
1282 sds = &psds;
1283 if (ScriptRecordDigitSubstitution(LOCALE_USER_DEFAULT, &psds) != S_OK)
1284 return E_INVALIDARG;
1287 sc->uDefaultLanguage = LANG_ENGLISH;
1288 sc->fContextDigits = 0;
1289 ss->fDigitSubstitute = 0;
1291 switch (sds->DigitSubstitute) {
1292 case SCRIPT_DIGITSUBSTITUTE_CONTEXT:
1293 case SCRIPT_DIGITSUBSTITUTE_NATIONAL:
1294 case SCRIPT_DIGITSUBSTITUTE_NONE:
1295 case SCRIPT_DIGITSUBSTITUTE_TRADITIONAL:
1296 return S_OK;
1297 default:
1298 return E_INVALIDARG;
1302 static inline BOOL is_indic(enum usp10_script script)
1304 return (script >= Script_Devanagari && script <= Script_Malayalam_Numeric);
1307 static inline enum usp10_script base_indic(enum usp10_script script)
1309 switch (script)
1311 case Script_Devanagari:
1312 case Script_Devanagari_Numeric: return Script_Devanagari;
1313 case Script_Bengali:
1314 case Script_Bengali_Numeric:
1315 case Script_Bengali_Currency: return Script_Bengali;
1316 case Script_Gurmukhi:
1317 case Script_Gurmukhi_Numeric: return Script_Gurmukhi;
1318 case Script_Gujarati:
1319 case Script_Gujarati_Numeric:
1320 case Script_Gujarati_Currency: return Script_Gujarati;
1321 case Script_Oriya:
1322 case Script_Oriya_Numeric: return Script_Oriya;
1323 case Script_Tamil:
1324 case Script_Tamil_Numeric: return Script_Tamil;
1325 case Script_Telugu:
1326 case Script_Telugu_Numeric: return Script_Telugu;
1327 case Script_Kannada:
1328 case Script_Kannada_Numeric: return Script_Kannada;
1329 case Script_Malayalam:
1330 case Script_Malayalam_Numeric: return Script_Malayalam;
1331 default:
1332 return Script_Undefined;
1336 static BOOL script_is_numeric(enum usp10_script script)
1338 return scriptInformation[script].props.fNumeric;
1341 static HRESULT _ItemizeInternal(const WCHAR *pwcInChars, int cInChars,
1342 int cMaxItems, const SCRIPT_CONTROL *psControl,
1343 const SCRIPT_STATE *psState, SCRIPT_ITEM *pItems,
1344 OPENTYPE_TAG *pScriptTags, int *pcItems)
1347 #define Numeric_space 0x0020
1348 #define ZWSP 0x200B
1349 #define ZWNJ 0x200C
1350 #define ZWJ 0x200D
1352 enum usp10_script last_indic = Script_Undefined;
1353 int cnt = 0, index = 0, str = 0;
1354 enum usp10_script New_Script = -1;
1355 int i;
1356 WORD *levels = NULL;
1357 WORD *layout_levels = NULL;
1358 WORD *overrides = NULL;
1359 WORD *strength = NULL;
1360 enum usp10_script *scripts;
1361 WORD baselevel = 0;
1362 WORD baselayout = 0;
1363 BOOL new_run;
1364 WORD layoutRTL = 0;
1365 BOOL forceLevels = FALSE;
1366 unsigned int consumed = 0;
1367 HRESULT res = E_OUTOFMEMORY;
1369 TRACE("%s,%d,%d,%p,%p,%p,%p\n", debugstr_wn(pwcInChars, cInChars), cInChars, cMaxItems,
1370 psControl, psState, pItems, pcItems);
1372 if (!pwcInChars || !cInChars || !pItems || cMaxItems < 2)
1373 return E_INVALIDARG;
1375 if (!(scripts = heap_calloc(cInChars, sizeof(*scripts))))
1376 return E_OUTOFMEMORY;
1378 for (i = 0; i < cInChars; i++)
1380 if (!consumed)
1382 scripts[i] = get_char_script(pwcInChars,i,cInChars,&consumed);
1383 consumed --;
1385 else
1387 scripts[i] = scripts[i-1];
1388 consumed --;
1390 /* Devanagari danda (U+0964) and double danda (U+0965) are used for
1391 all Indic scripts */
1392 if ((pwcInChars[i] == 0x964 || pwcInChars[i] ==0x965) && last_indic != Script_Undefined)
1393 scripts[i] = last_indic;
1394 else if (is_indic(scripts[i]))
1395 last_indic = base_indic(scripts[i]);
1397 /* Some unicode points :
1398 (Zero Width Space U+200B - Right-to-Left Mark U+200F)
1399 (Left Right Embed U+202A - Left Right Override U+202D)
1400 (Left Right Isolate U+2066 - Pop Directional Isolate U+2069)
1401 will force us into bidi mode */
1402 if (!forceLevels && ((pwcInChars[i] >= 0x200B && pwcInChars[i] <= 0x200F) ||
1403 (pwcInChars[i] >= 0x202A && pwcInChars[i] <= 0x202E) ||
1404 (pwcInChars[i] >= 0x2066 && pwcInChars[i] <= 0x2069)))
1406 forceLevels = TRUE;
1408 /* Diacritical marks merge with other scripts */
1409 if (scripts[i] == Script_Diacritical)
1411 if (i > 0)
1413 if (pScriptTags)
1414 scripts[i] = scripts[i-1];
1415 else
1417 int j;
1418 BOOL asian = FALSE;
1419 enum usp10_script first_script = scripts[i-1];
1420 for (j = i-1; j >= 0 && scripts[j] == first_script && pwcInChars[j] != Numeric_space; j--)
1422 enum usp10_script original = scripts[j];
1423 if (original == Script_Ideograph || original == Script_Kana || original == Script_Yi || original == Script_CJK_Han || original == Script_Bopomofo)
1425 asian = TRUE;
1426 break;
1428 if (original != Script_MathAlpha && scriptInformation[scripts[j]].props.fComplex)
1429 break;
1430 scripts[j] = scripts[i];
1431 if (original == Script_Punctuation2)
1432 break;
1434 if (j >= 0 && (scriptInformation[scripts[j]].props.fComplex || asian))
1435 scripts[i] = scripts[j];
1441 for (i = 0; i < cInChars; i++)
1443 /* Joiners get merged preferencially right */
1444 if (i > 0 && (pwcInChars[i] == ZWJ || pwcInChars[i] == ZWNJ || pwcInChars[i] == ZWSP))
1446 int j;
1447 if (i+1 == cInChars)
1448 scripts[i] = scripts[i-1];
1449 else
1451 for (j = i+1; j < cInChars; j++)
1453 if (pwcInChars[j] != ZWJ && pwcInChars[j] != ZWNJ
1454 && pwcInChars[j] != ZWSP && pwcInChars[j] != Numeric_space)
1456 scripts[i] = scripts[j];
1457 break;
1464 if (psState && psControl)
1466 if (!(levels = heap_calloc(cInChars, sizeof(*levels))))
1467 goto nomemory;
1469 if (!(overrides = heap_calloc(cInChars, sizeof(*overrides))))
1470 goto nomemory;
1472 if (!(layout_levels = heap_calloc(cInChars, sizeof(*layout_levels))))
1473 goto nomemory;
1475 if (psState->fOverrideDirection)
1477 if (!forceLevels)
1479 SCRIPT_STATE s = *psState;
1480 s.fOverrideDirection = FALSE;
1481 BIDI_DetermineLevels(pwcInChars, cInChars, &s, psControl, layout_levels, overrides);
1482 if (odd(layout_levels[0]))
1483 forceLevels = TRUE;
1484 else for (i = 0; i < cInChars; i++)
1485 if (layout_levels[i]!=layout_levels[0])
1487 forceLevels = TRUE;
1488 break;
1492 BIDI_DetermineLevels(pwcInChars, cInChars, psState, psControl, levels, overrides);
1494 else
1496 BIDI_DetermineLevels(pwcInChars, cInChars, psState, psControl, levels, overrides);
1497 memcpy(layout_levels, levels, cInChars * sizeof(WORD));
1499 baselevel = levels[0];
1500 baselayout = layout_levels[0];
1501 for (i = 0; i < cInChars; i++)
1502 if (levels[i]!=levels[0])
1503 break;
1504 if (i >= cInChars && !odd(baselevel) && !odd(psState->uBidiLevel) && !forceLevels)
1506 heap_free(levels);
1507 heap_free(overrides);
1508 heap_free(layout_levels);
1509 overrides = NULL;
1510 levels = NULL;
1511 layout_levels = NULL;
1513 else
1515 static const WCHAR math_punc[] = {'#','$','%','+',',','-','.','/',':',0x2212, 0x2044, 0x00a0,0};
1516 static const WCHAR repeatable_math_punc[] = {'#','$','%','+','-','/',0x2212, 0x2044,0};
1518 if (!(strength = heap_calloc(cInChars, sizeof(*strength))))
1519 goto nomemory;
1520 BIDI_GetStrengths(pwcInChars, cInChars, psControl, strength);
1522 /* We currently mis-level leading Diacriticals */
1523 if (scripts[0] == Script_Diacritical)
1524 for (i = 0; i < cInChars && scripts[0] == Script_Diacritical; i++)
1526 levels[i] = odd(levels[i])?levels[i]+1:levels[i];
1527 strength[i] = BIDI_STRONG;
1530 /* Math punctuation bordered on both sides by numbers can be
1531 merged into the number */
1532 for (i = 0; i < cInChars; i++)
1534 if (i > 0 && i < cInChars-1 &&
1535 script_is_numeric(scripts[i-1]) &&
1536 wcschr(math_punc, pwcInChars[i]))
1538 if (script_is_numeric(scripts[i+1]))
1540 scripts[i] = scripts[i+1];
1541 levels[i] = levels[i-1];
1542 strength[i] = strength[i-1];
1543 i++;
1545 else if (wcschr(repeatable_math_punc, pwcInChars[i]))
1547 int j;
1548 for (j = i+1; j < cInChars; j++)
1550 if (script_is_numeric(scripts[j]))
1552 for(;i<j; i++)
1554 scripts[i] = scripts[j];
1555 levels[i] = levels[i-1];
1556 strength[i] = strength[i-1];
1559 else if (pwcInChars[i] != pwcInChars[j]) break;
1565 for (i = 0; i < cInChars; i++)
1567 /* Numerics at level 0 get bumped to level 2 */
1568 if (!overrides[i] && (levels[i] == 0 || (odd(psState->uBidiLevel)
1569 && levels[i] == psState->uBidiLevel + 1)) && script_is_numeric(scripts[i]))
1571 levels[i] = 2;
1574 /* Joiners get merged preferencially right */
1575 if (i > 0 && (pwcInChars[i] == ZWJ || pwcInChars[i] == ZWNJ || pwcInChars[i] == ZWSP))
1577 int j;
1578 if (i+1 == cInChars && levels[i-1] == levels[i])
1579 strength[i] = strength[i-1];
1580 else
1581 for (j = i+1; j < cInChars && levels[i] == levels[j]; j++)
1582 if (pwcInChars[j] != ZWJ && pwcInChars[j] != ZWNJ
1583 && pwcInChars[j] != ZWSP && pwcInChars[j] != Numeric_space)
1585 strength[i] = strength[j];
1586 break;
1590 if (psControl->fMergeNeutralItems)
1592 /* Merge the neutrals */
1593 for (i = 0; i < cInChars; i++)
1595 if (strength[i] == BIDI_NEUTRAL || strength[i] == BIDI_WEAK)
1597 int j;
1598 for (j = i; j > 0; j--)
1600 if (levels[i] != levels[j])
1601 break;
1602 if ((strength[j] == BIDI_STRONG) || (strength[i] == BIDI_NEUTRAL && strength[j] == BIDI_WEAK))
1604 scripts[i] = scripts[j];
1605 strength[i] = strength[j];
1606 break;
1610 /* Try going the other way */
1611 if (strength[i] == BIDI_NEUTRAL || strength[i] == BIDI_WEAK)
1613 int j;
1614 for (j = i; j < cInChars; j++)
1616 if (levels[i] != levels[j])
1617 break;
1618 if ((strength[j] == BIDI_STRONG) || (strength[i] == BIDI_NEUTRAL && strength[j] == BIDI_WEAK))
1620 scripts[i] = scripts[j];
1621 strength[i] = strength[j];
1622 break;
1631 while ((!levels || (levels && cnt+1 < cInChars && levels[cnt+1] == levels[0]))
1632 && (cnt < cInChars && pwcInChars[cnt] == Numeric_space))
1633 cnt++;
1635 if (cnt == cInChars) /* All Spaces */
1637 cnt = 0;
1638 New_Script = scripts[cnt];
1641 pItems[index].iCharPos = 0;
1642 pItems[index].a = scriptInformation[scripts[cnt]].a;
1643 if (pScriptTags)
1644 pScriptTags[index] = scriptInformation[scripts[cnt]].scriptTag;
1646 if (strength && strength[cnt] == BIDI_STRONG)
1647 str = strength[cnt];
1648 else if (strength)
1649 str = strength[0];
1651 cnt = 0;
1653 if (levels)
1655 if (strength[cnt] == BIDI_STRONG)
1656 layoutRTL = odd(layout_levels[cnt]);
1657 else
1658 layoutRTL = (psState->uBidiLevel || odd(layout_levels[cnt]));
1659 if (overrides)
1660 pItems[index].a.s.fOverrideDirection = (overrides[cnt] != 0);
1661 pItems[index].a.fRTL = odd(levels[cnt]);
1662 if (script_is_numeric(pItems[index].a.eScript))
1663 pItems[index].a.fLayoutRTL = layoutRTL;
1664 else
1665 pItems[index].a.fLayoutRTL = pItems[index].a.fRTL;
1666 pItems[index].a.s.uBidiLevel = levels[cnt];
1668 else if (!pItems[index].a.s.uBidiLevel || (overrides && overrides[cnt]))
1670 if (pItems[index].a.s.uBidiLevel != baselevel)
1671 pItems[index].a.s.fOverrideDirection = TRUE;
1672 layoutRTL = odd(baselayout);
1673 pItems[index].a.s.uBidiLevel = baselevel;
1674 pItems[index].a.fRTL = odd(baselevel);
1675 if (script_is_numeric(pItems[index].a.eScript))
1676 pItems[index].a.fLayoutRTL = odd(baselayout);
1677 else
1678 pItems[index].a.fLayoutRTL = pItems[index].a.fRTL;
1681 TRACE("New_Level=%i New_Strength=%i New_Script=%d, eScript=%d index=%d cnt=%d iCharPos=%d\n",
1682 levels?levels[cnt]:-1, str, New_Script, pItems[index].a.eScript, index, cnt,
1683 pItems[index].iCharPos);
1685 for (cnt=1; cnt < cInChars; cnt++)
1687 if(pwcInChars[cnt] != Numeric_space)
1688 New_Script = scripts[cnt];
1689 else if (levels)
1691 int j = 1;
1692 while (cnt + j < cInChars - 1 && pwcInChars[cnt+j] == Numeric_space && levels[cnt] == levels[cnt+j])
1693 j++;
1694 if (cnt + j < cInChars && levels[cnt] == levels[cnt+j])
1695 New_Script = scripts[cnt+j];
1696 else
1697 New_Script = scripts[cnt];
1700 new_run = FALSE;
1701 /* merge space strengths*/
1702 if (strength && strength[cnt] == BIDI_STRONG && str != BIDI_STRONG && New_Script == pItems[index].a.eScript)
1703 str = BIDI_STRONG;
1705 if (strength && strength[cnt] == BIDI_NEUTRAL && str == BIDI_STRONG && pwcInChars[cnt] != Numeric_space && New_Script == pItems[index].a.eScript)
1706 str = BIDI_NEUTRAL;
1708 /* changes in level */
1709 if (levels && (levels[cnt] != pItems[index].a.s.uBidiLevel))
1711 TRACE("Level break(%i/%i)\n",pItems[index].a.s.uBidiLevel,levels[cnt]);
1712 new_run = TRUE;
1714 /* changes in strength */
1715 else if (strength && pwcInChars[cnt] != Numeric_space && str != strength[cnt])
1717 TRACE("Strength break (%i/%i)\n",str,strength[cnt]);
1718 new_run = TRUE;
1720 /* changes in script */
1721 else if (((pwcInChars[cnt] != Numeric_space) && (New_Script != -1) && (New_Script != pItems[index].a.eScript)) || (New_Script == Script_Control))
1723 TRACE("Script break(%i/%i)\n",pItems[index].a.eScript,New_Script);
1724 new_run = TRUE;
1727 if (!new_run && strength && str == BIDI_STRONG)
1729 layoutRTL = odd(layout_levels[cnt]);
1730 if (script_is_numeric(pItems[index].a.eScript))
1731 pItems[index].a.fLayoutRTL = layoutRTL;
1734 if (new_run)
1736 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);
1738 index++;
1739 if (index+1 > cMaxItems)
1740 goto nomemory;
1742 if (strength)
1743 str = strength[cnt];
1745 pItems[index].iCharPos = cnt;
1746 memset(&pItems[index].a, 0, sizeof(SCRIPT_ANALYSIS));
1748 pItems[index].a = scriptInformation[New_Script].a;
1749 if (pScriptTags)
1750 pScriptTags[index] = scriptInformation[New_Script].scriptTag;
1751 if (levels)
1753 if (overrides)
1754 pItems[index].a.s.fOverrideDirection = (overrides[cnt] != 0);
1755 if (layout_levels[cnt] == 0)
1756 layoutRTL = 0;
1757 else
1758 layoutRTL = (layoutRTL || odd(layout_levels[cnt]));
1759 pItems[index].a.fRTL = odd(levels[cnt]);
1760 if (script_is_numeric(pItems[index].a.eScript))
1761 pItems[index].a.fLayoutRTL = layoutRTL;
1762 else
1763 pItems[index].a.fLayoutRTL = pItems[index].a.fRTL;
1764 pItems[index].a.s.uBidiLevel = levels[cnt];
1766 else if (!pItems[index].a.s.uBidiLevel || (overrides && overrides[cnt]))
1768 if (pItems[index].a.s.uBidiLevel != baselevel)
1769 pItems[index].a.s.fOverrideDirection = TRUE;
1770 pItems[index].a.s.uBidiLevel = baselevel;
1771 pItems[index].a.fRTL = odd(baselevel);
1772 if (script_is_numeric(pItems[index].a.eScript))
1773 pItems[index].a.fLayoutRTL = layoutRTL;
1774 else
1775 pItems[index].a.fLayoutRTL = pItems[index].a.fRTL;
1778 TRACE("index=%d cnt=%d iCharPos=%d\n", index, cnt, pItems[index].iCharPos);
1782 /* While not strictly necessary according to the spec, make sure the n+1
1783 * item is set up to prevent random behaviour if the caller erroneously
1784 * checks the n+1 structure */
1785 index++;
1786 if (index + 1 > cMaxItems) goto nomemory;
1787 memset(&pItems[index].a, 0, sizeof(SCRIPT_ANALYSIS));
1789 TRACE("index=%d cnt=%d iCharPos=%d\n", index, cnt, pItems[index].iCharPos);
1791 /* Set one SCRIPT_STATE item being returned */
1792 if (pcItems) *pcItems = index;
1794 /* Set SCRIPT_ITEM */
1795 pItems[index].iCharPos = cnt; /* the last item contains the ptr to the lastchar */
1796 res = S_OK;
1797 nomemory:
1798 heap_free(levels);
1799 heap_free(overrides);
1800 heap_free(layout_levels);
1801 heap_free(strength);
1802 heap_free(scripts);
1803 return res;
1806 /***********************************************************************
1807 * ScriptItemizeOpenType (USP10.@)
1809 * Split a Unicode string into shapeable parts.
1811 * PARAMS
1812 * pwcInChars [I] String to split.
1813 * cInChars [I] Number of characters in pwcInChars.
1814 * cMaxItems [I] Maximum number of items to return.
1815 * psControl [I] Pointer to a SCRIPT_CONTROL structure.
1816 * psState [I] Pointer to a SCRIPT_STATE structure.
1817 * pItems [O] Buffer to receive SCRIPT_ITEM structures.
1818 * pScriptTags [O] Buffer to receive OPENTYPE_TAGs.
1819 * pcItems [O] Number of script items returned.
1821 * RETURNS
1822 * Success: S_OK
1823 * Failure: Non-zero HRESULT value.
1825 HRESULT WINAPI ScriptItemizeOpenType(const WCHAR *pwcInChars, int cInChars, int cMaxItems,
1826 const SCRIPT_CONTROL *psControl, const SCRIPT_STATE *psState,
1827 SCRIPT_ITEM *pItems, OPENTYPE_TAG *pScriptTags, int *pcItems)
1829 return _ItemizeInternal(pwcInChars, cInChars, cMaxItems, psControl, psState, pItems, pScriptTags, pcItems);
1832 /***********************************************************************
1833 * ScriptItemize (USP10.@)
1835 * Split a Unicode string into shapeable parts.
1837 * PARAMS
1838 * pwcInChars [I] String to split.
1839 * cInChars [I] Number of characters in pwcInChars.
1840 * cMaxItems [I] Maximum number of items to return.
1841 * psControl [I] Pointer to a SCRIPT_CONTROL structure.
1842 * psState [I] Pointer to a SCRIPT_STATE structure.
1843 * pItems [O] Buffer to receive SCRIPT_ITEM structures.
1844 * pcItems [O] Number of script items returned.
1846 * RETURNS
1847 * Success: S_OK
1848 * Failure: Non-zero HRESULT value.
1850 HRESULT WINAPI ScriptItemize(const WCHAR *pwcInChars, int cInChars, int cMaxItems,
1851 const SCRIPT_CONTROL *psControl, const SCRIPT_STATE *psState,
1852 SCRIPT_ITEM *pItems, int *pcItems)
1854 return _ItemizeInternal(pwcInChars, cInChars, cMaxItems, psControl, psState, pItems, NULL, pcItems);
1857 static inline int getGivenTabWidth(ScriptCache *psc, SCRIPT_TABDEF *pTabdef, int charPos, int current_x)
1859 int defWidth;
1860 int cTabStops=0;
1861 INT *lpTabPos = NULL;
1862 INT nTabOrg = 0;
1863 INT x = 0;
1865 if (pTabdef)
1866 lpTabPos = pTabdef->pTabStops;
1868 if (pTabdef && pTabdef->iTabOrigin)
1870 if (pTabdef->iScale)
1871 nTabOrg = (pTabdef->iTabOrigin * pTabdef->iScale)/4;
1872 else
1873 nTabOrg = pTabdef->iTabOrigin * psc->tm.tmAveCharWidth;
1876 if (pTabdef)
1877 cTabStops = pTabdef->cTabStops;
1879 if (cTabStops == 1)
1881 if (pTabdef->iScale)
1882 defWidth = ((pTabdef->pTabStops[0])*pTabdef->iScale) / 4;
1883 else
1884 defWidth = (pTabdef->pTabStops[0])*psc->tm.tmAveCharWidth;
1885 cTabStops = 0;
1887 else
1889 if (pTabdef->iScale)
1890 defWidth = (32 * pTabdef->iScale) / 4;
1891 else
1892 defWidth = 8 * psc->tm.tmAveCharWidth;
1895 for (; cTabStops>0 ; lpTabPos++, cTabStops--)
1897 int position = *lpTabPos;
1898 if (position < 0)
1899 position = -1 * position;
1900 if (pTabdef->iScale)
1901 position = (position * pTabdef->iScale) / 4;
1902 else
1903 position = position * psc->tm.tmAveCharWidth;
1905 if( nTabOrg + position > current_x)
1907 if( position >= 0)
1909 /* a left aligned tab */
1910 x = (nTabOrg + position) - current_x;
1911 break;
1913 else
1915 FIXME("Negative tabstop\n");
1916 break;
1920 if ((!cTabStops) && (defWidth > 0))
1921 x =((((current_x - nTabOrg) / defWidth)+1) * defWidth) - current_x;
1922 else if ((!cTabStops) && (defWidth < 0))
1923 FIXME("TODO: Negative defWidth\n");
1925 return x;
1928 /***********************************************************************
1929 * Helper function for ScriptStringAnalyse
1931 static BOOL requires_fallback(HDC hdc, SCRIPT_CACHE *psc, SCRIPT_ANALYSIS *psa,
1932 const WCHAR *pwcInChars, int cChars )
1934 /* FIXME: When to properly fallback is still a bit of a mystery */
1935 WORD *glyphs;
1937 if (psa->fNoGlyphIndex)
1938 return FALSE;
1940 if (init_script_cache(hdc, psc) != S_OK)
1941 return FALSE;
1943 if (SHAPE_CheckFontForRequiredFeatures(hdc, (ScriptCache *)*psc, psa) != S_OK)
1944 return TRUE;
1946 if (!(glyphs = heap_calloc(cChars, sizeof(*glyphs))))
1947 return FALSE;
1948 if (ScriptGetCMap(hdc, psc, pwcInChars, cChars, 0, glyphs) != S_OK)
1950 heap_free(glyphs);
1951 return TRUE;
1953 heap_free(glyphs);
1955 return FALSE;
1958 static void find_fallback_font(enum usp10_script scriptid, WCHAR *FaceName)
1960 HKEY hkey;
1962 if (!RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Uniscribe\\Fallback", &hkey))
1964 static const WCHAR szFmt[] = {'%','x',0};
1965 WCHAR value[10];
1966 DWORD count = LF_FACESIZE * sizeof(WCHAR);
1967 DWORD type;
1969 swprintf(value, ARRAY_SIZE(value), szFmt, scriptInformation[scriptid].scriptTag);
1970 if (RegQueryValueExW(hkey, value, 0, &type, (BYTE *)FaceName, &count))
1971 lstrcpyW(FaceName,scriptInformation[scriptid].fallbackFont);
1972 RegCloseKey(hkey);
1974 else
1975 lstrcpyW(FaceName,scriptInformation[scriptid].fallbackFont);
1978 /***********************************************************************
1979 * ScriptStringAnalyse (USP10.@)
1982 HRESULT WINAPI ScriptStringAnalyse(HDC hdc, const void *pString, int cString,
1983 int cGlyphs, int iCharset, DWORD dwFlags,
1984 int iReqWidth, SCRIPT_CONTROL *psControl,
1985 SCRIPT_STATE *psState, const int *piDx,
1986 SCRIPT_TABDEF *pTabdef, const BYTE *pbInClass,
1987 SCRIPT_STRING_ANALYSIS *pssa)
1989 HRESULT hr = E_OUTOFMEMORY;
1990 StringAnalysis *analysis = NULL;
1991 SCRIPT_CONTROL sControl;
1992 SCRIPT_STATE sState;
1993 int i, num_items = 255;
1994 BYTE *BidiLevel;
1995 WCHAR *iString = NULL;
1997 TRACE("(%p,%p,%d,%d,%d,0x%x,%d,%p,%p,%p,%p,%p,%p)\n",
1998 hdc, pString, cString, cGlyphs, iCharset, dwFlags, iReqWidth,
1999 psControl, psState, piDx, pTabdef, pbInClass, pssa);
2001 if (iCharset != -1)
2003 FIXME("Only Unicode strings are supported\n");
2004 return E_INVALIDARG;
2006 if (cString < 1 || !pString) return E_INVALIDARG;
2007 if ((dwFlags & SSA_GLYPHS) && !hdc) return E_PENDING;
2009 if (!(analysis = heap_alloc_zero(sizeof(*analysis))))
2010 return E_OUTOFMEMORY;
2011 if (!(analysis->pItem = heap_calloc(num_items + 1, sizeof(*analysis->pItem))))
2012 goto error;
2014 /* FIXME: handle clipping */
2015 analysis->clip_len = cString;
2016 analysis->hdc = hdc;
2017 analysis->ssa_flags = dwFlags;
2019 if (psState)
2020 sState = *psState;
2021 else
2022 memset(&sState, 0, sizeof(SCRIPT_STATE));
2024 if (psControl)
2025 sControl = *psControl;
2026 else
2027 memset(&sControl, 0, sizeof(SCRIPT_CONTROL));
2029 if (dwFlags & SSA_PASSWORD)
2031 if (!(iString = heap_calloc(cString, sizeof(*iString))))
2033 hr = E_OUTOFMEMORY;
2034 goto error;
2036 for (i = 0; i < cString; i++)
2037 iString[i] = *((const WCHAR *)pString);
2038 pString = iString;
2041 hr = ScriptItemize(pString, cString, num_items, &sControl, &sState, analysis->pItem,
2042 &analysis->numItems);
2044 if (FAILED(hr))
2046 if (hr == E_OUTOFMEMORY)
2047 hr = E_INVALIDARG;
2048 goto error;
2051 /* set back to out of memory for default goto error behaviour */
2052 hr = E_OUTOFMEMORY;
2054 if (dwFlags & SSA_BREAK)
2056 if (!(analysis->logattrs = heap_calloc(cString, sizeof(*analysis->logattrs))))
2057 goto error;
2059 for (i = 0; i < analysis->numItems; ++i)
2060 ScriptBreak(&((const WCHAR *)pString)[analysis->pItem[i].iCharPos],
2061 analysis->pItem[i + 1].iCharPos - analysis->pItem[i].iCharPos,
2062 &analysis->pItem[i].a, &analysis->logattrs[analysis->pItem[i].iCharPos]);
2065 if (!(analysis->logical2visual = heap_calloc(analysis->numItems, sizeof(*analysis->logical2visual))))
2066 goto error;
2067 if (!(BidiLevel = heap_alloc_zero(analysis->numItems)))
2068 goto error;
2070 if (dwFlags & SSA_GLYPHS)
2072 int tab_x = 0;
2074 if (!(analysis->glyphs = heap_calloc(analysis->numItems, sizeof(*analysis->glyphs))))
2076 heap_free(BidiLevel);
2077 goto error;
2080 for (i = 0; i < analysis->numItems; i++)
2082 SCRIPT_CACHE *sc = (SCRIPT_CACHE*)&analysis->glyphs[i].sc;
2083 int cChar = analysis->pItem[i+1].iCharPos - analysis->pItem[i].iCharPos;
2084 int numGlyphs = 1.5 * cChar + 16;
2085 WORD *glyphs = heap_calloc(numGlyphs, sizeof(*glyphs));
2086 WORD *pwLogClust = heap_calloc(cChar, sizeof(*pwLogClust));
2087 int *piAdvance = heap_calloc(numGlyphs, sizeof(*piAdvance));
2088 SCRIPT_VISATTR *psva = heap_calloc(numGlyphs, sizeof(*psva));
2089 GOFFSET *pGoffset = heap_calloc(numGlyphs, sizeof(*pGoffset));
2090 int numGlyphsReturned;
2091 HFONT originalFont = 0x0;
2093 /* FIXME: non unicode strings */
2094 const WCHAR* pStr = (const WCHAR*)pString;
2095 analysis->glyphs[i].fallbackFont = NULL;
2097 if (!glyphs || !pwLogClust || !piAdvance || !psva || !pGoffset)
2099 heap_free (BidiLevel);
2100 heap_free (glyphs);
2101 heap_free (pwLogClust);
2102 heap_free (piAdvance);
2103 heap_free (psva);
2104 heap_free (pGoffset);
2105 hr = E_OUTOFMEMORY;
2106 goto error;
2109 if ((dwFlags & SSA_FALLBACK) && requires_fallback(hdc, sc, &analysis->pItem[i].a, &pStr[analysis->pItem[i].iCharPos], cChar))
2111 LOGFONTW lf;
2112 GetObjectW(GetCurrentObject(hdc, OBJ_FONT), sizeof(lf), & lf);
2113 lf.lfCharSet = scriptInformation[analysis->pItem[i].a.eScript].props.bCharSet;
2114 lf.lfFaceName[0] = 0;
2115 find_fallback_font(analysis->pItem[i].a.eScript, lf.lfFaceName);
2116 if (lf.lfFaceName[0])
2118 analysis->glyphs[i].fallbackFont = CreateFontIndirectW(&lf);
2119 if (analysis->glyphs[i].fallbackFont)
2121 ScriptFreeCache(sc);
2122 originalFont = SelectObject(hdc, analysis->glyphs[i].fallbackFont);
2127 /* FIXME: When we properly shape Hangul remove this check */
2128 if ((dwFlags & SSA_LINK) && !analysis->glyphs[i].fallbackFont && analysis->pItem[i].a.eScript == Script_Hangul)
2129 analysis->pItem[i].a.fNoGlyphIndex = TRUE;
2131 if ((dwFlags & SSA_LINK) && !analysis->glyphs[i].fallbackFont && !scriptInformation[analysis->pItem[i].a.eScript].props.fComplex && !analysis->pItem[i].a.fRTL)
2132 analysis->pItem[i].a.fNoGlyphIndex = TRUE;
2134 ScriptShape(hdc, sc, &pStr[analysis->pItem[i].iCharPos], cChar, numGlyphs,
2135 &analysis->pItem[i].a, glyphs, pwLogClust, psva, &numGlyphsReturned);
2136 hr = ScriptPlace(hdc, sc, glyphs, numGlyphsReturned, psva, &analysis->pItem[i].a,
2137 piAdvance, pGoffset, &analysis->glyphs[i].abc);
2138 if (originalFont)
2139 SelectObject(hdc,originalFont);
2141 if (dwFlags & SSA_TAB)
2143 int tabi = 0;
2144 for (tabi = 0; tabi < cChar; tabi++)
2146 if (pStr[analysis->pItem[i].iCharPos+tabi] == 0x0009)
2147 piAdvance[tabi] = getGivenTabWidth(analysis->glyphs[i].sc, pTabdef, analysis->pItem[i].iCharPos+tabi, tab_x);
2148 tab_x+=piAdvance[tabi];
2152 analysis->glyphs[i].numGlyphs = numGlyphsReturned;
2153 analysis->glyphs[i].glyphs = glyphs;
2154 analysis->glyphs[i].pwLogClust = pwLogClust;
2155 analysis->glyphs[i].piAdvance = piAdvance;
2156 analysis->glyphs[i].psva = psva;
2157 analysis->glyphs[i].pGoffset = pGoffset;
2158 analysis->glyphs[i].iMaxPosX= -1;
2160 BidiLevel[i] = analysis->pItem[i].a.s.uBidiLevel;
2163 else
2165 for (i = 0; i < analysis->numItems; i++)
2166 BidiLevel[i] = analysis->pItem[i].a.s.uBidiLevel;
2169 ScriptLayout(analysis->numItems, BidiLevel, NULL, analysis->logical2visual);
2170 heap_free(BidiLevel);
2172 *pssa = analysis;
2173 heap_free(iString);
2174 return S_OK;
2176 error:
2177 heap_free(iString);
2178 heap_free(analysis->glyphs);
2179 heap_free(analysis->logattrs);
2180 heap_free(analysis->pItem);
2181 heap_free(analysis->logical2visual);
2182 heap_free(analysis);
2183 return hr;
2186 static inline BOOL does_glyph_start_cluster(const SCRIPT_VISATTR *pva, const WORD *pwLogClust, int cChars, int glyph, int direction)
2188 if (pva[glyph].fClusterStart)
2189 return TRUE;
2190 if (USP10_FindGlyphInLogClust(pwLogClust, cChars, glyph) >= 0)
2191 return TRUE;
2193 return FALSE;
2197 static HRESULT SS_ItemOut( SCRIPT_STRING_ANALYSIS ssa,
2198 int iX,
2199 int iY,
2200 int iItem,
2201 int cStart,
2202 int cEnd,
2203 UINT uOptions,
2204 const RECT *prc,
2205 BOOL fSelected,
2206 BOOL fDisabled)
2208 StringAnalysis *analysis;
2209 int off_x = 0;
2210 HRESULT hr;
2211 COLORREF BkColor = 0x0;
2212 COLORREF TextColor = 0x0;
2213 INT BkMode = 0;
2214 INT runStart, runEnd;
2215 INT iGlyph, cGlyphs;
2216 HFONT oldFont = 0x0;
2217 RECT crc;
2218 int i;
2220 TRACE("(%p,%d,%d,%d,%d,%d, 0x%1x, %d, %d)\n",
2221 ssa, iX, iY, iItem, cStart, cEnd, uOptions, fSelected, fDisabled);
2223 if (!(analysis = ssa)) return E_INVALIDARG;
2225 if ((cStart >= 0 && analysis->pItem[iItem+1].iCharPos <= cStart) ||
2226 (cEnd >= 0 && analysis->pItem[iItem].iCharPos >= cEnd))
2227 return S_OK;
2229 CopyRect(&crc,prc);
2230 if (fSelected)
2232 BkMode = GetBkMode(analysis->hdc);
2233 SetBkMode( analysis->hdc, OPAQUE);
2234 BkColor = GetBkColor(analysis->hdc);
2235 SetBkColor(analysis->hdc, GetSysColor(COLOR_HIGHLIGHT));
2236 if (!fDisabled)
2238 TextColor = GetTextColor(analysis->hdc);
2239 SetTextColor(analysis->hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
2242 if (analysis->glyphs[iItem].fallbackFont)
2243 oldFont = SelectObject(analysis->hdc, analysis->glyphs[iItem].fallbackFont);
2245 if (cStart >= 0 && analysis->pItem[iItem+1].iCharPos > cStart && analysis->pItem[iItem].iCharPos <= cStart)
2246 runStart = cStart - analysis->pItem[iItem].iCharPos;
2247 else
2248 runStart = 0;
2249 if (cEnd >= 0 && analysis->pItem[iItem+1].iCharPos > cEnd && analysis->pItem[iItem].iCharPos <= cEnd)
2250 runEnd = (cEnd-1) - analysis->pItem[iItem].iCharPos;
2251 else
2252 runEnd = (analysis->pItem[iItem+1].iCharPos - analysis->pItem[iItem].iCharPos) - 1;
2254 if (analysis->pItem[iItem].a.fRTL)
2256 if (cEnd >= 0 && cEnd < analysis->pItem[iItem+1].iCharPos)
2257 ScriptStringCPtoX(ssa, cEnd, FALSE, &off_x);
2258 else
2259 ScriptStringCPtoX(ssa, analysis->pItem[iItem+1].iCharPos-1, TRUE, &off_x);
2260 crc.left = iX + off_x;
2262 else
2264 if (cStart >=0 && runStart)
2265 ScriptStringCPtoX(ssa, cStart, FALSE, &off_x);
2266 else
2267 ScriptStringCPtoX(ssa, analysis->pItem[iItem].iCharPos, FALSE, &off_x);
2268 crc.left = iX + off_x;
2271 if (analysis->pItem[iItem].a.fRTL)
2272 iGlyph = analysis->glyphs[iItem].pwLogClust[runEnd];
2273 else
2274 iGlyph = analysis->glyphs[iItem].pwLogClust[runStart];
2276 if (analysis->pItem[iItem].a.fRTL)
2277 cGlyphs = analysis->glyphs[iItem].pwLogClust[runStart] - iGlyph;
2278 else
2279 cGlyphs = analysis->glyphs[iItem].pwLogClust[runEnd] - iGlyph;
2281 cGlyphs++;
2283 /* adjust for cluster glyphs when starting */
2284 if (analysis->pItem[iItem].a.fRTL)
2285 i = analysis->pItem[iItem+1].iCharPos - 1;
2286 else
2287 i = analysis->pItem[iItem].iCharPos;
2289 for (; i >=analysis->pItem[iItem].iCharPos && i < analysis->pItem[iItem+1].iCharPos; (analysis->pItem[iItem].a.fRTL)?i--:i++)
2291 if (analysis->glyphs[iItem].pwLogClust[i - analysis->pItem[iItem].iCharPos] == iGlyph)
2293 if (analysis->pItem[iItem].a.fRTL)
2294 ScriptStringCPtoX(ssa, i, TRUE, &off_x);
2295 else
2296 ScriptStringCPtoX(ssa, i, FALSE, &off_x);
2297 break;
2301 if (cEnd < 0 || scriptInformation[analysis->pItem[iItem].a.eScript].props.fNeedsCaretInfo)
2303 INT direction;
2304 INT clust_glyph;
2306 clust_glyph = iGlyph + cGlyphs;
2307 if (analysis->pItem[iItem].a.fRTL)
2308 direction = -1;
2309 else
2310 direction = 1;
2312 while(clust_glyph < analysis->glyphs[iItem].numGlyphs &&
2313 !does_glyph_start_cluster(analysis->glyphs[iItem].psva, analysis->glyphs[iItem].pwLogClust, (analysis->pItem[iItem+1].iCharPos - analysis->pItem[iItem].iCharPos), clust_glyph, direction))
2315 cGlyphs++;
2316 clust_glyph++;
2320 hr = ScriptTextOut(analysis->hdc,
2321 (SCRIPT_CACHE *)&analysis->glyphs[iItem].sc, iX + off_x,
2322 iY, uOptions, &crc, &analysis->pItem[iItem].a, NULL, 0,
2323 &analysis->glyphs[iItem].glyphs[iGlyph], cGlyphs,
2324 &analysis->glyphs[iItem].piAdvance[iGlyph], NULL,
2325 &analysis->glyphs[iItem].pGoffset[iGlyph]);
2327 TRACE("ScriptTextOut hr=%08x\n", hr);
2329 if (fSelected)
2331 SetBkColor(analysis->hdc, BkColor);
2332 SetBkMode( analysis->hdc, BkMode);
2333 if (!fDisabled)
2334 SetTextColor(analysis->hdc, TextColor);
2336 if (analysis->glyphs[iItem].fallbackFont)
2337 SelectObject(analysis->hdc, oldFont);
2339 return hr;
2342 /***********************************************************************
2343 * ScriptStringOut (USP10.@)
2345 * This function takes the output of ScriptStringAnalyse and joins the segments
2346 * of glyphs and passes the resulting string to ScriptTextOut. ScriptStringOut
2347 * only processes glyphs.
2349 * Parameters:
2350 * ssa [I] buffer to hold the analysed string components
2351 * iX [I] X axis displacement for output
2352 * iY [I] Y axis displacement for output
2353 * uOptions [I] flags controlling output processing
2354 * prc [I] rectangle coordinates
2355 * iMinSel [I] starting pos for substringing output string
2356 * iMaxSel [I] ending pos for substringing output string
2357 * fDisabled [I] controls text highlighting
2359 * RETURNS
2360 * Success: S_OK
2361 * Failure: is the value returned by ScriptTextOut
2363 HRESULT WINAPI ScriptStringOut(SCRIPT_STRING_ANALYSIS ssa,
2364 int iX,
2365 int iY,
2366 UINT uOptions,
2367 const RECT *prc,
2368 int iMinSel,
2369 int iMaxSel,
2370 BOOL fDisabled)
2372 StringAnalysis *analysis;
2373 int item;
2374 HRESULT hr;
2376 TRACE("(%p,%d,%d,0x%08x,%s,%d,%d,%d)\n",
2377 ssa, iX, iY, uOptions, wine_dbgstr_rect(prc), iMinSel, iMaxSel, fDisabled);
2379 if (!(analysis = ssa)) return E_INVALIDARG;
2380 if (!(analysis->ssa_flags & SSA_GLYPHS)) return E_INVALIDARG;
2382 for (item = 0; item < analysis->numItems; item++)
2384 hr = SS_ItemOut( ssa, iX, iY, analysis->logical2visual[item], -1, -1, uOptions, prc, FALSE, fDisabled);
2385 if (FAILED(hr))
2386 return hr;
2389 if (iMinSel < iMaxSel && (iMinSel > 0 || iMaxSel > 0))
2391 if (iMaxSel > 0 && iMinSel < 0)
2392 iMinSel = 0;
2393 for (item = 0; item < analysis->numItems; item++)
2395 hr = SS_ItemOut( ssa, iX, iY, analysis->logical2visual[item], iMinSel, iMaxSel, uOptions, prc, TRUE, fDisabled);
2396 if (FAILED(hr))
2397 return hr;
2401 return S_OK;
2404 /***********************************************************************
2405 * ScriptStringCPtoX (USP10.@)
2408 HRESULT WINAPI ScriptStringCPtoX(SCRIPT_STRING_ANALYSIS ssa, int icp, BOOL fTrailing, int* pX)
2410 int item;
2411 int runningX = 0;
2412 StringAnalysis* analysis = ssa;
2414 TRACE("(%p), %d, %d, (%p)\n", ssa, icp, fTrailing, pX);
2416 if (!ssa || !pX) return S_FALSE;
2417 if (!(analysis->ssa_flags & SSA_GLYPHS)) return S_FALSE;
2419 /* icp out of range */
2420 if(icp < 0)
2422 analysis->flags |= SCRIPT_STRING_ANALYSIS_FLAGS_INVALID;
2423 return E_INVALIDARG;
2426 for(item=0; item<analysis->numItems; item++)
2428 int CP, i;
2429 int offset;
2431 i = analysis->logical2visual[item];
2432 CP = analysis->pItem[i+1].iCharPos - analysis->pItem[i].iCharPos;
2433 /* initialize max extents for uninitialized runs */
2434 if (analysis->glyphs[i].iMaxPosX == -1)
2436 if (analysis->pItem[i].a.fRTL)
2437 ScriptCPtoX(0, FALSE, CP, analysis->glyphs[i].numGlyphs, analysis->glyphs[i].pwLogClust,
2438 analysis->glyphs[i].psva, analysis->glyphs[i].piAdvance,
2439 &analysis->pItem[i].a, &analysis->glyphs[i].iMaxPosX);
2440 else
2441 ScriptCPtoX(CP, TRUE, CP, analysis->glyphs[i].numGlyphs, analysis->glyphs[i].pwLogClust,
2442 analysis->glyphs[i].psva, analysis->glyphs[i].piAdvance,
2443 &analysis->pItem[i].a, &analysis->glyphs[i].iMaxPosX);
2446 if (icp >= analysis->pItem[i+1].iCharPos || icp < analysis->pItem[i].iCharPos)
2448 runningX += analysis->glyphs[i].iMaxPosX;
2449 continue;
2452 icp -= analysis->pItem[i].iCharPos;
2453 ScriptCPtoX(icp, fTrailing, CP, analysis->glyphs[i].numGlyphs, analysis->glyphs[i].pwLogClust,
2454 analysis->glyphs[i].psva, analysis->glyphs[i].piAdvance,
2455 &analysis->pItem[i].a, &offset);
2456 runningX += offset;
2458 *pX = runningX;
2459 return S_OK;
2462 /* icp out of range */
2463 analysis->flags |= SCRIPT_STRING_ANALYSIS_FLAGS_INVALID;
2464 return E_INVALIDARG;
2467 /***********************************************************************
2468 * ScriptStringXtoCP (USP10.@)
2471 HRESULT WINAPI ScriptStringXtoCP(SCRIPT_STRING_ANALYSIS ssa, int iX, int* piCh, int* piTrailing)
2473 StringAnalysis* analysis = ssa;
2474 int item;
2476 TRACE("(%p), %d, (%p), (%p)\n", ssa, iX, piCh, piTrailing);
2478 if (!ssa || !piCh || !piTrailing) return S_FALSE;
2479 if (!(analysis->ssa_flags & SSA_GLYPHS)) return S_FALSE;
2481 /* out of range */
2482 if(iX < 0)
2484 if (analysis->pItem[0].a.fRTL)
2486 *piCh = 1;
2487 *piTrailing = FALSE;
2489 else
2491 *piCh = -1;
2492 *piTrailing = TRUE;
2494 return S_OK;
2497 for(item=0; item<analysis->numItems; item++)
2499 int i;
2500 int CP;
2502 for (i = 0; i < analysis->numItems && analysis->logical2visual[i] != item; i++)
2503 /* nothing */;
2505 CP = analysis->pItem[i+1].iCharPos - analysis->pItem[i].iCharPos;
2506 /* initialize max extents for uninitialized runs */
2507 if (analysis->glyphs[i].iMaxPosX == -1)
2509 if (analysis->pItem[i].a.fRTL)
2510 ScriptCPtoX(0, FALSE, CP, analysis->glyphs[i].numGlyphs, analysis->glyphs[i].pwLogClust,
2511 analysis->glyphs[i].psva, analysis->glyphs[i].piAdvance,
2512 &analysis->pItem[i].a, &analysis->glyphs[i].iMaxPosX);
2513 else
2514 ScriptCPtoX(CP, TRUE, CP, analysis->glyphs[i].numGlyphs, analysis->glyphs[i].pwLogClust,
2515 analysis->glyphs[i].psva, analysis->glyphs[i].piAdvance,
2516 &analysis->pItem[i].a, &analysis->glyphs[i].iMaxPosX);
2519 if (iX > analysis->glyphs[i].iMaxPosX)
2521 iX -= analysis->glyphs[i].iMaxPosX;
2522 continue;
2525 ScriptXtoCP(iX, CP, analysis->glyphs[i].numGlyphs, analysis->glyphs[i].pwLogClust,
2526 analysis->glyphs[i].psva, analysis->glyphs[i].piAdvance,
2527 &analysis->pItem[i].a, piCh, piTrailing);
2528 *piCh += analysis->pItem[i].iCharPos;
2530 return S_OK;
2533 /* out of range */
2534 *piCh = analysis->pItem[analysis->numItems].iCharPos;
2535 *piTrailing = FALSE;
2537 return S_OK;
2541 /***********************************************************************
2542 * ScriptStringFree (USP10.@)
2544 * Free a string analysis.
2546 * PARAMS
2547 * pssa [I] string analysis.
2549 * RETURNS
2550 * Success: S_OK
2551 * Failure: Non-zero HRESULT value.
2553 HRESULT WINAPI ScriptStringFree(SCRIPT_STRING_ANALYSIS *pssa)
2555 StringAnalysis* analysis;
2556 BOOL invalid;
2557 int i;
2559 TRACE("(%p)\n", pssa);
2561 if (!pssa || !(analysis = *pssa)) return E_INVALIDARG;
2563 invalid = analysis->flags & SCRIPT_STRING_ANALYSIS_FLAGS_INVALID;
2565 if (analysis->glyphs)
2567 for (i = 0; i < analysis->numItems; i++)
2569 heap_free(analysis->glyphs[i].glyphs);
2570 heap_free(analysis->glyphs[i].pwLogClust);
2571 heap_free(analysis->glyphs[i].piAdvance);
2572 heap_free(analysis->glyphs[i].psva);
2573 heap_free(analysis->glyphs[i].pGoffset);
2574 if (analysis->glyphs[i].fallbackFont)
2575 DeleteObject(analysis->glyphs[i].fallbackFont);
2576 ScriptFreeCache((SCRIPT_CACHE *)&analysis->glyphs[i].sc);
2577 heap_free(analysis->glyphs[i].sc);
2579 heap_free(analysis->glyphs);
2582 heap_free(analysis->pItem);
2583 heap_free(analysis->logattrs);
2584 heap_free(analysis->logical2visual);
2585 heap_free(analysis);
2587 if (invalid) return E_INVALIDARG;
2588 return S_OK;
2591 static inline int get_cluster_size(const WORD *pwLogClust, int cChars, int item,
2592 int direction, int* iCluster, int *check_out)
2594 int clust_size = 1;
2595 int check;
2596 WORD clust = pwLogClust[item];
2598 for (check = item+direction; check < cChars && check >= 0; check+=direction)
2600 if (pwLogClust[check] == clust)
2602 clust_size ++;
2603 if (iCluster && *iCluster == -1)
2604 *iCluster = item;
2606 else break;
2609 if (check_out)
2610 *check_out = check;
2612 return clust_size;
2615 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)
2617 int advance;
2618 int log_clust_max;
2620 advance = piAdvance[glyph];
2622 if (pwLogClust[0] > pwLogClust[cChars-1])
2623 log_clust_max = pwLogClust[0];
2624 else
2625 log_clust_max = pwLogClust[cChars-1];
2627 if (glyph > log_clust_max)
2628 return advance;
2630 for (glyph+=direction; glyph < cGlyphs && glyph >= 0; glyph +=direction)
2633 if (does_glyph_start_cluster(pva, pwLogClust, cChars, glyph, direction))
2634 break;
2635 if (glyph > log_clust_max)
2636 break;
2637 advance += piAdvance[glyph];
2640 return advance;
2643 /***********************************************************************
2644 * ScriptCPtoX (USP10.@)
2647 HRESULT WINAPI ScriptCPtoX(int iCP,
2648 BOOL fTrailing,
2649 int cChars,
2650 int cGlyphs,
2651 const WORD *pwLogClust,
2652 const SCRIPT_VISATTR *psva,
2653 const int *piAdvance,
2654 const SCRIPT_ANALYSIS *psa,
2655 int *piX)
2657 int item;
2658 float iPosX;
2659 int iSpecial = -1;
2660 int iCluster = -1;
2661 int clust_size = 1;
2662 float special_size = 0.0;
2663 int iMaxPos = 0;
2664 int advance = 0;
2665 BOOL rtl = FALSE;
2667 TRACE("(%d,%d,%d,%d,%p,%p,%p,%p,%p)\n",
2668 iCP, fTrailing, cChars, cGlyphs, pwLogClust, psva, piAdvance,
2669 psa, piX);
2671 if (psa->fRTL && ! psa->fLogicalOrder)
2672 rtl = TRUE;
2674 if (fTrailing)
2675 iCP++;
2677 if (rtl)
2679 int max_clust = pwLogClust[0];
2681 for (item=0; item < cGlyphs; item++)
2682 if (pwLogClust[item] > max_clust)
2684 ERR("We do not handle non reversed clusters properly\n");
2685 break;
2688 iMaxPos = 0;
2689 for (item = max_clust; item >=0; item --)
2690 iMaxPos += piAdvance[item];
2693 iPosX = 0.0;
2694 for (item=0; item < iCP && item < cChars; item++)
2696 if (iSpecial == -1 && (iCluster == -1 || iCluster+clust_size <= item))
2698 int check;
2699 int clust = pwLogClust[item];
2701 iCluster = -1;
2702 clust_size = get_cluster_size(pwLogClust, cChars, item, 1, &iCluster,
2703 &check);
2705 advance = get_glyph_cluster_advance(piAdvance, psva, pwLogClust, cGlyphs, cChars, clust, 1);
2707 if (check >= cChars && !iMaxPos)
2709 int glyph;
2710 for (glyph = clust; glyph < cGlyphs; glyph++)
2711 special_size += get_glyph_cluster_advance(piAdvance, psva, pwLogClust, cGlyphs, cChars, glyph, 1);
2712 iSpecial = item;
2713 special_size /= (cChars - item);
2714 iPosX += special_size;
2716 else
2718 if (scriptInformation[psa->eScript].props.fNeedsCaretInfo)
2720 clust_size --;
2721 if (clust_size == 0)
2722 iPosX += advance;
2724 else
2725 iPosX += advance / (float)clust_size;
2728 else if (iSpecial != -1)
2729 iPosX += special_size;
2730 else /* (iCluster != -1) */
2732 int adv = get_glyph_cluster_advance(piAdvance, psva, pwLogClust, cGlyphs, cChars, pwLogClust[iCluster], 1);
2733 if (scriptInformation[psa->eScript].props.fNeedsCaretInfo)
2735 clust_size --;
2736 if (clust_size == 0)
2737 iPosX += adv;
2739 else
2740 iPosX += adv / (float)clust_size;
2744 if (iMaxPos > 0)
2746 iPosX = iMaxPos - iPosX;
2747 if (iPosX < 0)
2748 iPosX = 0;
2751 *piX = iPosX;
2752 TRACE("*piX=%d\n", *piX);
2753 return S_OK;
2756 /* Count the number of characters in a cluster and its starting index*/
2757 static inline BOOL get_cluster_data(const WORD *pwLogClust, int cChars, int cluster_index, int *cluster_size, int *start_index)
2759 int size = 0;
2760 int i;
2762 for (i = 0; i < cChars; i++)
2764 if (pwLogClust[i] == cluster_index)
2766 if (!size && start_index)
2768 *start_index = i;
2769 if (!cluster_size)
2770 return TRUE;
2772 size++;
2774 else if (size) break;
2776 if (cluster_size)
2777 *cluster_size = size;
2779 return (size > 0);
2783 To handle multi-glyph clusters we need to find all the glyphs that are
2784 represented in the cluster. This involves finding the glyph whose
2785 index is the cluster index as well as whose glyph indices are greater than
2786 our cluster index but not part of a new cluster.
2788 Then we sum all those glyphs' advances.
2790 static inline int get_cluster_advance(const int* piAdvance,
2791 const SCRIPT_VISATTR *psva,
2792 const WORD *pwLogClust, int cGlyphs,
2793 int cChars, int cluster, int direction)
2795 int glyph_start;
2796 int glyph_end;
2797 int i, advance;
2799 if (direction > 0)
2800 i = 0;
2801 else
2802 i = (cChars - 1);
2804 for (glyph_start = -1, glyph_end = -1; i < cChars && i >= 0 && (glyph_start < 0 || glyph_end < 0); i+=direction)
2806 if (glyph_start < 0 && pwLogClust[i] != cluster) continue;
2807 if (pwLogClust[i] == cluster && glyph_start < 0) glyph_start = pwLogClust[i];
2808 if (glyph_start >= 0 && glyph_end < 0 && pwLogClust[i] != cluster) glyph_end = pwLogClust[i];
2810 if (glyph_end < 0)
2812 if (direction > 0)
2813 glyph_end = cGlyphs;
2814 else
2816 /* Don't fully understand multi-glyph reversed clusters yet,
2817 * do they occur for real or just in our test? */
2818 FIXME("multi-glyph reversed clusters found\n");
2819 glyph_end = glyph_start + 1;
2823 /* Check for fClusterStart, finding this generally would mean a malformed set of data */
2824 for (i = glyph_start+1; i< glyph_end; i++)
2826 if (psva[i].fClusterStart)
2828 glyph_end = i;
2829 break;
2833 for (advance = 0, i = glyph_start; i < glyph_end; i++)
2834 advance += piAdvance[i];
2836 return advance;
2840 /***********************************************************************
2841 * ScriptXtoCP (USP10.@)
2843 * Basic algorithm :
2844 * Use piAdvance to find the cluster we are looking at.
2845 * Find the character that is the first character of the cluster.
2846 * That is our base piCP.
2847 * If the script snaps to cluster boundaries (Hebrew, Indic, Thai) then we
2848 * are good. Otherwise if the cluster is larger than 1 glyph we need to
2849 * determine how far through the cluster to advance the cursor.
2851 HRESULT WINAPI ScriptXtoCP(int iX,
2852 int cChars,
2853 int cGlyphs,
2854 const WORD *pwLogClust,
2855 const SCRIPT_VISATTR *psva,
2856 const int *piAdvance,
2857 const SCRIPT_ANALYSIS *psa,
2858 int *piCP,
2859 int *piTrailing)
2861 int direction = 1;
2862 int iPosX;
2863 int i;
2864 int glyph_index, cluster_index;
2865 int cluster_size;
2867 TRACE("(%d,%d,%d,%p,%p,%p,%p,%p,%p)\n",
2868 iX, cChars, cGlyphs, pwLogClust, psva, piAdvance,
2869 psa, piCP, piTrailing);
2871 if (psa->fRTL && ! psa->fLogicalOrder)
2872 direction = -1;
2874 /* Handle an iX < 0 */
2875 if (iX < 0)
2877 if (direction < 0)
2879 *piCP = cChars;
2880 *piTrailing = 0;
2882 else
2884 *piCP = -1;
2885 *piTrailing = 1;
2887 return S_OK;
2890 /* Looking for non-reversed clusters in a reversed string */
2891 if (direction < 0)
2893 int max_clust = pwLogClust[0];
2894 for (i=0; i< cChars; i++)
2895 if (pwLogClust[i] > max_clust)
2897 FIXME("We do not handle non reversed clusters properly\n");
2898 break;
2902 /* find the glyph_index based in iX */
2903 if (direction > 0)
2905 for (glyph_index = -1, iPosX = iX; iPosX >=0 && glyph_index < cGlyphs; iPosX -= piAdvance[glyph_index+1], glyph_index++)
2908 else
2910 for (glyph_index = -1, iPosX = iX; iPosX > 0 && glyph_index < cGlyphs; iPosX -= piAdvance[glyph_index+1], glyph_index++)
2914 TRACE("iPosX %i -> glyph_index %i (%i)\n", iPosX, glyph_index, cGlyphs);
2916 *piTrailing = 0;
2917 if (glyph_index >= 0 && glyph_index < cGlyphs)
2919 /* find the cluster */
2920 if (direction > 0 )
2921 for (i = 0, cluster_index = pwLogClust[0]; i < cChars && pwLogClust[i] <= glyph_index; cluster_index=pwLogClust[i++])
2923 else
2924 for (i = 0, cluster_index = pwLogClust[0]; i < cChars && pwLogClust[i] >= glyph_index; cluster_index=pwLogClust[i++])
2927 TRACE("cluster_index %i\n", cluster_index);
2929 if (direction < 0 && iPosX >= 0 && glyph_index != cluster_index)
2931 /* We are off the end of the string */
2932 *piCP = -1;
2933 *piTrailing = 1;
2934 return S_OK;
2937 get_cluster_data(pwLogClust, cChars, cluster_index, &cluster_size, &i);
2939 TRACE("first char index %i\n",i);
2940 if (scriptInformation[psa->eScript].props.fNeedsCaretInfo)
2942 /* Check trailing */
2943 if (glyph_index != cluster_index ||
2944 (direction > 0 && abs(iPosX) <= (piAdvance[glyph_index] / 2)) ||
2945 (direction < 0 && abs(iPosX) >= (piAdvance[glyph_index] / 2)))
2946 *piTrailing = cluster_size;
2948 else
2950 if (cluster_size > 1)
2952 /* Be part way through the glyph cluster based on size and position */
2953 int cluster_advance = get_cluster_advance(piAdvance, psva, pwLogClust, cGlyphs, cChars, cluster_index, direction);
2954 double cluster_part_width = cluster_advance / (float)cluster_size;
2955 double adv;
2956 int part_index;
2958 /* back up to the beginning of the cluster */
2959 for (adv = iPosX, part_index = cluster_index; part_index <= glyph_index; part_index++)
2960 adv += piAdvance[part_index];
2961 if (adv > iX) adv = iX;
2963 TRACE("Multi-char cluster, no snap\n");
2964 TRACE("cluster size %i, pre-cluster iPosX %f\n",cluster_size, adv);
2965 TRACE("advance %i divides into %f per char\n", cluster_advance, cluster_part_width);
2966 if (direction > 0)
2968 for (part_index = 0; adv >= 0; adv-=cluster_part_width, part_index++)
2970 if (part_index) part_index--;
2972 else
2974 for (part_index = 0; adv > 0; adv-=cluster_part_width, part_index++)
2976 if (part_index > cluster_size)
2978 adv += cluster_part_width;
2979 part_index=cluster_size;
2983 TRACE("base_char %i part_index %i, leftover advance %f\n",i, part_index, adv);
2985 if (direction > 0)
2986 i += part_index;
2987 else
2988 i += (cluster_size - part_index);
2990 /* Check trailing */
2991 if ((direction > 0 && fabs(adv) <= (cluster_part_width / 2.0)) ||
2992 (direction < 0 && adv && fabs(adv) >= (cluster_part_width / 2.0)))
2993 *piTrailing = 1;
2995 else
2997 /* Check trailing */
2998 if ((direction > 0 && abs(iPosX) <= (piAdvance[glyph_index] / 2)) ||
2999 (direction < 0 && abs(iPosX) >= (piAdvance[glyph_index] / 2)))
3000 *piTrailing = 1;
3004 else
3006 TRACE("Point falls outside of string\n");
3007 if (glyph_index < 0)
3008 i = cChars-1;
3009 else /* (glyph_index >= cGlyphs) */
3010 i = cChars;
3012 /* If not snapping in the reverse direction (such as Hebrew) Then 0
3013 point flow to the next character */
3014 if (direction < 0)
3016 if (!scriptInformation[psa->eScript].props.fNeedsCaretInfo && abs(iPosX) == piAdvance[glyph_index])
3017 i++;
3018 else
3019 *piTrailing = 1;
3023 *piCP = i;
3025 TRACE("*piCP=%d\n", *piCP);
3026 TRACE("*piTrailing=%d\n", *piTrailing);
3027 return S_OK;
3030 /***********************************************************************
3031 * ScriptBreak (USP10.@)
3033 * Retrieve line break information.
3035 * PARAMS
3036 * chars [I] Array of characters.
3037 * sa [I] Script analysis.
3038 * la [I] Array of logical attribute structures.
3040 * RETURNS
3041 * Success: S_OK
3042 * Failure: S_FALSE
3044 HRESULT WINAPI ScriptBreak(const WCHAR *chars, int count, const SCRIPT_ANALYSIS *sa, SCRIPT_LOGATTR *la)
3046 TRACE("(%s, %d, %p, %p)\n", debugstr_wn(chars, count), count, sa, la);
3048 if (count < 0 || !la) return E_INVALIDARG;
3049 if (count == 0) return E_FAIL;
3051 BREAK_line(chars, count, sa, la);
3053 return S_OK;
3056 /***********************************************************************
3057 * ScriptIsComplex (USP10.@)
3059 * Determine if a string is complex.
3061 * PARAMS
3062 * chars [I] Array of characters to test.
3063 * len [I] Length in characters.
3064 * flag [I] Flag.
3066 * RETURNS
3067 * Success: S_OK
3068 * Failure: S_FALSE
3071 HRESULT WINAPI ScriptIsComplex(const WCHAR *chars, int len, DWORD flag)
3073 enum usp10_script script;
3074 unsigned int i, consumed;
3076 TRACE("(%s,%d,0x%x)\n", debugstr_wn(chars, len), len, flag);
3078 if (!chars || len < 0)
3079 return E_INVALIDARG;
3081 for (i = 0; i < len; i+=consumed)
3083 if ((flag & SIC_ASCIIDIGIT) && chars[i] >= 0x30 && chars[i] <= 0x39)
3084 return S_OK;
3086 script = get_char_script(chars,i,len, &consumed);
3087 if ((scriptInformation[script].props.fComplex && (flag & SIC_COMPLEX))||
3088 (!scriptInformation[script].props.fComplex && (flag & SIC_NEUTRAL)))
3089 return S_OK;
3091 return S_FALSE;
3094 /***********************************************************************
3095 * ScriptShapeOpenType (USP10.@)
3097 * Produce glyphs and visual attributes for a run.
3099 * PARAMS
3100 * hdc [I] Device context.
3101 * psc [I/O] Opaque pointer to a script cache.
3102 * psa [I/O] Script analysis.
3103 * tagScript [I] The OpenType tag for the Script
3104 * tagLangSys [I] The OpenType tag for the Language
3105 * rcRangeChars[I] Array of Character counts in each range
3106 * rpRangeProperties [I] Array of TEXTRANGE_PROPERTIES structures
3107 * cRanges [I] Count of ranges
3108 * pwcChars [I] Array of characters specifying the run.
3109 * cChars [I] Number of characters in pwcChars.
3110 * cMaxGlyphs [I] Length of pwOutGlyphs.
3111 * pwLogClust [O] Array of logical cluster info.
3112 * pCharProps [O] Array of character property values
3113 * pwOutGlyphs [O] Array of glyphs.
3114 * pOutGlyphProps [O] Array of attributes for the retrieved glyphs
3115 * pcGlyphs [O] Number of glyphs returned.
3117 * RETURNS
3118 * Success: S_OK
3119 * Failure: Non-zero HRESULT value.
3121 HRESULT WINAPI ScriptShapeOpenType( HDC hdc, SCRIPT_CACHE *psc,
3122 SCRIPT_ANALYSIS *psa, OPENTYPE_TAG tagScript,
3123 OPENTYPE_TAG tagLangSys, int *rcRangeChars,
3124 TEXTRANGE_PROPERTIES **rpRangeProperties,
3125 int cRanges, const WCHAR *pwcChars, int cChars,
3126 int cMaxGlyphs, WORD *pwLogClust,
3127 SCRIPT_CHARPROP *pCharProps, WORD *pwOutGlyphs,
3128 SCRIPT_GLYPHPROP *pOutGlyphProps, int *pcGlyphs)
3130 HRESULT hr;
3131 int i;
3132 unsigned int g;
3133 BOOL rtl;
3134 int cluster;
3135 static int once = 0;
3137 TRACE("(%p, %p, %p, %s, %s, %p, %p, %d, %s, %d, %d, %p, %p, %p, %p, %p )\n",
3138 hdc, psc, psa,
3139 debugstr_an((char*)&tagScript,4), debugstr_an((char*)&tagLangSys,4),
3140 rcRangeChars, rpRangeProperties, cRanges, debugstr_wn(pwcChars, cChars),
3141 cChars, cMaxGlyphs, pwLogClust, pCharProps, pwOutGlyphs, pOutGlyphProps, pcGlyphs);
3143 if (psa) TRACE("psa values: %d, %d, %d, %d, %d, %d, %d\n", psa->eScript, psa->fRTL, psa->fLayoutRTL,
3144 psa->fLinkBefore, psa->fLinkAfter, psa->fLogicalOrder, psa->fNoGlyphIndex);
3146 if (!pOutGlyphProps || !pcGlyphs || !pCharProps) return E_INVALIDARG;
3147 if (cChars > cMaxGlyphs) return E_OUTOFMEMORY;
3149 if (cRanges)
3150 if(!once++) FIXME("Ranges not supported yet\n");
3152 rtl = (psa && !psa->fLogicalOrder && psa->fRTL);
3154 *pcGlyphs = cChars;
3155 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
3156 if (!pwLogClust) return E_FAIL;
3158 ((ScriptCache *)*psc)->userScript = tagScript;
3159 ((ScriptCache *)*psc)->userLang = tagLangSys;
3161 /* Initialize a SCRIPT_VISATTR and LogClust for each char in this run */
3162 for (i = 0; i < cChars; i++)
3164 int idx = i;
3165 if (rtl) idx = cChars - 1 - i;
3166 /* FIXME: set to better values */
3167 pOutGlyphProps[i].sva.uJustification = (pwcChars[idx] == ' ') ? SCRIPT_JUSTIFY_BLANK : SCRIPT_JUSTIFY_CHARACTER;
3168 pOutGlyphProps[i].sva.fClusterStart = 1;
3169 pOutGlyphProps[i].sva.fDiacritic = 0;
3170 pOutGlyphProps[i].sva.fZeroWidth = 0;
3171 pOutGlyphProps[i].sva.fReserved = 0;
3172 pOutGlyphProps[i].sva.fShapeReserved = 0;
3174 /* FIXME: have the shaping engine set this */
3175 pCharProps[i].fCanGlyphAlone = 0;
3177 pwLogClust[i] = idx;
3180 if (psa && !psa->fNoGlyphIndex && ((ScriptCache *)*psc)->sfnt)
3182 WCHAR *rChars;
3183 if ((hr = SHAPE_CheckFontForRequiredFeatures(hdc, (ScriptCache *)*psc, psa)) != S_OK) return hr;
3185 if (!(rChars = heap_calloc(cChars, sizeof(*rChars))))
3186 return E_OUTOFMEMORY;
3188 for (i = 0, g = 0, cluster = 0; i < cChars; i++)
3190 int idx = i;
3191 DWORD chInput;
3193 if (rtl) idx = cChars - 1 - i;
3194 if (!cluster)
3196 chInput = decode_surrogate_pair(pwcChars, idx, cChars);
3197 if (!chInput)
3199 if (psa->fRTL)
3200 chInput = mirror_char(pwcChars[idx]);
3201 else
3202 chInput = pwcChars[idx];
3203 rChars[i] = chInput;
3205 else
3207 rChars[i] = pwcChars[idx];
3208 rChars[i+1] = pwcChars[(rtl)?idx-1:idx+1];
3209 cluster = 1;
3211 if (!(pwOutGlyphs[g] = get_cache_glyph(psc, chInput)))
3213 WORD glyph;
3214 if (!hdc)
3216 heap_free(rChars);
3217 return E_PENDING;
3219 if (OpenType_CMAP_GetGlyphIndex(hdc, (ScriptCache *)*psc, chInput, &glyph, 0) == GDI_ERROR)
3221 heap_free(rChars);
3222 return S_FALSE;
3224 pwOutGlyphs[g] = set_cache_glyph(psc, chInput, glyph);
3226 g++;
3228 else
3230 int k;
3231 cluster--;
3232 pwLogClust[idx] = (rtl)?pwLogClust[idx+1]:pwLogClust[idx-1];
3233 for (k = (rtl)?idx-1:idx+1; k >= 0 && k < cChars; (rtl)?k--:k++)
3234 pwLogClust[k]--;
3237 *pcGlyphs = g;
3239 SHAPE_ContextualShaping(hdc, (ScriptCache *)*psc, psa, rChars, cChars, pwOutGlyphs, pcGlyphs, cMaxGlyphs, pwLogClust);
3240 SHAPE_ApplyDefaultOpentypeFeatures(hdc, (ScriptCache *)*psc, psa, pwOutGlyphs, pcGlyphs, cMaxGlyphs, cChars, pwLogClust);
3241 SHAPE_CharGlyphProp(hdc, (ScriptCache *)*psc, psa, pwcChars, cChars, pwOutGlyphs, *pcGlyphs, pwLogClust, pCharProps, pOutGlyphProps);
3243 for (i = 0; i < cChars; ++i)
3245 /* Special case for tabs and joiners. As control characters, ZWNJ
3246 * and ZWJ would in principle get handled by the corresponding
3247 * shaping functions. However, since ZWNJ and ZWJ can get merged
3248 * into adjoining runs during itemisation, these don't generally
3249 * get classified as Script_Control. */
3250 if (pwcChars[i] == 0x0009 || pwcChars[i] == ZWSP || pwcChars[i] == ZWNJ || pwcChars[i] == ZWJ)
3252 pwOutGlyphs[pwLogClust[i]] = ((ScriptCache *)*psc)->sfp.wgBlank;
3253 pOutGlyphProps[pwLogClust[i]].sva.fZeroWidth = 1;
3256 heap_free(rChars);
3258 else
3260 TRACE("no glyph translation\n");
3261 for (i = 0; i < cChars; i++)
3263 int idx = i;
3264 /* No mirroring done here */
3265 if (rtl) idx = cChars - 1 - i;
3266 pwOutGlyphs[i] = pwcChars[idx];
3268 if (!psa)
3269 continue;
3271 /* overwrite some basic control glyphs to blank */
3272 if (psa->fNoGlyphIndex)
3274 if (pwcChars[idx] == ZWSP || pwcChars[idx] == ZWNJ || pwcChars[idx] == ZWJ)
3276 pwOutGlyphs[i] = 0x20;
3277 pOutGlyphProps[i].sva.fZeroWidth = 1;
3280 else if (psa->eScript == Script_Control || pwcChars[idx] == ZWSP
3281 || pwcChars[idx] == ZWNJ || pwcChars[idx] == ZWJ)
3283 if (pwcChars[idx] == 0x0009 || pwcChars[idx] == 0x000A ||
3284 pwcChars[idx] == 0x000D || pwcChars[idx] >= 0x001C)
3286 pwOutGlyphs[i] = ((ScriptCache *)*psc)->sfp.wgBlank;
3287 pOutGlyphProps[i].sva.fZeroWidth = 1;
3293 return S_OK;
3297 /***********************************************************************
3298 * ScriptShape (USP10.@)
3300 * Produce glyphs and visual attributes for a run.
3302 * PARAMS
3303 * hdc [I] Device context.
3304 * psc [I/O] Opaque pointer to a script cache.
3305 * pwcChars [I] Array of characters specifying the run.
3306 * cChars [I] Number of characters in pwcChars.
3307 * cMaxGlyphs [I] Length of pwOutGlyphs.
3308 * psa [I/O] Script analysis.
3309 * pwOutGlyphs [O] Array of glyphs.
3310 * pwLogClust [O] Array of logical cluster info.
3311 * psva [O] Array of visual attributes.
3312 * pcGlyphs [O] Number of glyphs returned.
3314 * RETURNS
3315 * Success: S_OK
3316 * Failure: Non-zero HRESULT value.
3318 HRESULT WINAPI ScriptShape(HDC hdc, SCRIPT_CACHE *psc, const WCHAR *pwcChars,
3319 int cChars, int cMaxGlyphs,
3320 SCRIPT_ANALYSIS *psa, WORD *pwOutGlyphs, WORD *pwLogClust,
3321 SCRIPT_VISATTR *psva, int *pcGlyphs)
3323 HRESULT hr;
3324 int i;
3325 SCRIPT_CHARPROP *charProps;
3326 SCRIPT_GLYPHPROP *glyphProps;
3328 if (!psva || !pcGlyphs) return E_INVALIDARG;
3329 if (cChars > cMaxGlyphs) return E_OUTOFMEMORY;
3331 if (!(charProps = heap_calloc(cChars, sizeof(*charProps))))
3332 return E_OUTOFMEMORY;
3334 if (!(glyphProps = heap_calloc(cMaxGlyphs, sizeof(*glyphProps))))
3336 heap_free(charProps);
3337 return E_OUTOFMEMORY;
3340 hr = ScriptShapeOpenType(hdc, psc, psa, scriptInformation[psa->eScript].scriptTag, 0, NULL, NULL, 0, pwcChars, cChars, cMaxGlyphs, pwLogClust, charProps, pwOutGlyphs, glyphProps, pcGlyphs);
3342 if (SUCCEEDED(hr))
3344 for (i = 0; i < *pcGlyphs; i++)
3345 psva[i] = glyphProps[i].sva;
3348 heap_free(charProps);
3349 heap_free(glyphProps);
3351 return hr;
3354 /***********************************************************************
3355 * ScriptPlaceOpenType (USP10.@)
3357 * Produce advance widths for a run.
3359 * PARAMS
3360 * hdc [I] Device context.
3361 * psc [I/O] Opaque pointer to a script cache.
3362 * psa [I/O] Script analysis.
3363 * tagScript [I] The OpenType tag for the Script
3364 * tagLangSys [I] The OpenType tag for the Language
3365 * rcRangeChars[I] Array of Character counts in each range
3366 * rpRangeProperties [I] Array of TEXTRANGE_PROPERTIES structures
3367 * cRanges [I] Count of ranges
3368 * pwcChars [I] Array of characters specifying the run.
3369 * pwLogClust [I] Array of logical cluster info
3370 * pCharProps [I] Array of character property values
3371 * cChars [I] Number of characters in pwcChars.
3372 * pwGlyphs [I] Array of glyphs.
3373 * pGlyphProps [I] Array of attributes for the retrieved glyphs
3374 * cGlyphs [I] Count of Glyphs
3375 * piAdvance [O] Array of advance widths.
3376 * pGoffset [O] Glyph offsets.
3377 * pABC [O] Combined ABC width.
3379 * RETURNS
3380 * Success: S_OK
3381 * Failure: Non-zero HRESULT value.
3384 HRESULT WINAPI ScriptPlaceOpenType( HDC hdc, SCRIPT_CACHE *psc, SCRIPT_ANALYSIS *psa,
3385 OPENTYPE_TAG tagScript, OPENTYPE_TAG tagLangSys,
3386 int *rcRangeChars, TEXTRANGE_PROPERTIES **rpRangeProperties,
3387 int cRanges, const WCHAR *pwcChars, WORD *pwLogClust,
3388 SCRIPT_CHARPROP *pCharProps, int cChars,
3389 const WORD *pwGlyphs, const SCRIPT_GLYPHPROP *pGlyphProps,
3390 int cGlyphs, int *piAdvance,
3391 GOFFSET *pGoffset, ABC *pABC
3394 HRESULT hr;
3395 int i;
3396 static int once = 0;
3398 TRACE("(%p, %p, %p, %s, %s, %p, %p, %d, %s, %p, %p, %d, %p, %p, %d, %p %p %p)\n",
3399 hdc, psc, psa,
3400 debugstr_an((char*)&tagScript,4), debugstr_an((char*)&tagLangSys,4),
3401 rcRangeChars, rpRangeProperties, cRanges, debugstr_wn(pwcChars, cChars),
3402 pwLogClust, pCharProps, cChars, pwGlyphs, pGlyphProps, cGlyphs, piAdvance,
3403 pGoffset, pABC);
3405 if (!pGlyphProps) return E_INVALIDARG;
3406 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
3407 if (!pGoffset) return E_FAIL;
3409 if (cRanges)
3410 if (!once++) FIXME("Ranges not supported yet\n");
3412 ((ScriptCache *)*psc)->userScript = tagScript;
3413 ((ScriptCache *)*psc)->userLang = tagLangSys;
3415 if (pABC) memset(pABC, 0, sizeof(ABC));
3416 for (i = 0; i < cGlyphs; i++)
3418 WORD glyph;
3419 ABC abc;
3421 /* FIXME: set to more reasonable values */
3422 pGoffset[i].du = pGoffset[i].dv = 0;
3424 if (pGlyphProps[i].sva.fZeroWidth)
3426 abc.abcA = abc.abcB = abc.abcC = 0;
3427 if (piAdvance) piAdvance[i] = 0;
3428 continue;
3431 if (psa->fNoGlyphIndex)
3433 if (FAILED(hr = ScriptGetCMap(hdc, psc, &pwGlyphs[i], 1, 0, &glyph))) return hr;
3435 else
3437 hr = S_OK;
3438 glyph = pwGlyphs[i];
3441 if (hr == S_FALSE)
3443 if (!hdc) return E_PENDING;
3444 if (get_cache_pitch_family(psc) & TMPF_TRUETYPE)
3446 if (!GetCharABCWidthsW(hdc, pwGlyphs[i], pwGlyphs[i], &abc)) return S_FALSE;
3448 else
3450 INT width;
3451 if (!GetCharWidthW(hdc, pwGlyphs[i], pwGlyphs[i], &width)) return S_FALSE;
3452 abc.abcB = width;
3453 abc.abcA = abc.abcC = 0;
3456 else if (!get_cache_glyph_widths(psc, glyph, &abc))
3458 if (!hdc) return E_PENDING;
3459 if (get_cache_pitch_family(psc) & TMPF_TRUETYPE)
3461 if (!GetCharABCWidthsI(hdc, glyph, 1, NULL, &abc)) return S_FALSE;
3463 else
3465 INT width;
3466 if (!GetCharWidthI(hdc, glyph, 1, NULL, &width)) return S_FALSE;
3467 abc.abcB = width;
3468 abc.abcA = abc.abcC = 0;
3470 set_cache_glyph_widths(psc, glyph, &abc);
3472 if (pABC)
3474 pABC->abcA += abc.abcA;
3475 pABC->abcB += abc.abcB;
3476 pABC->abcC += abc.abcC;
3478 if (piAdvance) piAdvance[i] = abc.abcA + abc.abcB + abc.abcC;
3481 SHAPE_ApplyOpenTypePositions(hdc, (ScriptCache *)*psc, psa, pwGlyphs, cGlyphs, piAdvance, pGoffset);
3483 if (pABC) TRACE("Total for run: abcA=%d, abcB=%d, abcC=%d\n", pABC->abcA, pABC->abcB, pABC->abcC);
3484 return S_OK;
3487 /***********************************************************************
3488 * ScriptPlace (USP10.@)
3490 * Produce advance widths for a run.
3492 * PARAMS
3493 * hdc [I] Device context.
3494 * psc [I/O] Opaque pointer to a script cache.
3495 * pwGlyphs [I] Array of glyphs.
3496 * cGlyphs [I] Number of glyphs in pwGlyphs.
3497 * psva [I] Array of visual attributes.
3498 * psa [I/O] String analysis.
3499 * piAdvance [O] Array of advance widths.
3500 * pGoffset [O] Glyph offsets.
3501 * pABC [O] Combined ABC width.
3503 * RETURNS
3504 * Success: S_OK
3505 * Failure: Non-zero HRESULT value.
3507 HRESULT WINAPI ScriptPlace(HDC hdc, SCRIPT_CACHE *psc, const WORD *pwGlyphs,
3508 int cGlyphs, const SCRIPT_VISATTR *psva,
3509 SCRIPT_ANALYSIS *psa, int *piAdvance, GOFFSET *pGoffset, ABC *pABC )
3511 HRESULT hr;
3512 SCRIPT_GLYPHPROP *glyphProps;
3513 int i;
3515 TRACE("(%p, %p, %p, %d, %p, %p, %p, %p, %p)\n", hdc, psc, pwGlyphs, cGlyphs, psva, psa,
3516 piAdvance, pGoffset, pABC);
3518 if (!psva) return E_INVALIDARG;
3519 if (!pGoffset) return E_FAIL;
3521 if (!(glyphProps = heap_calloc(cGlyphs, sizeof(*glyphProps))))
3522 return E_OUTOFMEMORY;
3524 for (i = 0; i < cGlyphs; i++)
3525 glyphProps[i].sva = psva[i];
3527 hr = ScriptPlaceOpenType(hdc, psc, psa, scriptInformation[psa->eScript].scriptTag, 0, NULL, NULL, 0, NULL, NULL, NULL, 0, pwGlyphs, glyphProps, cGlyphs, piAdvance, pGoffset, pABC);
3529 heap_free(glyphProps);
3531 return hr;
3534 /***********************************************************************
3535 * ScriptGetCMap (USP10.@)
3537 * Retrieve glyph indices.
3539 * PARAMS
3540 * hdc [I] Device context.
3541 * psc [I/O] Opaque pointer to a script cache.
3542 * pwcInChars [I] Array of Unicode characters.
3543 * cChars [I] Number of characters in pwcInChars.
3544 * dwFlags [I] Flags.
3545 * pwOutGlyphs [O] Buffer to receive the array of glyph indices.
3547 * RETURNS
3548 * Success: S_OK
3549 * Failure: Non-zero HRESULT value.
3551 HRESULT WINAPI ScriptGetCMap(HDC hdc, SCRIPT_CACHE *psc, const WCHAR *pwcInChars,
3552 int cChars, DWORD dwFlags, WORD *pwOutGlyphs)
3554 HRESULT hr;
3555 int i;
3557 TRACE("(%p,%p,%s,%d,0x%x,%p)\n", hdc, psc, debugstr_wn(pwcInChars, cChars),
3558 cChars, dwFlags, pwOutGlyphs);
3560 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
3562 hr = S_OK;
3564 for (i = 0; i < cChars; i++)
3566 WCHAR inChar;
3567 if (dwFlags == SGCM_RTL)
3568 inChar = mirror_char(pwcInChars[i]);
3569 else
3570 inChar = pwcInChars[i];
3571 if (!(pwOutGlyphs[i] = get_cache_glyph(psc, inChar)))
3573 WORD glyph;
3574 if (!hdc) return E_PENDING;
3575 if (GetGlyphIndicesW(hdc, &inChar, 1, &glyph, GGI_MARK_NONEXISTING_GLYPHS) == GDI_ERROR) return S_FALSE;
3576 if (glyph == 0xffff)
3578 hr = S_FALSE;
3579 glyph = 0x0;
3581 pwOutGlyphs[i] = set_cache_glyph(psc, inChar, glyph);
3585 return hr;
3588 /***********************************************************************
3589 * ScriptTextOut (USP10.@)
3592 HRESULT WINAPI ScriptTextOut(const HDC hdc, SCRIPT_CACHE *psc, int x, int y, UINT fuOptions,
3593 const RECT *lprc, const SCRIPT_ANALYSIS *psa, const WCHAR *pwcReserved,
3594 int iReserved, const WORD *pwGlyphs, int cGlyphs, const int *piAdvance,
3595 const int *piJustify, const GOFFSET *pGoffset)
3597 HRESULT hr = S_OK;
3598 INT i, dir = 1;
3599 INT *lpDx;
3600 WORD *reordered_glyphs = (WORD *)pwGlyphs;
3602 TRACE("(%p, %p, %d, %d, %08x, %s, %p, %p, %d, %p, %d, %p, %p, %p)\n",
3603 hdc, psc, x, y, fuOptions, wine_dbgstr_rect(lprc), psa, pwcReserved, iReserved, pwGlyphs, cGlyphs,
3604 piAdvance, piJustify, pGoffset);
3606 if (!hdc || !psc) return E_INVALIDARG;
3607 if (!piAdvance || !psa || !pwGlyphs) return E_INVALIDARG;
3609 fuOptions &= ETO_CLIPPED + ETO_OPAQUE;
3610 fuOptions |= ETO_IGNORELANGUAGE;
3611 if (!psa->fNoGlyphIndex) /* Have Glyphs? */
3612 fuOptions |= ETO_GLYPH_INDEX; /* Say don't do translation to glyph */
3614 if (!(lpDx = heap_calloc(cGlyphs, 2 * sizeof(*lpDx))))
3615 return E_OUTOFMEMORY;
3616 fuOptions |= ETO_PDY;
3618 if (psa->fRTL && psa->fLogicalOrder)
3620 if (!(reordered_glyphs = heap_calloc(cGlyphs, sizeof(*reordered_glyphs))))
3622 heap_free( lpDx );
3623 return E_OUTOFMEMORY;
3626 for (i = 0; i < cGlyphs; i++)
3627 reordered_glyphs[i] = pwGlyphs[cGlyphs - 1 - i];
3628 dir = -1;
3631 for (i = 0; i < cGlyphs; i++)
3633 int orig_index = (dir > 0) ? i : cGlyphs - 1 - i;
3634 lpDx[i * 2] = piAdvance[orig_index];
3635 lpDx[i * 2 + 1] = 0;
3637 if (pGoffset)
3639 if (i == 0)
3641 x += pGoffset[orig_index].du * dir;
3642 y += pGoffset[orig_index].dv;
3644 else
3646 lpDx[(i - 1) * 2] += pGoffset[orig_index].du * dir;
3647 lpDx[(i - 1) * 2 + 1] += pGoffset[orig_index].dv;
3649 lpDx[i * 2] -= pGoffset[orig_index].du * dir;
3650 lpDx[i * 2 + 1] -= pGoffset[orig_index].dv;
3654 if (!ExtTextOutW(hdc, x, y, fuOptions, lprc, reordered_glyphs, cGlyphs, lpDx))
3655 hr = S_FALSE;
3657 if (reordered_glyphs != pwGlyphs) heap_free( reordered_glyphs );
3658 heap_free(lpDx);
3660 return hr;
3663 /***********************************************************************
3664 * ScriptCacheGetHeight (USP10.@)
3666 * Retrieve the height of the font in the cache.
3668 * PARAMS
3669 * hdc [I] Device context.
3670 * psc [I/O] Opaque pointer to a script cache.
3671 * height [O] Receives font height.
3673 * RETURNS
3674 * Success: S_OK
3675 * Failure: Non-zero HRESULT value.
3677 HRESULT WINAPI ScriptCacheGetHeight(HDC hdc, SCRIPT_CACHE *psc, LONG *height)
3679 HRESULT hr;
3681 TRACE("(%p, %p, %p)\n", hdc, psc, height);
3683 if (!height) return E_INVALIDARG;
3684 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
3686 *height = get_cache_height(psc);
3687 return S_OK;
3690 /***********************************************************************
3691 * ScriptGetGlyphABCWidth (USP10.@)
3693 * Retrieve the width of a glyph.
3695 * PARAMS
3696 * hdc [I] Device context.
3697 * psc [I/O] Opaque pointer to a script cache.
3698 * glyph [I] Glyph to retrieve the width for.
3699 * abc [O] ABC widths of the glyph.
3701 * RETURNS
3702 * Success: S_OK
3703 * Failure: Non-zero HRESULT value.
3705 HRESULT WINAPI ScriptGetGlyphABCWidth(HDC hdc, SCRIPT_CACHE *psc, WORD glyph, ABC *abc)
3707 HRESULT hr;
3709 TRACE("(%p, %p, 0x%04x, %p)\n", hdc, psc, glyph, abc);
3711 if (!abc) return E_INVALIDARG;
3712 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
3714 if (!get_cache_glyph_widths(psc, glyph, abc))
3716 if (!hdc) return E_PENDING;
3717 if (get_cache_pitch_family(psc) & TMPF_TRUETYPE)
3719 if (!GetCharABCWidthsI(hdc, 0, 1, &glyph, abc)) return S_FALSE;
3721 else
3723 INT width;
3724 if (!GetCharWidthI(hdc, glyph, 1, NULL, &width)) return S_FALSE;
3725 abc->abcB = width;
3726 abc->abcA = abc->abcC = 0;
3728 set_cache_glyph_widths(psc, glyph, abc);
3730 return S_OK;
3733 /***********************************************************************
3734 * ScriptLayout (USP10.@)
3736 * Map embedding levels to visual and/or logical order.
3738 * PARAMS
3739 * runs [I] Size of level array.
3740 * level [I] Array of embedding levels.
3741 * vistolog [O] Map of embedding levels from visual to logical order.
3742 * logtovis [O] Map of embedding levels from logical to visual order.
3744 * RETURNS
3745 * Success: S_OK
3746 * Failure: Non-zero HRESULT value.
3749 HRESULT WINAPI ScriptLayout(int runs, const BYTE *level, int *vistolog, int *logtovis)
3751 int* indices;
3752 int ich;
3754 TRACE("(%d, %p, %p, %p)\n", runs, level, vistolog, logtovis);
3756 if (!level || (!vistolog && !logtovis))
3757 return E_INVALIDARG;
3759 if (!(indices = heap_calloc(runs, sizeof(*indices))))
3760 return E_OUTOFMEMORY;
3762 if (vistolog)
3764 for( ich = 0; ich < runs; ich++)
3765 indices[ich] = ich;
3767 ich = 0;
3768 while (ich < runs)
3769 ich += BIDI_ReorderV2lLevel(0, indices+ich, level+ich, runs - ich, FALSE);
3770 memcpy(vistolog, indices, runs * sizeof(*vistolog));
3773 if (logtovis)
3775 for( ich = 0; ich < runs; ich++)
3776 indices[ich] = ich;
3778 ich = 0;
3779 while (ich < runs)
3780 ich += BIDI_ReorderL2vLevel(0, indices+ich, level+ich, runs - ich, FALSE);
3781 memcpy(logtovis, indices, runs * sizeof(*logtovis));
3783 heap_free(indices);
3785 return S_OK;
3788 /***********************************************************************
3789 * ScriptStringGetLogicalWidths (USP10.@)
3791 * Returns logical widths from a string analysis.
3793 * PARAMS
3794 * ssa [I] string analysis.
3795 * piDx [O] logical widths returned.
3797 * RETURNS
3798 * Success: S_OK
3799 * Failure: a non-zero HRESULT.
3801 HRESULT WINAPI ScriptStringGetLogicalWidths(SCRIPT_STRING_ANALYSIS ssa, int *piDx)
3803 int i, j, next = 0;
3804 StringAnalysis *analysis = ssa;
3806 TRACE("%p, %p\n", ssa, piDx);
3808 if (!analysis) return S_FALSE;
3809 if (!(analysis->ssa_flags & SSA_GLYPHS)) return S_FALSE;
3811 for (i = 0; i < analysis->numItems; i++)
3813 int cChar = analysis->pItem[i+1].iCharPos - analysis->pItem[i].iCharPos;
3814 int direction = 1;
3816 if (analysis->pItem[i].a.fRTL && ! analysis->pItem[i].a.fLogicalOrder)
3817 direction = -1;
3819 for (j = 0; j < cChar; j++)
3821 int k;
3822 int glyph = analysis->glyphs[i].pwLogClust[j];
3823 int clust_size = get_cluster_size(analysis->glyphs[i].pwLogClust,
3824 cChar, j, direction, NULL, NULL);
3825 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);
3827 for (k = 0; k < clust_size; k++)
3829 piDx[next] = advance / clust_size;
3830 next++;
3831 if (k) j++;
3835 return S_OK;
3838 /***********************************************************************
3839 * ScriptStringValidate (USP10.@)
3841 * Validate a string analysis.
3843 * PARAMS
3844 * ssa [I] string analysis.
3846 * RETURNS
3847 * Success: S_OK
3848 * Failure: S_FALSE if invalid sequences are found
3849 * or a non-zero HRESULT if it fails.
3851 HRESULT WINAPI ScriptStringValidate(SCRIPT_STRING_ANALYSIS ssa)
3853 StringAnalysis *analysis = ssa;
3855 TRACE("(%p)\n", ssa);
3857 if (!analysis) return E_INVALIDARG;
3858 return analysis->flags & SCRIPT_STRING_ANALYSIS_FLAGS_INVALID ? S_FALSE : S_OK;
3861 /***********************************************************************
3862 * ScriptString_pSize (USP10.@)
3864 * Retrieve width and height of an analysed string.
3866 * PARAMS
3867 * ssa [I] string analysis.
3869 * RETURNS
3870 * Success: Pointer to a SIZE structure.
3871 * Failure: NULL
3873 const SIZE * WINAPI ScriptString_pSize(SCRIPT_STRING_ANALYSIS ssa)
3875 int i, j;
3876 StringAnalysis *analysis = ssa;
3878 TRACE("(%p)\n", ssa);
3880 if (!analysis) return NULL;
3881 if (!(analysis->ssa_flags & SSA_GLYPHS)) return NULL;
3883 if (!(analysis->flags & SCRIPT_STRING_ANALYSIS_FLAGS_SIZE))
3885 analysis->sz.cy = analysis->glyphs[0].sc->tm.tmHeight;
3887 analysis->sz.cx = 0;
3888 for (i = 0; i < analysis->numItems; i++)
3890 if (analysis->glyphs[i].sc->tm.tmHeight > analysis->sz.cy)
3891 analysis->sz.cy = analysis->glyphs[i].sc->tm.tmHeight;
3892 for (j = 0; j < analysis->glyphs[i].numGlyphs; j++)
3893 analysis->sz.cx += analysis->glyphs[i].piAdvance[j];
3895 analysis->flags |= SCRIPT_STRING_ANALYSIS_FLAGS_SIZE;
3897 return &analysis->sz;
3900 /***********************************************************************
3901 * ScriptString_pLogAttr (USP10.@)
3903 * Retrieve logical attributes of an analysed string.
3905 * PARAMS
3906 * ssa [I] string analysis.
3908 * RETURNS
3909 * Success: Pointer to an array of SCRIPT_LOGATTR structures.
3910 * Failure: NULL
3912 const SCRIPT_LOGATTR * WINAPI ScriptString_pLogAttr(SCRIPT_STRING_ANALYSIS ssa)
3914 StringAnalysis *analysis = ssa;
3916 TRACE("(%p)\n", ssa);
3918 if (!analysis) return NULL;
3919 if (!(analysis->ssa_flags & SSA_BREAK)) return NULL;
3920 return analysis->logattrs;
3923 /***********************************************************************
3924 * ScriptString_pcOutChars (USP10.@)
3926 * Retrieve the length of a string after clipping.
3928 * PARAMS
3929 * ssa [I] String analysis.
3931 * RETURNS
3932 * Success: Pointer to the length.
3933 * Failure: NULL
3935 const int * WINAPI ScriptString_pcOutChars(SCRIPT_STRING_ANALYSIS ssa)
3937 StringAnalysis *analysis = ssa;
3939 TRACE("(%p)\n", ssa);
3941 if (!analysis) return NULL;
3942 return &analysis->clip_len;
3945 /***********************************************************************
3946 * ScriptStringGetOrder (USP10.@)
3948 * Retrieve a glyph order map.
3950 * PARAMS
3951 * ssa [I] String analysis.
3952 * order [I/O] Array of glyph positions.
3954 * RETURNS
3955 * Success: S_OK
3956 * Failure: a non-zero HRESULT.
3958 HRESULT WINAPI ScriptStringGetOrder(SCRIPT_STRING_ANALYSIS ssa, UINT *order)
3960 int i, j;
3961 unsigned int k;
3962 StringAnalysis *analysis = ssa;
3964 TRACE("(%p)\n", ssa);
3966 if (!analysis) return S_FALSE;
3967 if (!(analysis->ssa_flags & SSA_GLYPHS)) return S_FALSE;
3969 /* FIXME: handle RTL scripts */
3970 for (i = 0, k = 0; i < analysis->numItems; i++)
3971 for (j = 0; j < analysis->glyphs[i].numGlyphs; j++, k++)
3972 order[k] = k;
3974 return S_OK;
3977 /***********************************************************************
3978 * ScriptGetLogicalWidths (USP10.@)
3980 * Convert advance widths to logical widths.
3982 * PARAMS
3983 * sa [I] Script analysis.
3984 * nbchars [I] Number of characters.
3985 * nbglyphs [I] Number of glyphs.
3986 * glyph_width [I] Array of glyph widths.
3987 * log_clust [I] Array of logical clusters.
3988 * sva [I] Visual attributes.
3989 * widths [O] Array of logical widths.
3991 * RETURNS
3992 * Success: S_OK
3993 * Failure: a non-zero HRESULT.
3995 HRESULT WINAPI ScriptGetLogicalWidths(const SCRIPT_ANALYSIS *sa, int nbchars, int nbglyphs,
3996 const int *advances, const WORD *log_clust,
3997 const SCRIPT_VISATTR *sva, int *widths)
3999 int i, next = 0, direction;
4001 TRACE("(%p, %d, %d, %p, %p, %p, %p)\n",
4002 sa, nbchars, nbglyphs, advances, log_clust, sva, widths);
4004 if (sa->fRTL && !sa->fLogicalOrder)
4005 direction = -1;
4006 else
4007 direction = 1;
4009 for (i = 0; i < nbchars; i++)
4011 int clust_size = get_cluster_size(log_clust, nbchars, i, direction, NULL, NULL);
4012 int advance = get_glyph_cluster_advance(advances, sva, log_clust, nbglyphs, nbchars, log_clust[i], direction);
4013 int j;
4015 for (j = 0; j < clust_size; j++)
4017 widths[next] = advance / clust_size;
4018 next++;
4019 if (j) i++;
4023 return S_OK;
4026 /***********************************************************************
4027 * ScriptApplyLogicalWidth (USP10.@)
4029 * Generate glyph advance widths.
4031 * PARAMS
4032 * dx [I] Array of logical advance widths.
4033 * num_chars [I] Number of characters.
4034 * num_glyphs [I] Number of glyphs.
4035 * log_clust [I] Array of logical clusters.
4036 * sva [I] Visual attributes.
4037 * advance [I] Array of glyph advance widths.
4038 * sa [I] Script analysis.
4039 * abc [I/O] Summed ABC widths.
4040 * justify [O] Array of glyph advance widths.
4042 * RETURNS
4043 * Success: S_OK
4044 * Failure: a non-zero HRESULT.
4046 HRESULT WINAPI ScriptApplyLogicalWidth(const int *dx, int num_chars, int num_glyphs,
4047 const WORD *log_clust, const SCRIPT_VISATTR *sva,
4048 const int *advance, const SCRIPT_ANALYSIS *sa,
4049 ABC *abc, int *justify)
4051 int i;
4053 FIXME("(%p, %d, %d, %p, %p, %p, %p, %p, %p)\n",
4054 dx, num_chars, num_glyphs, log_clust, sva, advance, sa, abc, justify);
4056 for (i = 0; i < num_chars; i++) justify[i] = advance[i];
4057 return S_OK;
4060 HRESULT WINAPI ScriptJustify(const SCRIPT_VISATTR *sva, const int *advance,
4061 int num_glyphs, int dx, int min_kashida, int *justify)
4063 int i;
4065 FIXME("(%p, %p, %d, %d, %d, %p)\n", sva, advance, num_glyphs, dx, min_kashida, justify);
4067 for (i = 0; i < num_glyphs; i++) justify[i] = advance[i];
4068 return S_OK;
4071 HRESULT WINAPI ScriptGetFontScriptTags( HDC hdc, SCRIPT_CACHE *psc, SCRIPT_ANALYSIS *psa, int cMaxTags, OPENTYPE_TAG *pScriptTags, int *pcTags)
4073 HRESULT hr;
4074 if (!pScriptTags || !pcTags || cMaxTags == 0) return E_INVALIDARG;
4075 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
4077 return SHAPE_GetFontScriptTags(hdc, (ScriptCache *)*psc, psa, cMaxTags, pScriptTags, pcTags);
4080 HRESULT WINAPI ScriptGetFontLanguageTags( HDC hdc, SCRIPT_CACHE *psc, SCRIPT_ANALYSIS *psa, OPENTYPE_TAG tagScript, int cMaxTags, OPENTYPE_TAG *pLangSysTags, int *pcTags)
4082 HRESULT hr;
4083 if (!pLangSysTags || !pcTags || cMaxTags == 0) return E_INVALIDARG;
4084 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
4086 return SHAPE_GetFontLanguageTags(hdc, (ScriptCache *)*psc, psa, tagScript, cMaxTags, pLangSysTags, pcTags);
4089 HRESULT WINAPI ScriptGetFontFeatureTags( HDC hdc, SCRIPT_CACHE *psc, SCRIPT_ANALYSIS *psa, OPENTYPE_TAG tagScript, OPENTYPE_TAG tagLangSys, int cMaxTags, OPENTYPE_TAG *pFeatureTags, int *pcTags)
4091 HRESULT hr;
4092 if (!pFeatureTags || !pcTags || cMaxTags == 0) return E_INVALIDARG;
4093 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
4095 return SHAPE_GetFontFeatureTags(hdc, (ScriptCache *)*psc, psa, tagScript, tagLangSys, cMaxTags, pFeatureTags, pcTags);