usp10: Arabic numerals are written left-to-right.
[wine.git] / dlls / usp10 / usp10.c
blobd654cc8dce0c1981f9908beccf6f2c2948999406
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/unicode.h"
44 WINE_DEFAULT_DEBUG_CHANNEL(uniscribe);
46 typedef struct _scriptRange
48 WORD script;
49 DWORD rangeFirst;
50 DWORD rangeLast;
51 WORD numericScript;
52 WORD punctScript;
53 } scriptRange;
55 static const scriptRange scriptRanges[] = {
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},
299 /* END */
300 { SCRIPT_UNDEFINED, 0, 0, 0}
303 /* this must be in order so that the index matches the Script value */
304 const scriptData scriptInformation[] = {
305 {{SCRIPT_UNDEFINED, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
306 {LANG_NEUTRAL, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
307 0x00000000,
308 {0}},
309 {{Script_Latin, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
310 {LANG_ENGLISH, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
311 MS_MAKE_TAG('l','a','t','n'),
312 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
313 {{Script_CR, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
314 {LANG_NEUTRAL, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
315 0x00000000,
316 {0}},
317 {{Script_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
318 {LANG_ENGLISH, 1, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
319 0x00000000,
320 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
321 {{Script_Control, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
322 {LANG_ENGLISH, 0, 1, 0, 0, ANSI_CHARSET, 1, 0, 0, 0, 0, 0, 1, 0, 0},
323 0x00000000,
324 {0}},
325 {{Script_Punctuation, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
326 {LANG_NEUTRAL, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
327 0x00000000,
328 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
329 {{Script_Arabic, 1, 1, 0, 0, 0, 0, { 1,0,0,0,0,0,0,0,0,0,0}},
330 {LANG_ARABIC, 0, 1, 0, 0, ARABIC_CHARSET, 0, 0, 0, 0, 0, 0, 1, 1, 0},
331 MS_MAKE_TAG('a','r','a','b'),
332 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
333 {{Script_Arabic_Numeric, 0, 1, 0, 0, 0, 0, { 1,0,0,0,0,0,0,0,0,0,0}},
334 {LANG_ARABIC, 1, 1, 0, 0, ARABIC_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
335 MS_MAKE_TAG('a','r','a','b'),
336 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
337 {{Script_Hebrew, 1, 1, 0, 0, 0, 0, { 1,0,0,0,0,0,0,0,0,0,0}},
338 {LANG_HEBREW, 0, 1, 0, 1, HEBREW_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
339 MS_MAKE_TAG('h','e','b','r'),
340 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
341 {{Script_Syriac, 1, 1, 0, 0, 0, 0, { 1,0,0,0,0,0,0,0,0,0,0}},
342 {LANG_SYRIAC, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 1, 0},
343 MS_MAKE_TAG('s','y','r','c'),
344 {'E','s','t','r','a','n','g','e','l','o',' ','E','d','e','s','s','a',0}},
345 {{Script_Persian, 0, 1, 0, 0, 0, 0, { 2,0,0,0,0,0,0,0,0,0,0}},
346 {LANG_PERSIAN, 1, 1, 0, 0, ARABIC_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
347 MS_MAKE_TAG('a','r','a','b'),
348 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
349 {{Script_Thaana, 1, 1, 0, 0, 0, 0, { 1,0,0,0,0,0,0,0,0,0,0}},
350 {LANG_DIVEHI, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
351 MS_MAKE_TAG('t','h','a','a'),
352 {'M','V',' ','B','o','l','i',0}},
353 {{Script_Greek, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
354 {LANG_GREEK, 0, 0, 0, 0, GREEK_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
355 MS_MAKE_TAG('g','r','e','k'),
356 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
357 {{Script_Cyrillic, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
358 {LANG_RUSSIAN, 0, 0, 0, 0, RUSSIAN_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
359 MS_MAKE_TAG('c','y','r','l'),
360 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
361 {{Script_Armenian, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
362 {LANG_ARMENIAN, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
363 MS_MAKE_TAG('a','r','m','n'),
364 {'S','y','l','f','a','e','n',0}},
365 {{Script_Georgian, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
366 {LANG_GEORGIAN, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
367 MS_MAKE_TAG('g','e','o','r'),
368 {'S','y','l','f','a','e','n',0}},
369 {{Script_Sinhala, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
370 {LANG_SINHALESE, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
371 MS_MAKE_TAG('s','i','n','h'),
372 {'I','s','k','o','o','l','a',' ','P','o','t','a',0}},
373 {{Script_Tibetan, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
374 {LANG_TIBETAN, 0, 1, 1, 1, DEFAULT_CHARSET, 0, 0, 1, 0, 1, 0, 0, 0, 0},
375 MS_MAKE_TAG('t','i','b','t'),
376 {'M','i','c','r','o','s','o','f','t',' ','H','i','m','a','l','a','y','a',0}},
377 {{Script_Tibetan_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
378 {LANG_TIBETAN, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
379 MS_MAKE_TAG('t','i','b','t'),
380 {'M','i','c','r','o','s','o','f','t',' ','H','i','m','a','l','a','y','a',0}},
381 {{Script_Phags_pa, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
382 {LANG_MONGOLIAN, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
383 MS_MAKE_TAG('p','h','a','g'),
384 {'M','i','c','r','o','s','o','f','t',' ','P','h','a','g','s','P','a',0}},
385 {{Script_Thai, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
386 {LANG_THAI, 0, 1, 1, 1, THAI_CHARSET, 0, 0, 1, 0, 1, 0, 0, 0, 1},
387 MS_MAKE_TAG('t','h','a','i'),
388 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
389 {{Script_Thai_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
390 {LANG_THAI, 1, 1, 0, 0, THAI_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
391 MS_MAKE_TAG('t','h','a','i'),
392 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
393 {{Script_Lao, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
394 {LANG_LAO, 0, 1, 1, 1, DEFAULT_CHARSET, 0, 0, 1, 0, 1, 0, 0, 0, 0},
395 MS_MAKE_TAG('l','a','o',' '),
396 {'D','o','k','C','h','a','m','p','a',0}},
397 {{Script_Lao_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
398 {LANG_LAO, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
399 MS_MAKE_TAG('l','a','o',' '),
400 {'D','o','k','C','h','a','m','p','a',0}},
401 {{Script_Devanagari, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
402 {LANG_HINDI, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
403 MS_MAKE_TAG('d','e','v','a'),
404 {'M','a','n','g','a','l',0}},
405 {{Script_Devanagari_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
406 {LANG_HINDI, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
407 MS_MAKE_TAG('d','e','v','a'),
408 {'M','a','n','g','a','l',0}},
409 {{Script_Bengali, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
410 {LANG_BENGALI, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
411 MS_MAKE_TAG('b','e','n','g'),
412 {'V','r','i','n','d','a',0}},
413 {{Script_Bengali_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
414 {LANG_BENGALI, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
415 MS_MAKE_TAG('b','e','n','g'),
416 {'V','r','i','n','d','a',0}},
417 {{Script_Bengali_Currency, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
418 {LANG_BENGALI, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
419 MS_MAKE_TAG('b','e','n','g'),
420 {'V','r','i','n','d','a',0}},
421 {{Script_Gurmukhi, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
422 {LANG_PUNJABI, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
423 MS_MAKE_TAG('g','u','r','u'),
424 {'R','a','a','v','i',0}},
425 {{Script_Gurmukhi_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
426 {LANG_PUNJABI, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
427 MS_MAKE_TAG('g','u','r','u'),
428 {'R','a','a','v','i',0}},
429 {{Script_Gujarati, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
430 {LANG_GUJARATI, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
431 MS_MAKE_TAG('g','u','j','r'),
432 {'S','h','r','u','t','i',0}},
433 {{Script_Gujarati_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
434 {LANG_GUJARATI, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
435 MS_MAKE_TAG('g','u','j','r'),
436 {'S','h','r','u','t','i',0}},
437 {{Script_Gujarati_Currency, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
438 {LANG_GUJARATI, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
439 MS_MAKE_TAG('g','u','j','r'),
440 {'S','h','r','u','t','i',0}},
441 {{Script_Oriya, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
442 {LANG_ORIYA, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
443 MS_MAKE_TAG('o','r','y','a'),
444 {'K','a','l','i','n','g','a',0}},
445 {{Script_Oriya_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
446 {LANG_ORIYA, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
447 MS_MAKE_TAG('o','r','y','a'),
448 {'K','a','l','i','n','g','a',0}},
449 {{Script_Tamil, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
450 {LANG_TAMIL, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
451 MS_MAKE_TAG('t','a','m','l'),
452 {'L','a','t','h','a',0}},
453 {{Script_Tamil_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
454 {LANG_TAMIL, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
455 MS_MAKE_TAG('t','a','m','l'),
456 {'L','a','t','h','a',0}},
457 {{Script_Telugu, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
458 {LANG_TELUGU, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
459 MS_MAKE_TAG('t','e','l','u'),
460 {'G','a','u','t','a','m','i',0}},
461 {{Script_Telugu_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
462 {LANG_TELUGU, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
463 MS_MAKE_TAG('t','e','l','u'),
464 {'G','a','u','t','a','m','i',0}},
465 {{Script_Kannada, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
466 {LANG_KANNADA, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
467 MS_MAKE_TAG('k','n','d','a'),
468 {'T','u','n','g','a',0}},
469 {{Script_Kannada_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
470 {LANG_KANNADA, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
471 MS_MAKE_TAG('k','n','d','a'),
472 {'T','u','n','g','a',0}},
473 {{Script_Malayalam, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
474 {LANG_MALAYALAM, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
475 MS_MAKE_TAG('m','l','y','m'),
476 {'K','a','r','t','i','k','a',0}},
477 {{Script_Malayalam_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
478 {LANG_MALAYALAM, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
479 MS_MAKE_TAG('m','l','y','m'),
480 {'K','a','r','t','i','k','a',0}},
481 {{Script_Diacritical, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
482 {LANG_ENGLISH, 0, 1, 0, 1, ANSI_CHARSET, 0, 0, 0, 0, 0, 1, 1, 0, 0},
483 0x00000000,
484 {0}},
485 {{Script_Punctuation2, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
486 {LANG_ENGLISH, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
487 MS_MAKE_TAG('l','a','t','n'),
488 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
489 {{Script_Numeric2, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
490 {LANG_ENGLISH, 1, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
491 0x00000000,
492 {0}},
493 {{Script_Myanmar, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
494 {0x55, 0, 1, 1, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
495 MS_MAKE_TAG('m','y','m','r'),
496 {'M','y','a','n','m','a','r',' ','T','e','x','t',0}},
497 {{Script_Myanmar_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
498 {0x55, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
499 MS_MAKE_TAG('m','y','m','r'),
500 {0}},
501 {{Script_Tai_Le, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
502 {0, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
503 MS_MAKE_TAG('t','a','l','e'),
504 {'M','i','c','r','o','s','o','f','t',' ','T','a','i',' ','L','e',0}},
505 {{Script_New_Tai_Lue, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
506 {0, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
507 MS_MAKE_TAG('t','a','l','u'),
508 {'M','i','c','r','o','s','o','f','t',' ','N','e','w',' ','T','a','i',' ','L','u','e',0}},
509 {{Script_New_Tai_Lue_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
510 {0, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
511 MS_MAKE_TAG('t','a','l','u'),
512 {'M','i','c','r','o','s','o','f','t',' ','N','e','w',' ','T','a','i',' ','L','u','e',0}},
513 {{Script_Khmer, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
514 {0x53, 0, 1, 1, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
515 MS_MAKE_TAG('k','h','m','r'),
516 {'D','a','u','n','P','e','n','h',0}},
517 {{Script_Khmer_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
518 {0x53, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
519 MS_MAKE_TAG('k','h','m','r'),
520 {'D','a','u','n','P','e','n','h',0}},
521 {{Script_CJK_Han, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
522 {LANG_ENGLISH, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
523 MS_MAKE_TAG('h','a','n','i'),
524 {0}},
525 {{Script_Ideograph, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
526 {LANG_ENGLISH, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
527 MS_MAKE_TAG('h','a','n','i'),
528 {0}},
529 {{Script_Bopomofo, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
530 {LANG_ENGLISH, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
531 MS_MAKE_TAG('b','o','p','o'),
532 {0}},
533 {{Script_Kana, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
534 {LANG_ENGLISH, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
535 MS_MAKE_TAG('k','a','n','a'),
536 {0}},
537 {{Script_Hangul, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
538 {LANG_KOREAN, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
539 MS_MAKE_TAG('h','a','n','g'),
540 {0}},
541 {{Script_Yi, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
542 {LANG_ENGLISH, 0, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
543 MS_MAKE_TAG('y','i',' ',' '),
544 {'M','i','c','r','o','s','o','f','t',' ','Y','i',' ','B','a','i','t','i',0}},
545 {{Script_Ethiopic, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
546 {0x5e, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
547 MS_MAKE_TAG('e','t','h','i'),
548 {'N','y','a','l','a',0}},
549 {{Script_Ethiopic_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
550 {0x5e, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
551 MS_MAKE_TAG('e','t','h','i'),
552 {'N','y','a','l','a',0}},
553 {{Script_Mongolian, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
554 {LANG_MONGOLIAN, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
555 MS_MAKE_TAG('m','o','n','g'),
556 {'M','o','n','g','o','l','i','a','n',' ','B','a','i','t','i',0}},
557 {{Script_Mongolian_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
558 {LANG_MONGOLIAN, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
559 MS_MAKE_TAG('m','o','n','g'),
560 {'M','o','n','g','o','l','i','a','n',' ','B','a','i','t','i',0}},
561 {{Script_Tifinagh, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
562 {0, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
563 MS_MAKE_TAG('t','f','n','g'),
564 {'E','b','r','i','m','a',0}},
565 {{Script_NKo, 1, 1, 0, 0, 0, 0, { 1,0,0,0,0,0,0,0,0,0,0}},
566 {0, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
567 MS_MAKE_TAG('n','k','o',' '),
568 {'E','b','r','i','m','a',0}},
569 {{Script_Vai, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
570 {0, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
571 MS_MAKE_TAG('v','a','i',' '),
572 {'E','b','r','i','m','a',0}},
573 {{Script_Vai_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
574 {0, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
575 MS_MAKE_TAG('v','a','i',' '),
576 {'E','b','r','i','m','a',0}},
577 {{Script_Cherokee, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
578 {0x5c, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
579 MS_MAKE_TAG('c','h','e','r'),
580 {'P','l','a','n','t','a','g','e','n','e','t',' ','C','h','e','r','o','k','e','e',0}},
581 {{Script_Canadian, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
582 {0x5d, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
583 MS_MAKE_TAG('c','a','n','s'),
584 {'E','u','p','h','e','m','i','a',0}},
585 {{Script_Ogham, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
586 {0, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
587 MS_MAKE_TAG('o','g','a','m'),
588 {'S','e','g','o','e',' ','U','I',' ','S','y','m','b','o','l',0}},
589 {{Script_Runic, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
590 {0, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
591 MS_MAKE_TAG('r','u','n','r'),
592 {'S','e','g','o','e',' ','U','I',' ','S','y','m','b','o','l',0}},
593 {{Script_Braille, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
594 {LANG_ENGLISH, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
595 MS_MAKE_TAG('b','r','a','i'),
596 {'S','e','g','o','e',' ','U','I',' ','S','y','m','b','o','l',0}},
597 {{Script_Surrogates, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
598 {LANG_ENGLISH, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
599 0x00000000,
600 {0}},
601 {{Script_Private, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
602 {0, 0, 0, 0, 0, DEFAULT_CHARSET, 0, 1, 0, 0, 0, 0, 1, 0, 0},
603 0x00000000,
604 {0}},
605 {{Script_Deseret, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
606 {0, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
607 MS_MAKE_TAG('d','s','r','t'),
608 {'S','e','g','o','e',' ','U','I',' ','S','y','m','b','o','l',0}},
609 {{Script_Osmanya, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
610 {0, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
611 MS_MAKE_TAG('o','s','m','a'),
612 {'E','b','r','i','m','a',0}},
613 {{Script_Osmanya_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
614 {0, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
615 MS_MAKE_TAG('o','s','m','a'),
616 {'E','b','r','i','m','a',0}},
617 {{Script_MathAlpha, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
618 {0, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
619 MS_MAKE_TAG('m','a','t','h'),
620 {'C','a','m','b','r','i','a',' ','M','a','t','h',0}},
621 {{Script_Hebrew_Currency, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
622 {LANG_HEBREW, 0, 1, 0, 0, HEBREW_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
623 MS_MAKE_TAG('h','e','b','r'),
624 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
625 {{Script_Vietnamese_Currency, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
626 {LANG_VIETNAMESE, 0, 0, 0, 0, VIETNAMESE_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
627 MS_MAKE_TAG('l','a','t','n'),
628 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
629 {{Script_Thai_Currency, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
630 {LANG_THAI, 0, 1, 0, 0, THAI_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
631 MS_MAKE_TAG('t','h','a','i'),
632 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
635 static const SCRIPT_PROPERTIES *script_props[] =
637 &scriptInformation[0].props, &scriptInformation[1].props,
638 &scriptInformation[2].props, &scriptInformation[3].props,
639 &scriptInformation[4].props, &scriptInformation[5].props,
640 &scriptInformation[6].props, &scriptInformation[7].props,
641 &scriptInformation[8].props, &scriptInformation[9].props,
642 &scriptInformation[10].props, &scriptInformation[11].props,
643 &scriptInformation[12].props, &scriptInformation[13].props,
644 &scriptInformation[14].props, &scriptInformation[15].props,
645 &scriptInformation[16].props, &scriptInformation[17].props,
646 &scriptInformation[18].props, &scriptInformation[19].props,
647 &scriptInformation[20].props, &scriptInformation[21].props,
648 &scriptInformation[22].props, &scriptInformation[23].props,
649 &scriptInformation[24].props, &scriptInformation[25].props,
650 &scriptInformation[26].props, &scriptInformation[27].props,
651 &scriptInformation[28].props, &scriptInformation[29].props,
652 &scriptInformation[30].props, &scriptInformation[31].props,
653 &scriptInformation[32].props, &scriptInformation[33].props,
654 &scriptInformation[34].props, &scriptInformation[35].props,
655 &scriptInformation[36].props, &scriptInformation[37].props,
656 &scriptInformation[38].props, &scriptInformation[39].props,
657 &scriptInformation[40].props, &scriptInformation[41].props,
658 &scriptInformation[42].props, &scriptInformation[43].props,
659 &scriptInformation[44].props, &scriptInformation[45].props,
660 &scriptInformation[46].props, &scriptInformation[47].props,
661 &scriptInformation[48].props, &scriptInformation[49].props,
662 &scriptInformation[50].props, &scriptInformation[51].props,
663 &scriptInformation[52].props, &scriptInformation[53].props,
664 &scriptInformation[54].props, &scriptInformation[55].props,
665 &scriptInformation[56].props, &scriptInformation[57].props,
666 &scriptInformation[58].props, &scriptInformation[59].props,
667 &scriptInformation[60].props, &scriptInformation[61].props,
668 &scriptInformation[62].props, &scriptInformation[63].props,
669 &scriptInformation[64].props, &scriptInformation[65].props,
670 &scriptInformation[66].props, &scriptInformation[67].props,
671 &scriptInformation[68].props, &scriptInformation[69].props,
672 &scriptInformation[70].props, &scriptInformation[71].props,
673 &scriptInformation[72].props, &scriptInformation[73].props,
674 &scriptInformation[74].props, &scriptInformation[75].props,
675 &scriptInformation[76].props, &scriptInformation[77].props,
676 &scriptInformation[78].props, &scriptInformation[79].props,
677 &scriptInformation[80].props, &scriptInformation[81].props
680 typedef struct {
681 ScriptCache *sc;
682 int numGlyphs;
683 WORD* glyphs;
684 WORD* pwLogClust;
685 int* piAdvance;
686 SCRIPT_VISATTR* psva;
687 GOFFSET* pGoffset;
688 ABC* abc;
689 int iMaxPosX;
690 HFONT fallbackFont;
691 } StringGlyphs;
693 typedef struct {
694 HDC hdc;
695 DWORD dwFlags;
696 BOOL invalid;
697 int clip_len;
698 int cItems;
699 int cMaxGlyphs;
700 SCRIPT_ITEM* pItem;
701 int numItems;
702 StringGlyphs* glyphs;
703 SCRIPT_LOGATTR* logattrs;
704 SIZE* sz;
705 int* logical2visual;
706 } StringAnalysis;
708 typedef struct {
709 BOOL ascending;
710 WORD target;
711 } FindGlyph_struct;
713 static inline void *heap_alloc(SIZE_T size)
715 return HeapAlloc(GetProcessHeap(), 0, size);
718 static inline void *heap_alloc_zero(SIZE_T size)
720 return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);
723 static inline BOOL heap_free(LPVOID mem)
725 return HeapFree(GetProcessHeap(), 0, mem);
728 /* TODO Fix font properties on Arabic locale */
729 static inline BOOL set_cache_font_properties(const HDC hdc, ScriptCache *sc)
731 if (!sc->sfnt)
733 sc->sfp.wgBlank = sc->tm.tmBreakChar;
734 sc->sfp.wgDefault = sc->tm.tmDefaultChar;
735 sc->sfp.wgInvalid = sc->sfp.wgBlank;
736 sc->sfp.wgKashida = 0xFFFF;
737 sc->sfp.iKashidaWidth = 0;
739 else
741 static const WCHAR chars[4] = {0x0020, 0x200B, 0xF71B, 0x0640};
742 /* U+0020: numeric space
743 U+200B: zero width space
744 U+F71B: unknown char found by black box testing
745 U+0640: kashida */
746 WORD gi[4];
748 if (GetGlyphIndicesW(hdc, chars, 4, gi, GGI_MARK_NONEXISTING_GLYPHS) != GDI_ERROR)
750 if(gi[0] != 0xFFFF) /* 0xFFFF: index of default non exist char */
751 sc->sfp.wgBlank = gi[0];
752 else
753 sc->sfp.wgBlank = 0;
755 sc->sfp.wgDefault = 0;
757 if (gi[2] != 0xFFFF)
758 sc->sfp.wgInvalid = gi[2];
759 else if (gi[1] != 0xFFFF)
760 sc->sfp.wgInvalid = gi[1];
761 else if (gi[0] != 0xFFFF)
762 sc->sfp.wgInvalid = gi[0];
763 else
764 sc->sfp.wgInvalid = 0;
766 sc->sfp.wgKashida = gi[3];
768 sc->sfp.iKashidaWidth = 0; /* TODO */
770 else
771 return FALSE;
773 return TRUE;
776 static inline void get_cache_font_properties(SCRIPT_FONTPROPERTIES *sfp, ScriptCache *sc)
778 sfp->wgBlank = sc->sfp.wgBlank;
779 sfp->wgDefault = sc->sfp.wgDefault;
780 sfp->wgInvalid = sc->sfp.wgInvalid;
781 sfp->wgKashida = sc->sfp.wgKashida;
782 sfp->iKashidaWidth = sc->sfp.iKashidaWidth;
785 static inline LONG get_cache_height(SCRIPT_CACHE *psc)
787 return ((ScriptCache *)*psc)->tm.tmHeight;
790 static inline BYTE get_cache_pitch_family(SCRIPT_CACHE *psc)
792 return ((ScriptCache *)*psc)->tm.tmPitchAndFamily;
795 static inline WORD get_cache_glyph(SCRIPT_CACHE *psc, DWORD c)
797 CacheGlyphPage *page = ((ScriptCache *)*psc)->page[c / 0x10000];
798 WORD *block;
800 if (!page) return 0;
801 block = page->glyphs[(c % 0x10000) >> GLYPH_BLOCK_SHIFT];
802 if (!block) return 0;
803 return block[(c % 0x10000) & GLYPH_BLOCK_MASK];
806 static inline WORD set_cache_glyph(SCRIPT_CACHE *psc, WCHAR c, WORD glyph)
808 CacheGlyphPage **page = &((ScriptCache *)*psc)->page[c / 0x10000];
809 WORD **block;
810 if (!*page && !(*page = heap_alloc_zero(sizeof(CacheGlyphPage)))) return 0;
812 block = &(*page)->glyphs[(c % 0x10000) >> GLYPH_BLOCK_SHIFT];
813 if (!*block && !(*block = heap_alloc_zero(sizeof(WORD) * GLYPH_BLOCK_SIZE))) return 0;
814 return ((*block)[(c % 0x10000) & GLYPH_BLOCK_MASK] = glyph);
817 static inline BOOL get_cache_glyph_widths(SCRIPT_CACHE *psc, WORD glyph, ABC *abc)
819 static const ABC nil;
820 ABC *block = ((ScriptCache *)*psc)->widths[glyph >> GLYPH_BLOCK_SHIFT];
822 if (!block || !memcmp(&block[glyph & GLYPH_BLOCK_MASK], &nil, sizeof(ABC))) return FALSE;
823 memcpy(abc, &block[glyph & GLYPH_BLOCK_MASK], sizeof(ABC));
824 return TRUE;
827 static inline BOOL set_cache_glyph_widths(SCRIPT_CACHE *psc, WORD glyph, ABC *abc)
829 ABC **block = &((ScriptCache *)*psc)->widths[glyph >> GLYPH_BLOCK_SHIFT];
831 if (!*block && !(*block = heap_alloc_zero(sizeof(ABC) * GLYPH_BLOCK_SIZE))) return FALSE;
832 memcpy(&(*block)[glyph & GLYPH_BLOCK_MASK], abc, sizeof(ABC));
833 return TRUE;
836 static HRESULT init_script_cache(const HDC hdc, SCRIPT_CACHE *psc)
838 ScriptCache *sc;
839 int size;
841 if (!psc) return E_INVALIDARG;
842 if (*psc) return S_OK;
843 if (!hdc) return E_PENDING;
845 if (!(sc = heap_alloc_zero(sizeof(ScriptCache)))) return E_OUTOFMEMORY;
846 if (!GetTextMetricsW(hdc, &sc->tm))
848 heap_free(sc);
849 return E_INVALIDARG;
851 size = GetOutlineTextMetricsW(hdc, 0, NULL);
852 if (size)
854 sc->otm = heap_alloc(size);
855 sc->otm->otmSize = size;
856 GetOutlineTextMetricsW(hdc, size, sc->otm);
858 if (!GetObjectW(GetCurrentObject(hdc, OBJ_FONT), sizeof(LOGFONTW), &sc->lf))
860 heap_free(sc);
861 return E_INVALIDARG;
863 sc->sfnt = (GetFontData(hdc, MS_MAKE_TAG('h','e','a','d'), 0, NULL, 0)!=GDI_ERROR);
864 if (!set_cache_font_properties(hdc, sc))
866 heap_free(sc);
867 return E_INVALIDARG;
869 *psc = sc;
870 TRACE("<- %p\n", sc);
871 return S_OK;
874 static WCHAR mirror_char( WCHAR ch )
876 extern const WCHAR wine_mirror_map[] DECLSPEC_HIDDEN;
877 return ch + wine_mirror_map[wine_mirror_map[ch >> 8] + (ch & 0xff)];
880 static inline DWORD decode_surrogate_pair(LPCWSTR str, INT index, INT end)
882 if (index < end-1 && IS_SURROGATE_PAIR(str[index],str[index+1]))
884 DWORD ch = 0x10000 + ((str[index] - 0xd800) << 10) + (str[index+1] - 0xdc00);
885 TRACE("Surrogate Pair %x %x => %x\n",str[index], str[index+1], ch);
886 return ch;
888 return 0;
891 static WORD get_char_script( LPCWSTR str, INT index, INT end, INT *consumed)
893 static const WCHAR latin_punc[] = {'#','$','&','\'',',',';','<','>','?','@','\\','^','_','`','{','|','}','~', 0x00a0, 0};
894 WORD type = 0, type2 = 0;
895 DWORD ch;
896 int i;
898 *consumed = 1;
900 if (str[index] == 0xc || str[index] == 0x20 || str[index] == 0x202f)
901 return Script_CR;
903 /* These punctuation characters are separated out as Latin punctuation */
904 if (strchrW(latin_punc,str[index]))
905 return Script_Punctuation2;
907 /* These chars are itemized as Punctuation by Windows */
908 if (str[index] == 0x2212 || str[index] == 0x2044)
909 return Script_Punctuation;
911 /* Currency Symbols by Unicode point */
912 switch (str[index])
914 case 0x09f2:
915 case 0x09f3: return Script_Bengali_Currency;
916 case 0x0af1: return Script_Gujarati_Currency;
917 case 0x0e3f: return Script_Thai_Currency;
918 case 0x20aa: return Script_Hebrew_Currency;
919 case 0x20ab: return Script_Vietnamese_Currency;
920 case 0xfb29: return Script_Hebrew_Currency;
923 GetStringTypeW(CT_CTYPE1, &str[index], 1, &type);
924 GetStringTypeW(CT_CTYPE2, &str[index], 1, &type2);
926 if (type == 0)
927 return SCRIPT_UNDEFINED;
929 if (type & C1_CNTRL)
930 return Script_Control;
932 ch = decode_surrogate_pair(str, index, end);
933 if (ch)
934 *consumed = 2;
935 else
936 ch = str[index];
938 i = 0;
941 if (ch < scriptRanges[i].rangeFirst || scriptRanges[i].script == SCRIPT_UNDEFINED)
942 break;
944 if (ch >= scriptRanges[i].rangeFirst && ch <= scriptRanges[i].rangeLast)
946 if (scriptRanges[i].numericScript && (type & C1_DIGIT || type2 == C2_ARABICNUMBER))
947 return scriptRanges[i].numericScript;
948 if (scriptRanges[i].punctScript && type & C1_PUNCT)
949 return scriptRanges[i].punctScript;
950 return scriptRanges[i].script;
952 i++;
953 } while (1);
955 return SCRIPT_UNDEFINED;
958 static int compare_FindGlyph(const void *a, const void* b)
960 const FindGlyph_struct *find = (FindGlyph_struct*)a;
961 const WORD *idx= (WORD*)b;
962 int rc = 0;
964 if ( find->target > *idx)
965 rc = 1;
966 else if (find->target < *idx)
967 rc = -1;
969 if (!find->ascending)
970 rc *= -1;
971 return rc;
974 int USP10_FindGlyphInLogClust(const WORD* pwLogClust, int cChars, WORD target)
976 FindGlyph_struct fgs;
977 WORD *ptr;
978 INT k;
980 if (pwLogClust[0] < pwLogClust[cChars-1])
981 fgs.ascending = TRUE;
982 else
983 fgs.ascending = FALSE;
985 fgs.target = target;
986 ptr = bsearch(&fgs, pwLogClust, cChars, sizeof(WORD), compare_FindGlyph);
988 if (!ptr)
989 return -1;
991 for (k = (ptr - pwLogClust)-1; k >= 0 && pwLogClust[k] == target; k--)
993 k++;
995 return k;
998 /***********************************************************************
999 * ScriptFreeCache (USP10.@)
1001 * Free a script cache.
1003 * PARAMS
1004 * psc [I/O] Script cache.
1006 * RETURNS
1007 * Success: S_OK
1008 * Failure: Non-zero HRESULT value.
1010 HRESULT WINAPI ScriptFreeCache(SCRIPT_CACHE *psc)
1012 TRACE("%p\n", psc);
1014 if (psc && *psc)
1016 unsigned int i;
1017 INT n;
1018 for (i = 0; i < GLYPH_MAX / GLYPH_BLOCK_SIZE; i++)
1020 heap_free(((ScriptCache *)*psc)->widths[i]);
1022 for (i = 0; i < 0x10; i++)
1024 unsigned int j;
1025 if (((ScriptCache *)*psc)->page[i])
1026 for (j = 0; j < GLYPH_MAX / GLYPH_BLOCK_SIZE; j++)
1027 heap_free(((ScriptCache *)*psc)->page[i]->glyphs[j]);
1028 heap_free(((ScriptCache *)*psc)->page[i]);
1030 heap_free(((ScriptCache *)*psc)->GSUB_Table);
1031 heap_free(((ScriptCache *)*psc)->GDEF_Table);
1032 heap_free(((ScriptCache *)*psc)->CMAP_Table);
1033 heap_free(((ScriptCache *)*psc)->GPOS_Table);
1034 for (n = 0; n < ((ScriptCache *)*psc)->script_count; n++)
1036 int j;
1037 for (j = 0; j < ((ScriptCache *)*psc)->scripts[n].language_count; j++)
1039 int k;
1040 for (k = 0; k < ((ScriptCache *)*psc)->scripts[n].languages[j].feature_count; k++)
1041 heap_free(((ScriptCache *)*psc)->scripts[n].languages[j].features[k].lookups);
1042 heap_free(((ScriptCache *)*psc)->scripts[n].languages[j].features);
1044 for (j = 0; j < ((ScriptCache *)*psc)->scripts[n].default_language.feature_count; j++)
1045 heap_free(((ScriptCache *)*psc)->scripts[n].default_language.features[j].lookups);
1046 heap_free(((ScriptCache *)*psc)->scripts[n].default_language.features);
1047 heap_free(((ScriptCache *)*psc)->scripts[n].languages);
1049 heap_free(((ScriptCache *)*psc)->scripts);
1050 heap_free(((ScriptCache *)*psc)->otm);
1051 heap_free(*psc);
1052 *psc = NULL;
1054 return S_OK;
1057 /***********************************************************************
1058 * ScriptGetProperties (USP10.@)
1060 * Retrieve a list of script properties.
1062 * PARAMS
1063 * props [I] Pointer to an array of SCRIPT_PROPERTIES pointers.
1064 * num [I] Pointer to the number of scripts.
1066 * RETURNS
1067 * Success: S_OK
1068 * Failure: Non-zero HRESULT value.
1070 * NOTES
1071 * Behaviour matches WinXP.
1073 HRESULT WINAPI ScriptGetProperties(const SCRIPT_PROPERTIES ***props, int *num)
1075 TRACE("(%p,%p)\n", props, num);
1077 if (!props && !num) return E_INVALIDARG;
1079 if (num) *num = sizeof(script_props)/sizeof(script_props[0]);
1080 if (props) *props = script_props;
1082 return S_OK;
1085 /***********************************************************************
1086 * ScriptGetFontProperties (USP10.@)
1088 * Get information on special glyphs.
1090 * PARAMS
1091 * hdc [I] Device context.
1092 * psc [I/O] Opaque pointer to a script cache.
1093 * sfp [O] Font properties structure.
1095 HRESULT WINAPI ScriptGetFontProperties(HDC hdc, SCRIPT_CACHE *psc, SCRIPT_FONTPROPERTIES *sfp)
1097 HRESULT hr;
1099 TRACE("%p,%p,%p\n", hdc, psc, sfp);
1101 if (!sfp) return E_INVALIDARG;
1102 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
1104 if (sfp->cBytes != sizeof(SCRIPT_FONTPROPERTIES))
1105 return E_INVALIDARG;
1107 get_cache_font_properties(sfp, *psc);
1109 return S_OK;
1112 /***********************************************************************
1113 * ScriptRecordDigitSubstitution (USP10.@)
1115 * Record digit substitution settings for a given locale.
1117 * PARAMS
1118 * locale [I] Locale identifier.
1119 * sds [I] Structure to record substitution settings.
1121 * RETURNS
1122 * Success: S_OK
1123 * Failure: E_POINTER if sds is NULL, E_INVALIDARG otherwise.
1125 * SEE ALSO
1126 * http://blogs.msdn.com/michkap/archive/2006/02/22/536877.aspx
1128 HRESULT WINAPI ScriptRecordDigitSubstitution(LCID locale, SCRIPT_DIGITSUBSTITUTE *sds)
1130 DWORD plgid, sub;
1132 TRACE("0x%x, %p\n", locale, sds);
1134 /* This implementation appears to be correct for all languages, but it's
1135 * not clear if sds->DigitSubstitute is ever set to anything except
1136 * CONTEXT or NONE in reality */
1138 if (!sds) return E_POINTER;
1140 locale = ConvertDefaultLocale(locale);
1142 if (!IsValidLocale(locale, LCID_INSTALLED))
1143 return E_INVALIDARG;
1145 plgid = PRIMARYLANGID(LANGIDFROMLCID(locale));
1146 sds->TraditionalDigitLanguage = plgid;
1148 if (plgid == LANG_ARABIC || plgid == LANG_FARSI)
1149 sds->NationalDigitLanguage = plgid;
1150 else
1151 sds->NationalDigitLanguage = LANG_ENGLISH;
1153 if (!GetLocaleInfoW(locale, LOCALE_IDIGITSUBSTITUTION | LOCALE_RETURN_NUMBER,
1154 (LPWSTR)&sub, sizeof(sub)/sizeof(WCHAR))) return E_INVALIDARG;
1156 switch (sub)
1158 case 0:
1159 if (plgid == LANG_ARABIC || plgid == LANG_FARSI)
1160 sds->DigitSubstitute = SCRIPT_DIGITSUBSTITUTE_CONTEXT;
1161 else
1162 sds->DigitSubstitute = SCRIPT_DIGITSUBSTITUTE_NONE;
1163 break;
1164 case 1:
1165 sds->DigitSubstitute = SCRIPT_DIGITSUBSTITUTE_NONE;
1166 break;
1167 case 2:
1168 sds->DigitSubstitute = SCRIPT_DIGITSUBSTITUTE_NATIONAL;
1169 break;
1170 default:
1171 sds->DigitSubstitute = SCRIPT_DIGITSUBSTITUTE_TRADITIONAL;
1172 break;
1175 sds->dwReserved = 0;
1176 return S_OK;
1179 /***********************************************************************
1180 * ScriptApplyDigitSubstitution (USP10.@)
1182 * Apply digit substitution settings.
1184 * PARAMS
1185 * sds [I] Structure with recorded substitution settings.
1186 * sc [I] Script control structure.
1187 * ss [I] Script state structure.
1189 * RETURNS
1190 * Success: S_OK
1191 * Failure: E_INVALIDARG if sds is invalid. Otherwise an HRESULT.
1193 HRESULT WINAPI ScriptApplyDigitSubstitution(const SCRIPT_DIGITSUBSTITUTE *sds,
1194 SCRIPT_CONTROL *sc, SCRIPT_STATE *ss)
1196 SCRIPT_DIGITSUBSTITUTE psds;
1198 TRACE("%p, %p, %p\n", sds, sc, ss);
1200 if (!sc || !ss) return E_POINTER;
1201 if (!sds)
1203 sds = &psds;
1204 if (ScriptRecordDigitSubstitution(LOCALE_USER_DEFAULT, &psds) != S_OK)
1205 return E_INVALIDARG;
1208 sc->uDefaultLanguage = LANG_ENGLISH;
1209 sc->fContextDigits = 0;
1210 ss->fDigitSubstitute = 0;
1212 switch (sds->DigitSubstitute) {
1213 case SCRIPT_DIGITSUBSTITUTE_CONTEXT:
1214 case SCRIPT_DIGITSUBSTITUTE_NATIONAL:
1215 case SCRIPT_DIGITSUBSTITUTE_NONE:
1216 case SCRIPT_DIGITSUBSTITUTE_TRADITIONAL:
1217 return S_OK;
1218 default:
1219 return E_INVALIDARG;
1223 static inline BOOL is_indic(WORD script)
1225 return (script >= Script_Devanagari && script <= Script_Malayalam_Numeric);
1228 static inline WORD base_indic(WORD script)
1230 switch (script)
1232 case Script_Devanagari:
1233 case Script_Devanagari_Numeric: return Script_Devanagari;
1234 case Script_Bengali:
1235 case Script_Bengali_Numeric:
1236 case Script_Bengali_Currency: return Script_Bengali;
1237 case Script_Gurmukhi:
1238 case Script_Gurmukhi_Numeric: return Script_Gurmukhi;
1239 case Script_Gujarati:
1240 case Script_Gujarati_Numeric:
1241 case Script_Gujarati_Currency: return Script_Gujarati;
1242 case Script_Oriya:
1243 case Script_Oriya_Numeric: return Script_Oriya;
1244 case Script_Tamil:
1245 case Script_Tamil_Numeric: return Script_Tamil;
1246 case Script_Telugu:
1247 case Script_Telugu_Numeric: return Script_Telugu;
1248 case Script_Kannada:
1249 case Script_Kannada_Numeric: return Script_Kannada;
1250 case Script_Malayalam:
1251 case Script_Malayalam_Numeric: return Script_Malayalam;
1252 default:
1253 return -1;
1257 static BOOL script_is_numeric(WORD script)
1259 return scriptInformation[script].props.fNumeric;
1262 static HRESULT _ItemizeInternal(const WCHAR *pwcInChars, int cInChars,
1263 int cMaxItems, const SCRIPT_CONTROL *psControl,
1264 const SCRIPT_STATE *psState, SCRIPT_ITEM *pItems,
1265 OPENTYPE_TAG *pScriptTags, int *pcItems)
1268 #define Numeric_space 0x0020
1269 #define ZWNJ 0x200C
1270 #define ZWJ 0x200D
1272 int cnt = 0, index = 0, str = 0;
1273 int New_Script = -1;
1274 int i;
1275 WORD *levels = NULL;
1276 WORD *layout_levels = NULL;
1277 WORD *overrides = NULL;
1278 WORD *strength = NULL;
1279 WORD *scripts = NULL;
1280 WORD baselevel = 0;
1281 WORD baselayout = 0;
1282 BOOL new_run;
1283 WORD last_indic = -1;
1284 WORD layoutRTL = 0;
1285 BOOL forceLevels = FALSE;
1286 INT consumed = 0;
1287 HRESULT res = E_OUTOFMEMORY;
1289 TRACE("%s,%d,%d,%p,%p,%p,%p\n", debugstr_wn(pwcInChars, cInChars), cInChars, cMaxItems,
1290 psControl, psState, pItems, pcItems);
1292 if (!pwcInChars || !cInChars || !pItems || cMaxItems < 2)
1293 return E_INVALIDARG;
1295 scripts = heap_alloc(cInChars * sizeof(WORD));
1296 if (!scripts)
1297 return E_OUTOFMEMORY;
1299 for (i = 0; i < cInChars; i++)
1301 if (consumed <= 0)
1303 scripts[i] = get_char_script(pwcInChars,i,cInChars,&consumed);
1304 consumed --;
1306 else
1308 scripts[i] = scripts[i-1];
1309 consumed --;
1311 /* Devanagari danda (U+0964) and double danda (U+0965) are used for
1312 all Indic scripts */
1313 if ((pwcInChars[i] == 0x964 || pwcInChars[i] ==0x965) && last_indic > 0)
1314 scripts[i] = last_indic;
1315 else if (is_indic(scripts[i]))
1316 last_indic = base_indic(scripts[i]);
1318 /* Some unicode points :
1319 (Zero Width Space U+200B - Right-to-Left Mark U+200F)
1320 (Left Right Embed U+202A - Left Right Override U+202D)
1321 (Left Right Isolate U+2066 - Pop Directional Isolate U+2069)
1322 will force us into bidi mode */
1323 if (!forceLevels && ((pwcInChars[i] >= 0x200B && pwcInChars[i] <= 0x200F) ||
1324 (pwcInChars[i] >= 0x202A && pwcInChars[i] <= 0x202E) ||
1325 (pwcInChars[i] >= 0x2066 && pwcInChars[i] <= 0x2069)))
1327 forceLevels = TRUE;
1329 /* Diacritical marks merge with other scripts */
1330 if (scripts[i] == Script_Diacritical)
1332 if (i > 0)
1334 if (pScriptTags)
1335 scripts[i] = scripts[i-1];
1336 else
1338 int j;
1339 BOOL asian = FALSE;
1340 WORD first_script = scripts[i-1];
1341 for (j = i-1; j >= 0 && scripts[j] == first_script && pwcInChars[j] != Numeric_space; j--)
1343 WORD original = scripts[j];
1344 if (original == Script_Ideograph || original == Script_Kana || original == Script_Yi || original == Script_CJK_Han || original == Script_Bopomofo)
1346 asian = TRUE;
1347 break;
1349 if (original != Script_MathAlpha && scriptInformation[scripts[j]].props.fComplex)
1350 break;
1351 scripts[j] = scripts[i];
1352 if (original == Script_Punctuation2)
1353 break;
1355 if (j >= 0 && (scriptInformation[scripts[j]].props.fComplex || asian))
1356 scripts[i] = scripts[j];
1362 for (i = 0; i < cInChars; i++)
1364 /* Joiners get merged preferencially right */
1365 if (i > 0 && (pwcInChars[i] == ZWJ || pwcInChars[i] == ZWNJ))
1367 int j;
1368 if (i+1 == cInChars)
1369 scripts[i] = scripts[i-1];
1370 else
1372 for (j = i+1; j < cInChars; j++)
1374 if (pwcInChars[j] != ZWJ && pwcInChars[j] != ZWNJ && pwcInChars[j] != Numeric_space)
1376 scripts[i] = scripts[j];
1377 break;
1384 if (psState && psControl)
1386 levels = heap_alloc_zero(cInChars * sizeof(WORD));
1387 if (!levels)
1388 goto nomemory;
1390 overrides = heap_alloc_zero(cInChars * sizeof(WORD));
1391 if (!overrides)
1392 goto nomemory;
1394 layout_levels = heap_alloc_zero(cInChars * sizeof(WORD));
1395 if (!layout_levels)
1396 goto nomemory;
1398 if (psState->fOverrideDirection)
1400 if (!forceLevels)
1402 SCRIPT_STATE s = *psState;
1403 s.fOverrideDirection = FALSE;
1404 BIDI_DetermineLevels(pwcInChars, cInChars, &s, psControl, layout_levels, overrides);
1405 if (odd(layout_levels[0]))
1406 forceLevels = TRUE;
1407 else for (i = 0; i < cInChars; i++)
1408 if (layout_levels[i]!=layout_levels[0])
1410 forceLevels = TRUE;
1411 break;
1415 BIDI_DetermineLevels(pwcInChars, cInChars, psState, psControl, levels, overrides);
1417 else
1419 BIDI_DetermineLevels(pwcInChars, cInChars, psState, psControl, levels, overrides);
1420 memcpy(layout_levels, levels, cInChars * sizeof(WORD));
1422 baselevel = levels[0];
1423 baselayout = layout_levels[0];
1424 for (i = 0; i < cInChars; i++)
1425 if (levels[i]!=levels[0])
1426 break;
1427 if (i >= cInChars && !odd(baselevel) && !odd(psState->uBidiLevel) && !forceLevels)
1429 heap_free(levels);
1430 heap_free(overrides);
1431 heap_free(layout_levels);
1432 overrides = NULL;
1433 levels = NULL;
1434 layout_levels = NULL;
1436 else
1438 static const WCHAR math_punc[] = {'#','$','%','+',',','-','.','/',':',0x2212, 0x2044, 0x00a0,0};
1439 static const WCHAR repeatable_math_punc[] = {'#','$','%','+','-','/',0x2212, 0x2044,0};
1441 strength = heap_alloc_zero(cInChars * sizeof(WORD));
1442 if (!strength)
1443 goto nomemory;
1444 BIDI_GetStrengths(pwcInChars, cInChars, psControl, strength);
1446 /* We currently mis-level leading Diacriticals */
1447 if (scripts[0] == Script_Diacritical)
1448 for (i = 0; i < cInChars && scripts[0] == Script_Diacritical; i++)
1450 levels[i] = odd(levels[i])?levels[i]+1:levels[i];
1451 strength[i] = BIDI_STRONG;
1454 /* Math punctuation bordered on both sides by numbers can be
1455 merged into the number */
1456 for (i = 0; i < cInChars; i++)
1458 if (i > 0 && i < cInChars-1 &&
1459 script_is_numeric(scripts[i-1]) &&
1460 strchrW(math_punc, pwcInChars[i]))
1462 if (script_is_numeric(scripts[i+1]))
1464 scripts[i] = scripts[i+1];
1465 levels[i] = levels[i-1];
1466 strength[i] = strength[i-1];
1467 i++;
1469 else if (strchrW(repeatable_math_punc, pwcInChars[i]))
1471 int j;
1472 for (j = i+1; j < cInChars; j++)
1474 if (script_is_numeric(scripts[j]))
1476 for(;i<j; i++)
1478 scripts[i] = scripts[j];
1479 levels[i] = levels[i-1];
1480 strength[i] = strength[i-1];
1483 else if (pwcInChars[i] != pwcInChars[j]) break;
1489 for (i = 0; i < cInChars; i++)
1491 /* Numerics at level 0 get bumped to level 2 */
1492 if (!overrides[i] && (levels[i] == 0 || (odd(psState->uBidiLevel)
1493 && levels[i] == psState->uBidiLevel + 1)) && script_is_numeric(scripts[i]))
1495 levels[i] = 2;
1498 /* Joiners get merged preferencially right */
1499 if (i > 0 && (pwcInChars[i] == ZWJ || pwcInChars[i] == ZWNJ))
1501 int j;
1502 if (i+1 == cInChars && levels[i-1] == levels[i])
1503 strength[i] = strength[i-1];
1504 else
1505 for (j = i+1; j < cInChars && levels[i] == levels[j]; j++)
1506 if (pwcInChars[j] != ZWJ && pwcInChars[j] != ZWNJ && pwcInChars[j] != Numeric_space)
1508 strength[i] = strength[j];
1509 break;
1513 if (psControl->fMergeNeutralItems)
1515 /* Merge the neutrals */
1516 for (i = 0; i < cInChars; i++)
1518 if (strength[i] == BIDI_NEUTRAL || strength[i] == BIDI_WEAK)
1520 int j;
1521 for (j = i; j > 0; j--)
1523 if (levels[i] != levels[j])
1524 break;
1525 if ((strength[j] == BIDI_STRONG) || (strength[i] == BIDI_NEUTRAL && strength[j] == BIDI_WEAK))
1527 scripts[i] = scripts[j];
1528 strength[i] = strength[j];
1529 break;
1533 /* Try going the other way */
1534 if (strength[i] == BIDI_NEUTRAL || strength[i] == BIDI_WEAK)
1536 int j;
1537 for (j = i; j < cInChars; j++)
1539 if (levels[i] != levels[j])
1540 break;
1541 if ((strength[j] == BIDI_STRONG) || (strength[i] == BIDI_NEUTRAL && strength[j] == BIDI_WEAK))
1543 scripts[i] = scripts[j];
1544 strength[i] = strength[j];
1545 break;
1554 while ((!levels || (levels && cnt+1 < cInChars && levels[cnt+1] == levels[0]))
1555 && (cnt < cInChars && pwcInChars[cnt] == Numeric_space))
1556 cnt++;
1558 if (cnt == cInChars) /* All Spaces */
1560 cnt = 0;
1561 New_Script = scripts[cnt];
1564 pItems[index].iCharPos = 0;
1565 pItems[index].a = scriptInformation[scripts[cnt]].a;
1566 if (pScriptTags)
1567 pScriptTags[index] = scriptInformation[scripts[cnt]].scriptTag;
1569 if (strength && strength[cnt] == BIDI_STRONG)
1570 str = strength[cnt];
1571 else if (strength)
1572 str = strength[0];
1574 cnt = 0;
1576 if (levels)
1578 if (strength[cnt] == BIDI_STRONG)
1579 layoutRTL = odd(layout_levels[cnt]);
1580 else
1581 layoutRTL = (psState->uBidiLevel || odd(layout_levels[cnt]));
1582 if (overrides)
1583 pItems[index].a.s.fOverrideDirection = (overrides[cnt] != 0);
1584 pItems[index].a.fRTL = odd(levels[cnt]);
1585 if (script_is_numeric(pItems[index].a.eScript))
1586 pItems[index].a.fLayoutRTL = layoutRTL;
1587 else
1588 pItems[index].a.fLayoutRTL = pItems[index].a.fRTL;
1589 pItems[index].a.s.uBidiLevel = levels[cnt];
1591 else if (!pItems[index].a.s.uBidiLevel || (overrides && overrides[cnt]))
1593 if (pItems[index].a.s.uBidiLevel != baselevel)
1594 pItems[index].a.s.fOverrideDirection = TRUE;
1595 layoutRTL = odd(baselayout);
1596 pItems[index].a.s.uBidiLevel = baselevel;
1597 pItems[index].a.fRTL = odd(baselevel);
1598 if (script_is_numeric(pItems[index].a.eScript))
1599 pItems[index].a.fLayoutRTL = odd(baselayout);
1600 else
1601 pItems[index].a.fLayoutRTL = pItems[index].a.fRTL;
1604 TRACE("New_Level=%i New_Strength=%i New_Script=%d, eScript=%d index=%d cnt=%d iCharPos=%d\n",
1605 levels?levels[cnt]:-1, str, New_Script, pItems[index].a.eScript, index, cnt,
1606 pItems[index].iCharPos);
1608 for (cnt=1; cnt < cInChars; cnt++)
1610 if(pwcInChars[cnt] != Numeric_space)
1611 New_Script = scripts[cnt];
1612 else if (levels)
1614 int j = 1;
1615 while (cnt + j < cInChars - 1 && pwcInChars[cnt+j] == Numeric_space && levels[cnt] == levels[cnt+j])
1616 j++;
1617 if (cnt + j < cInChars && levels[cnt] == levels[cnt+j])
1618 New_Script = scripts[cnt+j];
1619 else
1620 New_Script = scripts[cnt];
1623 new_run = FALSE;
1624 /* merge space strengths*/
1625 if (strength && strength[cnt] == BIDI_STRONG && str != BIDI_STRONG && New_Script == pItems[index].a.eScript)
1626 str = BIDI_STRONG;
1628 if (strength && strength[cnt] == BIDI_NEUTRAL && str == BIDI_STRONG && pwcInChars[cnt] != Numeric_space && New_Script == pItems[index].a.eScript)
1629 str = BIDI_NEUTRAL;
1631 /* changes in level */
1632 if (levels && (levels[cnt] != pItems[index].a.s.uBidiLevel))
1634 TRACE("Level break(%i/%i)\n",pItems[index].a.s.uBidiLevel,levels[cnt]);
1635 new_run = TRUE;
1637 /* changes in strength */
1638 else if (strength && pwcInChars[cnt] != Numeric_space && str != strength[cnt])
1640 TRACE("Strength break (%i/%i)\n",str,strength[cnt]);
1641 new_run = TRUE;
1643 /* changes in script */
1644 else if (((pwcInChars[cnt] != Numeric_space) && (New_Script != -1) && (New_Script != pItems[index].a.eScript)) || (New_Script == Script_Control))
1646 TRACE("Script break(%i/%i)\n",pItems[index].a.eScript,New_Script);
1647 new_run = TRUE;
1650 if (!new_run && strength && str == BIDI_STRONG)
1652 layoutRTL = odd(layout_levels[cnt]);
1653 if (script_is_numeric(pItems[index].a.eScript))
1654 pItems[index].a.fLayoutRTL = layoutRTL;
1657 if (new_run)
1659 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);
1661 index++;
1662 if (index+1 > cMaxItems)
1663 goto nomemory;
1665 if (strength)
1666 str = strength[cnt];
1668 pItems[index].iCharPos = cnt;
1669 memset(&pItems[index].a, 0, sizeof(SCRIPT_ANALYSIS));
1671 pItems[index].a = scriptInformation[New_Script].a;
1672 if (pScriptTags)
1673 pScriptTags[index] = scriptInformation[New_Script].scriptTag;
1674 if (levels)
1676 if (overrides)
1677 pItems[index].a.s.fOverrideDirection = (overrides[cnt] != 0);
1678 if (layout_levels[cnt] == 0)
1679 layoutRTL = 0;
1680 else
1681 layoutRTL = (layoutRTL || odd(layout_levels[cnt]));
1682 pItems[index].a.fRTL = odd(levels[cnt]);
1683 if (script_is_numeric(pItems[index].a.eScript))
1684 pItems[index].a.fLayoutRTL = layoutRTL;
1685 else
1686 pItems[index].a.fLayoutRTL = pItems[index].a.fRTL;
1687 pItems[index].a.s.uBidiLevel = levels[cnt];
1689 else if (!pItems[index].a.s.uBidiLevel || (overrides && overrides[cnt]))
1691 if (pItems[index].a.s.uBidiLevel != baselevel)
1692 pItems[index].a.s.fOverrideDirection = TRUE;
1693 pItems[index].a.s.uBidiLevel = baselevel;
1694 pItems[index].a.fRTL = odd(baselevel);
1695 if (script_is_numeric(pItems[index].a.eScript))
1696 pItems[index].a.fLayoutRTL = layoutRTL;
1697 else
1698 pItems[index].a.fLayoutRTL = pItems[index].a.fRTL;
1701 TRACE("index=%d cnt=%d iCharPos=%d\n", index, cnt, pItems[index].iCharPos);
1705 /* While not strictly necessary according to the spec, make sure the n+1
1706 * item is set up to prevent random behaviour if the caller erroneously
1707 * checks the n+1 structure */
1708 index++;
1709 if (index + 1 > cMaxItems) goto nomemory;
1710 memset(&pItems[index].a, 0, sizeof(SCRIPT_ANALYSIS));
1712 TRACE("index=%d cnt=%d iCharPos=%d\n", index, cnt, pItems[index].iCharPos);
1714 /* Set one SCRIPT_STATE item being returned */
1715 if (pcItems) *pcItems = index;
1717 /* Set SCRIPT_ITEM */
1718 pItems[index].iCharPos = cnt; /* the last item contains the ptr to the lastchar */
1719 res = S_OK;
1720 nomemory:
1721 heap_free(levels);
1722 heap_free(overrides);
1723 heap_free(layout_levels);
1724 heap_free(strength);
1725 heap_free(scripts);
1726 return res;
1729 /***********************************************************************
1730 * ScriptItemizeOpenType (USP10.@)
1732 * Split a Unicode string into shapeable parts.
1734 * PARAMS
1735 * pwcInChars [I] String to split.
1736 * cInChars [I] Number of characters in pwcInChars.
1737 * cMaxItems [I] Maximum number of items to return.
1738 * psControl [I] Pointer to a SCRIPT_CONTROL structure.
1739 * psState [I] Pointer to a SCRIPT_STATE structure.
1740 * pItems [O] Buffer to receive SCRIPT_ITEM structures.
1741 * pScriptTags [O] Buffer to receive OPENTYPE_TAGs.
1742 * pcItems [O] Number of script items returned.
1744 * RETURNS
1745 * Success: S_OK
1746 * Failure: Non-zero HRESULT value.
1748 HRESULT WINAPI ScriptItemizeOpenType(const WCHAR *pwcInChars, int cInChars, int cMaxItems,
1749 const SCRIPT_CONTROL *psControl, const SCRIPT_STATE *psState,
1750 SCRIPT_ITEM *pItems, OPENTYPE_TAG *pScriptTags, int *pcItems)
1752 return _ItemizeInternal(pwcInChars, cInChars, cMaxItems, psControl, psState, pItems, pScriptTags, pcItems);
1755 /***********************************************************************
1756 * ScriptItemize (USP10.@)
1758 * Split a Unicode string into shapeable parts.
1760 * PARAMS
1761 * pwcInChars [I] String to split.
1762 * cInChars [I] Number of characters in pwcInChars.
1763 * cMaxItems [I] Maximum number of items to return.
1764 * psControl [I] Pointer to a SCRIPT_CONTROL structure.
1765 * psState [I] Pointer to a SCRIPT_STATE structure.
1766 * pItems [O] Buffer to receive SCRIPT_ITEM structures.
1767 * pcItems [O] Number of script items returned.
1769 * RETURNS
1770 * Success: S_OK
1771 * Failure: Non-zero HRESULT value.
1773 HRESULT WINAPI ScriptItemize(const WCHAR *pwcInChars, int cInChars, int cMaxItems,
1774 const SCRIPT_CONTROL *psControl, const SCRIPT_STATE *psState,
1775 SCRIPT_ITEM *pItems, int *pcItems)
1777 return _ItemizeInternal(pwcInChars, cInChars, cMaxItems, psControl, psState, pItems, NULL, pcItems);
1780 static inline int getGivenTabWidth(ScriptCache *psc, SCRIPT_TABDEF *pTabdef, int charPos, int current_x)
1782 int defWidth;
1783 int cTabStops=0;
1784 INT *lpTabPos = NULL;
1785 INT nTabOrg = 0;
1786 INT x = 0;
1788 if (pTabdef)
1789 lpTabPos = pTabdef->pTabStops;
1791 if (pTabdef && pTabdef->iTabOrigin)
1793 if (pTabdef->iScale)
1794 nTabOrg = (pTabdef->iTabOrigin * pTabdef->iScale)/4;
1795 else
1796 nTabOrg = pTabdef->iTabOrigin * psc->tm.tmAveCharWidth;
1799 if (pTabdef)
1800 cTabStops = pTabdef->cTabStops;
1802 if (cTabStops == 1)
1804 if (pTabdef->iScale)
1805 defWidth = ((pTabdef->pTabStops[0])*pTabdef->iScale) / 4;
1806 else
1807 defWidth = (pTabdef->pTabStops[0])*psc->tm.tmAveCharWidth;
1808 cTabStops = 0;
1810 else
1811 defWidth = 8 * psc->tm.tmAveCharWidth;
1813 for (; cTabStops>0 ; lpTabPos++, cTabStops--)
1815 int position = *lpTabPos;
1816 if (position < 0)
1817 position = -1 * position;
1818 if (pTabdef->iScale)
1819 position = (position * pTabdef->iScale) / 4;
1820 else
1821 position = position * psc->tm.tmAveCharWidth;
1823 if( nTabOrg + position > current_x)
1825 if( *lpTabPos >= 0)
1827 /* a left aligned tab */
1828 x = (nTabOrg + *lpTabPos) - current_x;
1829 break;
1831 else
1833 FIXME("Negative tabstop\n");
1834 break;
1838 if ((!cTabStops) && (defWidth > 0))
1839 x =((((current_x - nTabOrg) / defWidth)+1) * defWidth) - current_x;
1840 else if ((!cTabStops) && (defWidth < 0))
1841 FIXME("TODO: Negative defWidth\n");
1843 return x;
1846 /***********************************************************************
1847 * Helper function for ScriptStringAnalyse
1849 static BOOL requires_fallback(HDC hdc, SCRIPT_CACHE *psc, SCRIPT_ANALYSIS *psa,
1850 const WCHAR *pwcInChars, int cChars )
1852 /* FIXME: When to properly fallback is still a bit of a mystery */
1853 WORD *glyphs;
1855 if (psa->fNoGlyphIndex)
1856 return FALSE;
1858 if (init_script_cache(hdc, psc) != S_OK)
1859 return FALSE;
1861 if (SHAPE_CheckFontForRequiredFeatures(hdc, (ScriptCache *)*psc, psa) != S_OK)
1862 return TRUE;
1864 glyphs = heap_alloc(sizeof(WORD) * cChars);
1865 if (!glyphs)
1866 return FALSE;
1867 if (ScriptGetCMap(hdc, psc, pwcInChars, cChars, 0, glyphs) != S_OK)
1869 heap_free(glyphs);
1870 return TRUE;
1872 heap_free(glyphs);
1874 return FALSE;
1877 static void find_fallback_font(DWORD scriptid, LPWSTR FaceName)
1879 HKEY hkey;
1881 if (!RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Uniscribe\\Fallback", &hkey))
1883 static const WCHAR szFmt[] = {'%','x',0};
1884 WCHAR value[10];
1885 DWORD count = LF_FACESIZE * sizeof(WCHAR);
1886 DWORD type;
1888 sprintfW(value, szFmt, scriptInformation[scriptid].scriptTag);
1889 if (RegQueryValueExW(hkey, value, 0, &type, (LPBYTE)FaceName, &count))
1890 lstrcpyW(FaceName,scriptInformation[scriptid].fallbackFont);
1891 RegCloseKey(hkey);
1893 else
1894 lstrcpyW(FaceName,scriptInformation[scriptid].fallbackFont);
1897 /***********************************************************************
1898 * ScriptStringAnalyse (USP10.@)
1901 HRESULT WINAPI ScriptStringAnalyse(HDC hdc, const void *pString, int cString,
1902 int cGlyphs, int iCharset, DWORD dwFlags,
1903 int iReqWidth, SCRIPT_CONTROL *psControl,
1904 SCRIPT_STATE *psState, const int *piDx,
1905 SCRIPT_TABDEF *pTabdef, const BYTE *pbInClass,
1906 SCRIPT_STRING_ANALYSIS *pssa)
1908 HRESULT hr = E_OUTOFMEMORY;
1909 StringAnalysis *analysis = NULL;
1910 SCRIPT_CONTROL sControl;
1911 SCRIPT_STATE sState;
1912 int i, num_items = 255;
1913 BYTE *BidiLevel;
1914 WCHAR *iString = NULL;
1916 TRACE("(%p,%p,%d,%d,%d,0x%x,%d,%p,%p,%p,%p,%p,%p)\n",
1917 hdc, pString, cString, cGlyphs, iCharset, dwFlags, iReqWidth,
1918 psControl, psState, piDx, pTabdef, pbInClass, pssa);
1920 if (iCharset != -1)
1922 FIXME("Only Unicode strings are supported\n");
1923 return E_INVALIDARG;
1925 if (cString < 1 || !pString) return E_INVALIDARG;
1926 if ((dwFlags & SSA_GLYPHS) && !hdc) return E_PENDING;
1928 if (!(analysis = heap_alloc_zero(sizeof(StringAnalysis)))) return E_OUTOFMEMORY;
1929 if (!(analysis->pItem = heap_alloc_zero(num_items * sizeof(SCRIPT_ITEM) + 1))) goto error;
1931 /* FIXME: handle clipping */
1932 analysis->clip_len = cString;
1933 analysis->hdc = hdc;
1934 analysis->dwFlags = dwFlags;
1936 if (psState)
1937 sState = *psState;
1938 else
1939 memset(&sState, 0, sizeof(SCRIPT_STATE));
1941 if (psControl)
1942 sControl = *psControl;
1943 else
1944 memset(&sControl, 0, sizeof(SCRIPT_CONTROL));
1946 if (dwFlags & SSA_PASSWORD)
1948 iString = heap_alloc(sizeof(WCHAR)*cString);
1949 if (!iString)
1951 hr = E_OUTOFMEMORY;
1952 goto error;
1954 for (i = 0; i < cString; i++)
1955 iString[i] = *((const WCHAR *)pString);
1956 pString = iString;
1959 hr = ScriptItemize(pString, cString, num_items, &sControl, &sState, analysis->pItem,
1960 &analysis->numItems);
1962 if (FAILED(hr))
1964 if (hr == E_OUTOFMEMORY)
1965 hr = E_INVALIDARG;
1966 goto error;
1969 /* set back to out of memory for default goto error behaviour */
1970 hr = E_OUTOFMEMORY;
1972 if (dwFlags & SSA_BREAK)
1974 if ((analysis->logattrs = heap_alloc(sizeof(SCRIPT_LOGATTR) * cString)))
1976 for (i = 0; i < analysis->numItems; i++)
1977 ScriptBreak(&((LPWSTR)pString)[analysis->pItem[i].iCharPos], analysis->pItem[i+1].iCharPos - analysis->pItem[i].iCharPos, &analysis->pItem[i].a, &analysis->logattrs[analysis->pItem[i].iCharPos]);
1979 else
1980 goto error;
1983 if (!(analysis->logical2visual = heap_alloc_zero(sizeof(int) * analysis->numItems)))
1984 goto error;
1985 if (!(BidiLevel = heap_alloc_zero(analysis->numItems)))
1986 goto error;
1988 if (dwFlags & SSA_GLYPHS)
1990 int tab_x = 0;
1991 if (!(analysis->glyphs = heap_alloc_zero(sizeof(StringGlyphs) * analysis->numItems)))
1993 heap_free(BidiLevel);
1994 goto error;
1997 for (i = 0; i < analysis->numItems; i++)
1999 SCRIPT_CACHE *sc = (SCRIPT_CACHE*)&analysis->glyphs[i].sc;
2000 int cChar = analysis->pItem[i+1].iCharPos - analysis->pItem[i].iCharPos;
2001 int numGlyphs = 1.5 * cChar + 16;
2002 WORD *glyphs = heap_alloc_zero(sizeof(WORD) * numGlyphs);
2003 WORD *pwLogClust = heap_alloc_zero(sizeof(WORD) * cChar);
2004 int *piAdvance = heap_alloc_zero(sizeof(int) * numGlyphs);
2005 SCRIPT_VISATTR *psva = heap_alloc_zero(sizeof(SCRIPT_VISATTR) * numGlyphs);
2006 GOFFSET *pGoffset = heap_alloc_zero(sizeof(GOFFSET) * numGlyphs);
2007 ABC *abc = heap_alloc_zero(sizeof(ABC));
2008 int numGlyphsReturned;
2009 HFONT originalFont = 0x0;
2011 /* FIXME: non unicode strings */
2012 const WCHAR* pStr = (const WCHAR*)pString;
2013 analysis->glyphs[i].fallbackFont = NULL;
2015 if (!glyphs || !pwLogClust || !piAdvance || !psva || !pGoffset || !abc)
2017 heap_free (BidiLevel);
2018 heap_free (glyphs);
2019 heap_free (pwLogClust);
2020 heap_free (piAdvance);
2021 heap_free (psva);
2022 heap_free (pGoffset);
2023 heap_free (abc);
2024 hr = E_OUTOFMEMORY;
2025 goto error;
2028 if ((dwFlags & SSA_FALLBACK) && requires_fallback(hdc, sc, &analysis->pItem[i].a, &pStr[analysis->pItem[i].iCharPos], cChar))
2030 LOGFONTW lf;
2031 GetObjectW(GetCurrentObject(hdc, OBJ_FONT), sizeof(lf), & lf);
2032 lf.lfCharSet = scriptInformation[analysis->pItem[i].a.eScript].props.bCharSet;
2033 lf.lfFaceName[0] = 0;
2034 find_fallback_font(analysis->pItem[i].a.eScript, lf.lfFaceName);
2035 if (lf.lfFaceName[0])
2037 analysis->glyphs[i].fallbackFont = CreateFontIndirectW(&lf);
2038 if (analysis->glyphs[i].fallbackFont)
2040 ScriptFreeCache(sc);
2041 originalFont = SelectObject(hdc, analysis->glyphs[i].fallbackFont);
2046 /* FIXME: When we properly shape Hangul remove this check */
2047 if ((dwFlags & SSA_LINK) && !analysis->glyphs[i].fallbackFont && analysis->pItem[i].a.eScript == Script_Hangul)
2048 analysis->pItem[i].a.fNoGlyphIndex = TRUE;
2050 if ((dwFlags & SSA_LINK) && !analysis->glyphs[i].fallbackFont && !scriptInformation[analysis->pItem[i].a.eScript].props.fComplex && !analysis->pItem[i].a.fRTL)
2051 analysis->pItem[i].a.fNoGlyphIndex = TRUE;
2053 ScriptShape(hdc, sc, &pStr[analysis->pItem[i].iCharPos], cChar, numGlyphs,
2054 &analysis->pItem[i].a, glyphs, pwLogClust, psva, &numGlyphsReturned);
2055 hr = ScriptPlace(hdc, sc, glyphs, numGlyphsReturned, psva, &analysis->pItem[i].a,
2056 piAdvance, pGoffset, abc);
2057 if (originalFont)
2058 SelectObject(hdc,originalFont);
2060 if (dwFlags & SSA_TAB)
2062 int tabi = 0;
2063 for (tabi = 0; tabi < cChar; tabi++)
2065 if (pStr[analysis->pItem[i].iCharPos+tabi] == 0x0009)
2066 piAdvance[tabi] = getGivenTabWidth(analysis->glyphs[i].sc, pTabdef, analysis->pItem[i].iCharPos+tabi, tab_x);
2067 tab_x+=piAdvance[tabi];
2071 analysis->glyphs[i].numGlyphs = numGlyphsReturned;
2072 analysis->glyphs[i].glyphs = glyphs;
2073 analysis->glyphs[i].pwLogClust = pwLogClust;
2074 analysis->glyphs[i].piAdvance = piAdvance;
2075 analysis->glyphs[i].psva = psva;
2076 analysis->glyphs[i].pGoffset = pGoffset;
2077 analysis->glyphs[i].abc = abc;
2078 analysis->glyphs[i].iMaxPosX= -1;
2080 BidiLevel[i] = analysis->pItem[i].a.s.uBidiLevel;
2083 else
2085 for (i = 0; i < analysis->numItems; i++)
2086 BidiLevel[i] = analysis->pItem[i].a.s.uBidiLevel;
2089 ScriptLayout(analysis->numItems, BidiLevel, NULL, analysis->logical2visual);
2090 heap_free(BidiLevel);
2092 *pssa = analysis;
2093 heap_free(iString);
2094 return S_OK;
2096 error:
2097 heap_free(iString);
2098 heap_free(analysis->glyphs);
2099 heap_free(analysis->logattrs);
2100 heap_free(analysis->pItem);
2101 heap_free(analysis->logical2visual);
2102 heap_free(analysis);
2103 return hr;
2106 static inline BOOL does_glyph_start_cluster(const SCRIPT_VISATTR *pva, const WORD *pwLogClust, int cChars, int glyph, int direction)
2108 if (pva[glyph].fClusterStart)
2109 return TRUE;
2110 if (USP10_FindGlyphInLogClust(pwLogClust, cChars, glyph) >= 0)
2111 return TRUE;
2113 return FALSE;
2117 static HRESULT SS_ItemOut( SCRIPT_STRING_ANALYSIS ssa,
2118 int iX,
2119 int iY,
2120 int iItem,
2121 int cStart,
2122 int cEnd,
2123 UINT uOptions,
2124 const RECT *prc,
2125 BOOL fSelected,
2126 BOOL fDisabled)
2128 StringAnalysis *analysis;
2129 int off_x = 0;
2130 HRESULT hr;
2131 COLORREF BkColor = 0x0;
2132 COLORREF TextColor = 0x0;
2133 INT BkMode = 0;
2134 INT runStart, runEnd;
2135 INT iGlyph, cGlyphs;
2136 HFONT oldFont = 0x0;
2137 RECT crc;
2138 int i;
2140 TRACE("(%p,%d,%d,%d,%d,%d, 0x%1x, %d, %d)\n",
2141 ssa, iX, iY, iItem, cStart, cEnd, uOptions, fSelected, fDisabled);
2143 if (!(analysis = ssa)) return E_INVALIDARG;
2145 if ((cStart >= 0 && analysis->pItem[iItem+1].iCharPos <= cStart) ||
2146 (cEnd >= 0 && analysis->pItem[iItem].iCharPos >= cEnd))
2147 return S_OK;
2149 CopyRect(&crc,prc);
2150 if (fSelected)
2152 BkMode = GetBkMode(analysis->hdc);
2153 SetBkMode( analysis->hdc, OPAQUE);
2154 BkColor = GetBkColor(analysis->hdc);
2155 SetBkColor(analysis->hdc, GetSysColor(COLOR_HIGHLIGHT));
2156 if (!fDisabled)
2158 TextColor = GetTextColor(analysis->hdc);
2159 SetTextColor(analysis->hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
2162 if (analysis->glyphs[iItem].fallbackFont)
2163 oldFont = SelectObject(analysis->hdc, analysis->glyphs[iItem].fallbackFont);
2165 if (cStart >= 0 && analysis->pItem[iItem+1].iCharPos > cStart && analysis->pItem[iItem].iCharPos <= cStart)
2166 runStart = cStart - analysis->pItem[iItem].iCharPos;
2167 else
2168 runStart = 0;
2169 if (cEnd >= 0 && analysis->pItem[iItem+1].iCharPos > cEnd && analysis->pItem[iItem].iCharPos <= cEnd)
2170 runEnd = (cEnd-1) - analysis->pItem[iItem].iCharPos;
2171 else
2172 runEnd = (analysis->pItem[iItem+1].iCharPos - analysis->pItem[iItem].iCharPos) - 1;
2174 if (analysis->pItem[iItem].a.fRTL)
2176 if (cEnd >= 0 && cEnd < analysis->pItem[iItem+1].iCharPos)
2177 ScriptStringCPtoX(ssa, cEnd, FALSE, &off_x);
2178 else
2179 ScriptStringCPtoX(ssa, analysis->pItem[iItem+1].iCharPos-1, TRUE, &off_x);
2180 crc.left = iX + off_x;
2182 else
2184 if (cStart >=0 && runStart)
2185 ScriptStringCPtoX(ssa, cStart, FALSE, &off_x);
2186 else
2187 ScriptStringCPtoX(ssa, analysis->pItem[iItem].iCharPos, FALSE, &off_x);
2188 crc.left = iX + off_x;
2191 if (analysis->pItem[iItem].a.fRTL)
2192 iGlyph = analysis->glyphs[iItem].pwLogClust[runEnd];
2193 else
2194 iGlyph = analysis->glyphs[iItem].pwLogClust[runStart];
2196 if (analysis->pItem[iItem].a.fRTL)
2197 cGlyphs = analysis->glyphs[iItem].pwLogClust[runStart] - iGlyph;
2198 else
2199 cGlyphs = analysis->glyphs[iItem].pwLogClust[runEnd] - iGlyph;
2201 cGlyphs++;
2203 /* adjust for cluster glyphs when starting */
2204 if (analysis->pItem[iItem].a.fRTL)
2205 i = analysis->pItem[iItem+1].iCharPos - 1;
2206 else
2207 i = analysis->pItem[iItem].iCharPos;
2209 for (; i >=analysis->pItem[iItem].iCharPos && i < analysis->pItem[iItem+1].iCharPos; (analysis->pItem[iItem].a.fRTL)?i--:i++)
2211 if (analysis->glyphs[iItem].pwLogClust[i - analysis->pItem[iItem].iCharPos] == iGlyph)
2213 if (analysis->pItem[iItem].a.fRTL)
2214 ScriptStringCPtoX(ssa, i, TRUE, &off_x);
2215 else
2216 ScriptStringCPtoX(ssa, i, FALSE, &off_x);
2217 break;
2221 if (cEnd < 0 || scriptInformation[analysis->pItem[iItem].a.eScript].props.fNeedsCaretInfo)
2223 INT direction;
2224 INT clust_glyph;
2226 clust_glyph = iGlyph + cGlyphs;
2227 if (analysis->pItem[iItem].a.fRTL)
2228 direction = -1;
2229 else
2230 direction = 1;
2232 while(clust_glyph < analysis->glyphs[iItem].numGlyphs &&
2233 !does_glyph_start_cluster(analysis->glyphs[iItem].psva, analysis->glyphs[iItem].pwLogClust, (analysis->pItem[iItem+1].iCharPos - analysis->pItem[iItem].iCharPos), clust_glyph, direction))
2235 cGlyphs++;
2236 clust_glyph++;
2240 hr = ScriptTextOut(analysis->hdc,
2241 (SCRIPT_CACHE *)&analysis->glyphs[iItem].sc, iX + off_x,
2242 iY, uOptions, &crc, &analysis->pItem[iItem].a, NULL, 0,
2243 &analysis->glyphs[iItem].glyphs[iGlyph], cGlyphs,
2244 &analysis->glyphs[iItem].piAdvance[iGlyph], NULL,
2245 &analysis->glyphs[iItem].pGoffset[iGlyph]);
2247 TRACE("ScriptTextOut hr=%08x\n", hr);
2249 if (fSelected)
2251 SetBkColor(analysis->hdc, BkColor);
2252 SetBkMode( analysis->hdc, BkMode);
2253 if (!fDisabled)
2254 SetTextColor(analysis->hdc, TextColor);
2256 if (analysis->glyphs[iItem].fallbackFont)
2257 SelectObject(analysis->hdc, oldFont);
2259 return hr;
2262 /***********************************************************************
2263 * ScriptStringOut (USP10.@)
2265 * This function takes the output of ScriptStringAnalyse and joins the segments
2266 * of glyphs and passes the resulting string to ScriptTextOut. ScriptStringOut
2267 * only processes glyphs.
2269 * Parameters:
2270 * ssa [I] buffer to hold the analysed string components
2271 * iX [I] X axis displacement for output
2272 * iY [I] Y axis displacement for output
2273 * uOptions [I] flags controlling output processing
2274 * prc [I] rectangle coordinates
2275 * iMinSel [I] starting pos for substringing output string
2276 * iMaxSel [I] ending pos for substringing output string
2277 * fDisabled [I] controls text highlighting
2279 * RETURNS
2280 * Success: S_OK
2281 * Failure: is the value returned by ScriptTextOut
2283 HRESULT WINAPI ScriptStringOut(SCRIPT_STRING_ANALYSIS ssa,
2284 int iX,
2285 int iY,
2286 UINT uOptions,
2287 const RECT *prc,
2288 int iMinSel,
2289 int iMaxSel,
2290 BOOL fDisabled)
2292 StringAnalysis *analysis;
2293 int item;
2294 HRESULT hr;
2296 TRACE("(%p,%d,%d,0x%08x,%s,%d,%d,%d)\n",
2297 ssa, iX, iY, uOptions, wine_dbgstr_rect(prc), iMinSel, iMaxSel, fDisabled);
2299 if (!(analysis = ssa)) return E_INVALIDARG;
2300 if (!(analysis->dwFlags & SSA_GLYPHS)) return E_INVALIDARG;
2302 for (item = 0; item < analysis->numItems; item++)
2304 hr = SS_ItemOut( ssa, iX, iY, analysis->logical2visual[item], -1, -1, uOptions, prc, FALSE, fDisabled);
2305 if (FAILED(hr))
2306 return hr;
2309 if (iMinSel < iMaxSel && (iMinSel > 0 || iMaxSel > 0))
2311 if (iMaxSel > 0 && iMinSel < 0)
2312 iMinSel = 0;
2313 for (item = 0; item < analysis->numItems; item++)
2315 hr = SS_ItemOut( ssa, iX, iY, analysis->logical2visual[item], iMinSel, iMaxSel, uOptions, prc, TRUE, fDisabled);
2316 if (FAILED(hr))
2317 return hr;
2321 return S_OK;
2324 /***********************************************************************
2325 * ScriptStringCPtoX (USP10.@)
2328 HRESULT WINAPI ScriptStringCPtoX(SCRIPT_STRING_ANALYSIS ssa, int icp, BOOL fTrailing, int* pX)
2330 int item;
2331 int runningX = 0;
2332 StringAnalysis* analysis = ssa;
2334 TRACE("(%p), %d, %d, (%p)\n", ssa, icp, fTrailing, pX);
2336 if (!ssa || !pX) return S_FALSE;
2337 if (!(analysis->dwFlags & SSA_GLYPHS)) return S_FALSE;
2339 /* icp out of range */
2340 if(icp < 0)
2342 analysis->invalid = TRUE;
2343 return E_INVALIDARG;
2346 for(item=0; item<analysis->numItems; item++)
2348 int CP, i;
2349 int offset;
2351 i = analysis->logical2visual[item];
2352 CP = analysis->pItem[i+1].iCharPos - analysis->pItem[i].iCharPos;
2353 /* initialize max extents for uninitialized runs */
2354 if (analysis->glyphs[i].iMaxPosX == -1)
2356 if (analysis->pItem[i].a.fRTL)
2357 ScriptCPtoX(0, FALSE, CP, analysis->glyphs[i].numGlyphs, analysis->glyphs[i].pwLogClust,
2358 analysis->glyphs[i].psva, analysis->glyphs[i].piAdvance,
2359 &analysis->pItem[i].a, &analysis->glyphs[i].iMaxPosX);
2360 else
2361 ScriptCPtoX(CP, TRUE, CP, analysis->glyphs[i].numGlyphs, analysis->glyphs[i].pwLogClust,
2362 analysis->glyphs[i].psva, analysis->glyphs[i].piAdvance,
2363 &analysis->pItem[i].a, &analysis->glyphs[i].iMaxPosX);
2366 if (icp >= analysis->pItem[i+1].iCharPos || icp < analysis->pItem[i].iCharPos)
2368 runningX += analysis->glyphs[i].iMaxPosX;
2369 continue;
2372 icp -= analysis->pItem[i].iCharPos;
2373 ScriptCPtoX(icp, fTrailing, CP, analysis->glyphs[i].numGlyphs, analysis->glyphs[i].pwLogClust,
2374 analysis->glyphs[i].psva, analysis->glyphs[i].piAdvance,
2375 &analysis->pItem[i].a, &offset);
2376 runningX += offset;
2378 *pX = runningX;
2379 return S_OK;
2382 /* icp out of range */
2383 analysis->invalid = TRUE;
2384 return E_INVALIDARG;
2387 /***********************************************************************
2388 * ScriptStringXtoCP (USP10.@)
2391 HRESULT WINAPI ScriptStringXtoCP(SCRIPT_STRING_ANALYSIS ssa, int iX, int* piCh, int* piTrailing)
2393 StringAnalysis* analysis = ssa;
2394 int item;
2396 TRACE("(%p), %d, (%p), (%p)\n", ssa, iX, piCh, piTrailing);
2398 if (!ssa || !piCh || !piTrailing) return S_FALSE;
2399 if (!(analysis->dwFlags & SSA_GLYPHS)) return S_FALSE;
2401 /* out of range */
2402 if(iX < 0)
2404 if (analysis->pItem[0].a.fRTL)
2406 *piCh = 1;
2407 *piTrailing = FALSE;
2409 else
2411 *piCh = -1;
2412 *piTrailing = TRUE;
2414 return S_OK;
2417 for(item=0; item<analysis->numItems; item++)
2419 int i;
2420 int CP;
2422 for (i = 0; i < analysis->numItems && analysis->logical2visual[i] != item; i++)
2423 /* nothing */;
2425 CP = analysis->pItem[i+1].iCharPos - analysis->pItem[i].iCharPos;
2426 /* initialize max extents for uninitialized runs */
2427 if (analysis->glyphs[i].iMaxPosX == -1)
2429 if (analysis->pItem[i].a.fRTL)
2430 ScriptCPtoX(0, FALSE, CP, analysis->glyphs[i].numGlyphs, analysis->glyphs[i].pwLogClust,
2431 analysis->glyphs[i].psva, analysis->glyphs[i].piAdvance,
2432 &analysis->pItem[i].a, &analysis->glyphs[i].iMaxPosX);
2433 else
2434 ScriptCPtoX(CP, TRUE, CP, analysis->glyphs[i].numGlyphs, analysis->glyphs[i].pwLogClust,
2435 analysis->glyphs[i].psva, analysis->glyphs[i].piAdvance,
2436 &analysis->pItem[i].a, &analysis->glyphs[i].iMaxPosX);
2439 if (iX > analysis->glyphs[i].iMaxPosX)
2441 iX -= analysis->glyphs[i].iMaxPosX;
2442 continue;
2445 ScriptXtoCP(iX, CP, analysis->glyphs[i].numGlyphs, analysis->glyphs[i].pwLogClust,
2446 analysis->glyphs[i].psva, analysis->glyphs[i].piAdvance,
2447 &analysis->pItem[i].a, piCh, piTrailing);
2448 *piCh += analysis->pItem[i].iCharPos;
2450 return S_OK;
2453 /* out of range */
2454 *piCh = analysis->pItem[analysis->numItems].iCharPos;
2455 *piTrailing = FALSE;
2457 return S_OK;
2461 /***********************************************************************
2462 * ScriptStringFree (USP10.@)
2464 * Free a string analysis.
2466 * PARAMS
2467 * pssa [I] string analysis.
2469 * RETURNS
2470 * Success: S_OK
2471 * Failure: Non-zero HRESULT value.
2473 HRESULT WINAPI ScriptStringFree(SCRIPT_STRING_ANALYSIS *pssa)
2475 StringAnalysis* analysis;
2476 BOOL invalid;
2477 int i;
2479 TRACE("(%p)\n", pssa);
2481 if (!pssa || !(analysis = *pssa)) return E_INVALIDARG;
2483 invalid = analysis->invalid;
2485 if (analysis->glyphs)
2487 for (i = 0; i < analysis->numItems; i++)
2489 heap_free(analysis->glyphs[i].glyphs);
2490 heap_free(analysis->glyphs[i].pwLogClust);
2491 heap_free(analysis->glyphs[i].piAdvance);
2492 heap_free(analysis->glyphs[i].psva);
2493 heap_free(analysis->glyphs[i].pGoffset);
2494 heap_free(analysis->glyphs[i].abc);
2495 if (analysis->glyphs[i].fallbackFont)
2496 DeleteObject(analysis->glyphs[i].fallbackFont);
2497 ScriptFreeCache((SCRIPT_CACHE *)&analysis->glyphs[i].sc);
2498 heap_free(analysis->glyphs[i].sc);
2500 heap_free(analysis->glyphs);
2503 heap_free(analysis->pItem);
2504 heap_free(analysis->logattrs);
2505 heap_free(analysis->sz);
2506 heap_free(analysis->logical2visual);
2507 heap_free(analysis);
2509 if (invalid) return E_INVALIDARG;
2510 return S_OK;
2513 static inline int get_cluster_size(const WORD *pwLogClust, int cChars, int item,
2514 int direction, int* iCluster, int *check_out)
2516 int clust_size = 1;
2517 int check;
2518 WORD clust = pwLogClust[item];
2520 for (check = item+direction; check < cChars && check >= 0; check+=direction)
2522 if (pwLogClust[check] == clust)
2524 clust_size ++;
2525 if (iCluster && *iCluster == -1)
2526 *iCluster = item;
2528 else break;
2531 if (check_out)
2532 *check_out = check;
2534 return clust_size;
2537 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)
2539 int advance;
2540 int log_clust_max;
2542 advance = piAdvance[glyph];
2544 if (pwLogClust[0] > pwLogClust[cChars-1])
2545 log_clust_max = pwLogClust[0];
2546 else
2547 log_clust_max = pwLogClust[cChars-1];
2549 if (glyph > log_clust_max)
2550 return advance;
2552 for (glyph+=direction; glyph < cGlyphs && glyph >= 0; glyph +=direction)
2555 if (does_glyph_start_cluster(pva, pwLogClust, cChars, glyph, direction))
2556 break;
2557 if (glyph > log_clust_max)
2558 break;
2559 advance += piAdvance[glyph];
2562 return advance;
2565 /***********************************************************************
2566 * ScriptCPtoX (USP10.@)
2569 HRESULT WINAPI ScriptCPtoX(int iCP,
2570 BOOL fTrailing,
2571 int cChars,
2572 int cGlyphs,
2573 const WORD *pwLogClust,
2574 const SCRIPT_VISATTR *psva,
2575 const int *piAdvance,
2576 const SCRIPT_ANALYSIS *psa,
2577 int *piX)
2579 int item;
2580 float iPosX;
2581 int iSpecial = -1;
2582 int iCluster = -1;
2583 int clust_size = 1;
2584 float special_size = 0.0;
2585 int iMaxPos = 0;
2586 int advance = 0;
2587 BOOL rtl = FALSE;
2589 TRACE("(%d,%d,%d,%d,%p,%p,%p,%p,%p)\n",
2590 iCP, fTrailing, cChars, cGlyphs, pwLogClust, psva, piAdvance,
2591 psa, piX);
2593 if (psa->fRTL && ! psa->fLogicalOrder)
2594 rtl = TRUE;
2596 if (fTrailing)
2597 iCP++;
2599 if (rtl)
2601 int max_clust = pwLogClust[0];
2603 for (item=0; item < cGlyphs; item++)
2604 if (pwLogClust[item] > max_clust)
2606 ERR("We do not handle non reversed clusters properly\n");
2607 break;
2610 iMaxPos = 0;
2611 for (item = max_clust; item >=0; item --)
2612 iMaxPos += piAdvance[item];
2615 iPosX = 0.0;
2616 for (item=0; item < iCP && item < cChars; item++)
2618 if (iSpecial == -1 && (iCluster == -1 || (iCluster != -1 && iCluster+clust_size <= item)))
2620 int check;
2621 int clust = pwLogClust[item];
2623 iCluster = -1;
2624 clust_size = get_cluster_size(pwLogClust, cChars, item, 1, &iCluster,
2625 &check);
2627 advance = get_glyph_cluster_advance(piAdvance, psva, pwLogClust, cGlyphs, cChars, clust, 1);
2629 if (check >= cChars && !iMaxPos)
2631 int glyph;
2632 for (glyph = clust; glyph < cGlyphs; glyph++)
2633 special_size += get_glyph_cluster_advance(piAdvance, psva, pwLogClust, cGlyphs, cChars, glyph, 1);
2634 iSpecial = item;
2635 special_size /= (cChars - item);
2636 iPosX += special_size;
2638 else
2640 if (scriptInformation[psa->eScript].props.fNeedsCaretInfo)
2642 clust_size --;
2643 if (clust_size == 0)
2644 iPosX += advance;
2646 else
2647 iPosX += advance / (float)clust_size;
2650 else if (iSpecial != -1)
2651 iPosX += special_size;
2652 else /* (iCluster != -1) */
2654 int adv = get_glyph_cluster_advance(piAdvance, psva, pwLogClust, cGlyphs, cChars, pwLogClust[iCluster], 1);
2655 if (scriptInformation[psa->eScript].props.fNeedsCaretInfo)
2657 clust_size --;
2658 if (clust_size == 0)
2659 iPosX += adv;
2661 else
2662 iPosX += adv / (float)clust_size;
2666 if (iMaxPos > 0)
2668 iPosX = iMaxPos - iPosX;
2669 if (iPosX < 0)
2670 iPosX = 0;
2673 *piX = iPosX;
2674 TRACE("*piX=%d\n", *piX);
2675 return S_OK;
2678 /* Count the number of characters in a cluster and its starting index*/
2679 static inline BOOL get_cluster_data(const WORD *pwLogClust, int cChars, int cluster_index, int *cluster_size, int *start_index)
2681 int size = 0;
2682 int i;
2684 for (i = 0; i < cChars; i++)
2686 if (pwLogClust[i] == cluster_index)
2688 if (!size && start_index)
2690 *start_index = i;
2691 if (!cluster_size)
2692 return TRUE;
2694 size++;
2696 else if (size) break;
2698 if (cluster_size)
2699 *cluster_size = size;
2701 return (size > 0);
2705 To handle multi-glyph clusters we need to find all the glyphs that are
2706 represented in the cluster. This involves finding the glyph whose
2707 index is the cluster index as well as whose glyph indices are greater than
2708 our cluster index but not part of a new cluster.
2710 Then we sum all those glyphs' advances.
2712 static inline int get_cluster_advance(const int* piAdvance,
2713 const SCRIPT_VISATTR *psva,
2714 const WORD *pwLogClust, int cGlyphs,
2715 int cChars, int cluster, int direction)
2717 int glyph_start;
2718 int glyph_end;
2719 int i, advance;
2721 if (direction > 0)
2722 i = 0;
2723 else
2724 i = (cChars - 1);
2726 for (glyph_start = -1, glyph_end = -1; i < cChars && i >= 0 && (glyph_start < 0 || glyph_end < 0); i+=direction)
2728 if (glyph_start < 0 && pwLogClust[i] != cluster) continue;
2729 if (pwLogClust[i] == cluster && glyph_start < 0) glyph_start = pwLogClust[i];
2730 if (glyph_start >= 0 && glyph_end < 0 && pwLogClust[i] != cluster) glyph_end = pwLogClust[i];
2732 if (glyph_end < 0)
2734 if (direction > 0)
2735 glyph_end = cGlyphs;
2736 else
2738 /* Don't fully understand multi-glyph reversed clusters yet,
2739 * do they occur for real or just in our test? */
2740 FIXME("multi-glyph reversed clusters found\n");
2741 glyph_end = glyph_start + 1;
2745 /* Check for fClusterStart, finding this generally would mean a malformed set of data */
2746 for (i = glyph_start+1; i< glyph_end; i++)
2748 if (psva[i].fClusterStart)
2750 glyph_end = i;
2751 break;
2755 for (advance = 0, i = glyph_start; i < glyph_end; i++)
2756 advance += piAdvance[i];
2758 return advance;
2762 /***********************************************************************
2763 * ScriptXtoCP (USP10.@)
2765 * Basic algorithm :
2766 * Use piAdvance to find the cluster we are looking at.
2767 * Find the character that is the first character of the cluster.
2768 * That is our base piCP.
2769 * If the script snaps to cluster boundaries (Hebrew, Indic, Thai) then we
2770 * are good. Otherwise if the cluster is larger than 1 glyph we need to
2771 * determine how far through the cluster to advance the cursor.
2773 HRESULT WINAPI ScriptXtoCP(int iX,
2774 int cChars,
2775 int cGlyphs,
2776 const WORD *pwLogClust,
2777 const SCRIPT_VISATTR *psva,
2778 const int *piAdvance,
2779 const SCRIPT_ANALYSIS *psa,
2780 int *piCP,
2781 int *piTrailing)
2783 int direction = 1;
2784 int iPosX;
2785 int i;
2786 int glyph_index, cluster_index;
2787 int cluster_size;
2789 TRACE("(%d,%d,%d,%p,%p,%p,%p,%p,%p)\n",
2790 iX, cChars, cGlyphs, pwLogClust, psva, piAdvance,
2791 psa, piCP, piTrailing);
2793 if (psa->fRTL && ! psa->fLogicalOrder)
2794 direction = -1;
2796 /* Handle an iX < 0 */
2797 if (iX < 0)
2799 if (direction < 0)
2801 *piCP = cChars;
2802 *piTrailing = 0;
2804 else
2806 *piCP = -1;
2807 *piTrailing = 1;
2809 return S_OK;
2812 /* Looking for non-reversed clusters in a reversed string */
2813 if (direction < 0)
2815 int max_clust = pwLogClust[0];
2816 for (i=0; i< cChars; i++)
2817 if (pwLogClust[i] > max_clust)
2819 FIXME("We do not handle non reversed clusters properly\n");
2820 break;
2824 /* find the glyph_index based in iX */
2825 if (direction > 0)
2827 for (glyph_index = -1, iPosX = iX; iPosX >=0 && glyph_index < cGlyphs; iPosX -= piAdvance[glyph_index+1], glyph_index++)
2830 else
2832 for (glyph_index = -1, iPosX = iX; iPosX > 0 && glyph_index < cGlyphs; iPosX -= piAdvance[glyph_index+1], glyph_index++)
2836 TRACE("iPosX %i -> glyph_index %i (%i)\n", iPosX, glyph_index, cGlyphs);
2838 *piTrailing = 0;
2839 if (glyph_index >= 0 && glyph_index < cGlyphs)
2841 /* find the cluster */
2842 if (direction > 0 )
2843 for (i = 0, cluster_index = pwLogClust[0]; i < cChars && pwLogClust[i] <= glyph_index; cluster_index=pwLogClust[i++])
2845 else
2846 for (i = 0, cluster_index = pwLogClust[0]; i < cChars && pwLogClust[i] >= glyph_index; cluster_index=pwLogClust[i++])
2849 TRACE("cluster_index %i\n", cluster_index);
2851 if (direction < 0 && iPosX >= 0 && glyph_index != cluster_index)
2853 /* We are off the end of the string */
2854 *piCP = -1;
2855 *piTrailing = 1;
2856 return S_OK;
2859 get_cluster_data(pwLogClust, cChars, cluster_index, &cluster_size, &i);
2861 TRACE("first char index %i\n",i);
2862 if (scriptInformation[psa->eScript].props.fNeedsCaretInfo)
2864 /* Check trailing */
2865 if (glyph_index != cluster_index ||
2866 (direction > 0 && abs(iPosX) <= (piAdvance[glyph_index] / 2)) ||
2867 (direction < 0 && abs(iPosX) >= (piAdvance[glyph_index] / 2)))
2868 *piTrailing = cluster_size;
2870 else
2872 if (cluster_size > 1)
2874 /* Be part way through the glyph cluster based on size and position */
2875 int cluster_advance = get_cluster_advance(piAdvance, psva, pwLogClust, cGlyphs, cChars, cluster_index, direction);
2876 double cluster_part_width = cluster_advance / (float)cluster_size;
2877 double adv;
2878 int part_index;
2880 /* back up to the beginning of the cluster */
2881 for (adv = iPosX, part_index = cluster_index; part_index <= glyph_index; part_index++)
2882 adv += piAdvance[part_index];
2883 if (adv > iX) adv = iX;
2885 TRACE("Multi-char cluster, no snap\n");
2886 TRACE("cluster size %i, pre-cluster iPosX %f\n",cluster_size, adv);
2887 TRACE("advance %i divides into %f per char\n", cluster_advance, cluster_part_width);
2888 if (direction > 0)
2890 for (part_index = 0; adv >= 0; adv-=cluster_part_width, part_index++)
2892 if (part_index) part_index--;
2894 else
2896 for (part_index = 0; adv > 0; adv-=cluster_part_width, part_index++)
2898 if (part_index > cluster_size)
2900 adv += cluster_part_width;
2901 part_index=cluster_size;
2905 TRACE("base_char %i part_index %i, leftover advance %f\n",i, part_index, adv);
2907 if (direction > 0)
2908 i += part_index;
2909 else
2910 i += (cluster_size - part_index);
2912 /* Check trailing */
2913 if ((direction > 0 && fabs(adv) <= (cluster_part_width / 2.0)) ||
2914 (direction < 0 && adv && fabs(adv) >= (cluster_part_width / 2.0)))
2915 *piTrailing = 1;
2917 else
2919 /* Check trailing */
2920 if ((direction > 0 && abs(iPosX) <= (piAdvance[glyph_index] / 2)) ||
2921 (direction < 0 && abs(iPosX) >= (piAdvance[glyph_index] / 2)))
2922 *piTrailing = 1;
2926 else
2928 TRACE("Point falls outside of string\n");
2929 if (glyph_index < 0)
2930 i = cChars-1;
2931 else /* (glyph_index >= cGlyphs) */
2932 i = cChars;
2934 /* If not snaping in the reverse direction (such as Hebrew) Then 0
2935 point flow to the next character */
2936 if (direction < 0)
2938 if (!scriptInformation[psa->eScript].props.fNeedsCaretInfo && abs(iPosX) == piAdvance[glyph_index])
2939 i++;
2940 else
2941 *piTrailing = 1;
2945 *piCP = i;
2947 TRACE("*piCP=%d\n", *piCP);
2948 TRACE("*piTrailing=%d\n", *piTrailing);
2949 return S_OK;
2952 /***********************************************************************
2953 * ScriptBreak (USP10.@)
2955 * Retrieve line break information.
2957 * PARAMS
2958 * chars [I] Array of characters.
2959 * sa [I] Script analysis.
2960 * la [I] Array of logical attribute structures.
2962 * RETURNS
2963 * Success: S_OK
2964 * Failure: S_FALSE
2966 HRESULT WINAPI ScriptBreak(const WCHAR *chars, int count, const SCRIPT_ANALYSIS *sa, SCRIPT_LOGATTR *la)
2968 TRACE("(%s, %d, %p, %p)\n", debugstr_wn(chars, count), count, sa, la);
2970 if (count < 0 || !la) return E_INVALIDARG;
2971 if (count == 0) return E_FAIL;
2973 BREAK_line(chars, count, sa, la);
2975 return S_OK;
2978 /***********************************************************************
2979 * ScriptIsComplex (USP10.@)
2981 * Determine if a string is complex.
2983 * PARAMS
2984 * chars [I] Array of characters to test.
2985 * len [I] Length in characters.
2986 * flag [I] Flag.
2988 * RETURNS
2989 * Success: S_OK
2990 * Failure: S_FALSE
2993 HRESULT WINAPI ScriptIsComplex(const WCHAR *chars, int len, DWORD flag)
2995 int i;
2996 INT consumed = 0;
2998 TRACE("(%s,%d,0x%x)\n", debugstr_wn(chars, len), len, flag);
3000 for (i = 0; i < len; i+=consumed)
3002 int script;
3003 if (i >= len)
3004 break;
3006 if ((flag & SIC_ASCIIDIGIT) && chars[i] >= 0x30 && chars[i] <= 0x39)
3007 return S_OK;
3009 script = get_char_script(chars,i,len, &consumed);
3010 if ((scriptInformation[script].props.fComplex && (flag & SIC_COMPLEX))||
3011 (!scriptInformation[script].props.fComplex && (flag & SIC_NEUTRAL)))
3012 return S_OK;
3014 return S_FALSE;
3017 /***********************************************************************
3018 * ScriptShapeOpenType (USP10.@)
3020 * Produce glyphs and visual attributes for a run.
3022 * PARAMS
3023 * hdc [I] Device context.
3024 * psc [I/O] Opaque pointer to a script cache.
3025 * psa [I/O] Script analysis.
3026 * tagScript [I] The OpenType tag for the Script
3027 * tagLangSys [I] The OpenType tag for the Language
3028 * rcRangeChars[I] Array of Character counts in each range
3029 * rpRangeProperties [I] Array of TEXTRANGE_PROPERTIES structures
3030 * cRanges [I] Count of ranges
3031 * pwcChars [I] Array of characters specifying the run.
3032 * cChars [I] Number of characters in pwcChars.
3033 * cMaxGlyphs [I] Length of pwOutGlyphs.
3034 * pwLogClust [O] Array of logical cluster info.
3035 * pCharProps [O] Array of character property values
3036 * pwOutGlyphs [O] Array of glyphs.
3037 * pOutGlyphProps [O] Array of attributes for the retrieved glyphs
3038 * pcGlyphs [O] Number of glyphs returned.
3040 * RETURNS
3041 * Success: S_OK
3042 * Failure: Non-zero HRESULT value.
3044 HRESULT WINAPI ScriptShapeOpenType( HDC hdc, SCRIPT_CACHE *psc,
3045 SCRIPT_ANALYSIS *psa, OPENTYPE_TAG tagScript,
3046 OPENTYPE_TAG tagLangSys, int *rcRangeChars,
3047 TEXTRANGE_PROPERTIES **rpRangeProperties,
3048 int cRanges, const WCHAR *pwcChars, int cChars,
3049 int cMaxGlyphs, WORD *pwLogClust,
3050 SCRIPT_CHARPROP *pCharProps, WORD *pwOutGlyphs,
3051 SCRIPT_GLYPHPROP *pOutGlyphProps, int *pcGlyphs)
3053 HRESULT hr;
3054 int i;
3055 unsigned int g;
3056 BOOL rtl;
3057 int cluster;
3058 static int once = 0;
3060 TRACE("(%p, %p, %p, %s, %s, %p, %p, %d, %s, %d, %d, %p, %p, %p, %p, %p )\n",
3061 hdc, psc, psa,
3062 debugstr_an((char*)&tagScript,4), debugstr_an((char*)&tagLangSys,4),
3063 rcRangeChars, rpRangeProperties, cRanges, debugstr_wn(pwcChars, cChars),
3064 cChars, cMaxGlyphs, pwLogClust, pCharProps, pwOutGlyphs, pOutGlyphProps, pcGlyphs);
3066 if (psa) TRACE("psa values: %d, %d, %d, %d, %d, %d, %d\n", psa->eScript, psa->fRTL, psa->fLayoutRTL,
3067 psa->fLinkBefore, psa->fLinkAfter, psa->fLogicalOrder, psa->fNoGlyphIndex);
3069 if (!pOutGlyphProps || !pcGlyphs || !pCharProps) return E_INVALIDARG;
3070 if (cChars > cMaxGlyphs) return E_OUTOFMEMORY;
3072 if (cRanges)
3073 if(!once++) FIXME("Ranges not supported yet\n");
3075 rtl = (psa && !psa->fLogicalOrder && psa->fRTL);
3077 *pcGlyphs = cChars;
3078 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
3079 if (!pwLogClust) return E_FAIL;
3081 ((ScriptCache *)*psc)->userScript = tagScript;
3082 ((ScriptCache *)*psc)->userLang = tagLangSys;
3084 /* set fNoGlyphIndex non truetype/opentype fonts */
3085 if (psa && !psa->fNoGlyphIndex && !((ScriptCache *)*psc)->sfnt)
3086 psa->fNoGlyphIndex = TRUE;
3088 /* Initialize a SCRIPT_VISATTR and LogClust for each char in this run */
3089 for (i = 0; i < cChars; i++)
3091 int idx = i;
3092 if (rtl) idx = cChars - 1 - i;
3093 /* FIXME: set to better values */
3094 pOutGlyphProps[i].sva.uJustification = (pwcChars[idx] == ' ') ? SCRIPT_JUSTIFY_BLANK : SCRIPT_JUSTIFY_CHARACTER;
3095 pOutGlyphProps[i].sva.fClusterStart = 1;
3096 pOutGlyphProps[i].sva.fDiacritic = 0;
3097 pOutGlyphProps[i].sva.fZeroWidth = 0;
3098 pOutGlyphProps[i].sva.fReserved = 0;
3099 pOutGlyphProps[i].sva.fShapeReserved = 0;
3101 /* FIXME: have the shaping engine set this */
3102 pCharProps[i].fCanGlyphAlone = 0;
3104 pwLogClust[i] = idx;
3107 if (psa && !psa->fNoGlyphIndex)
3109 WCHAR *rChars;
3110 if ((hr = SHAPE_CheckFontForRequiredFeatures(hdc, (ScriptCache *)*psc, psa)) != S_OK) return hr;
3112 rChars = heap_alloc(sizeof(WCHAR) * cChars);
3113 if (!rChars) return E_OUTOFMEMORY;
3114 for (i = 0, g = 0, cluster = 0; i < cChars; i++)
3116 int idx = i;
3117 DWORD chInput;
3119 if (rtl) idx = cChars - 1 - i;
3120 if (!cluster)
3122 chInput = decode_surrogate_pair(pwcChars, idx, cChars);
3123 if (!chInput)
3125 if (psa->fRTL)
3126 chInput = mirror_char(pwcChars[idx]);
3127 else
3128 chInput = pwcChars[idx];
3129 /* special case for tabs */
3130 if (chInput == 0x0009)
3131 chInput = 0x0020;
3132 rChars[i] = chInput;
3134 else
3136 rChars[i] = pwcChars[idx];
3137 rChars[i+1] = pwcChars[(rtl)?idx-1:idx+1];
3138 cluster = 1;
3140 if (!(pwOutGlyphs[g] = get_cache_glyph(psc, chInput)))
3142 WORD glyph;
3143 if (!hdc)
3145 heap_free(rChars);
3146 return E_PENDING;
3148 if (OpenType_CMAP_GetGlyphIndex(hdc, (ScriptCache *)*psc, chInput, &glyph, 0) == GDI_ERROR)
3150 heap_free(rChars);
3151 return S_FALSE;
3153 pwOutGlyphs[g] = set_cache_glyph(psc, chInput, glyph);
3155 g++;
3157 else
3159 int k;
3160 cluster--;
3161 pwLogClust[idx] = (rtl)?pwLogClust[idx+1]:pwLogClust[idx-1];
3162 for (k = (rtl)?idx-1:idx+1; k >= 0 && k < cChars; (rtl)?k--:k++)
3163 pwLogClust[k]--;
3166 *pcGlyphs = g;
3168 SHAPE_ContextualShaping(hdc, (ScriptCache *)*psc, psa, rChars, cChars, pwOutGlyphs, pcGlyphs, cMaxGlyphs, pwLogClust);
3169 SHAPE_ApplyDefaultOpentypeFeatures(hdc, (ScriptCache *)*psc, psa, pwOutGlyphs, pcGlyphs, cMaxGlyphs, cChars, pwLogClust);
3170 SHAPE_CharGlyphProp(hdc, (ScriptCache *)*psc, psa, pwcChars, cChars, pwOutGlyphs, *pcGlyphs, pwLogClust, pCharProps, pOutGlyphProps);
3171 heap_free(rChars);
3173 else
3175 TRACE("no glyph translation\n");
3176 for (i = 0; i < cChars; i++)
3178 int idx = i;
3179 /* No mirroring done here */
3180 if (rtl) idx = cChars - 1 - i;
3181 pwOutGlyphs[i] = pwcChars[idx];
3183 /* overwrite some basic control glyphs to blank */
3184 if (psa && psa->eScript == Script_Control &&
3185 pwcChars[idx] < ((ScriptCache *)*psc)->tm.tmFirstChar)
3187 if (pwcChars[idx] == 0x0009 || pwcChars[idx] == 0x000A ||
3188 pwcChars[idx] == 0x000D || pwcChars[idx] >= 0x001C)
3189 pwOutGlyphs[i] = ((ScriptCache *)*psc)->sfp.wgBlank;
3194 return S_OK;
3198 /***********************************************************************
3199 * ScriptShape (USP10.@)
3201 * Produce glyphs and visual attributes for a run.
3203 * PARAMS
3204 * hdc [I] Device context.
3205 * psc [I/O] Opaque pointer to a script cache.
3206 * pwcChars [I] Array of characters specifying the run.
3207 * cChars [I] Number of characters in pwcChars.
3208 * cMaxGlyphs [I] Length of pwOutGlyphs.
3209 * psa [I/O] Script analysis.
3210 * pwOutGlyphs [O] Array of glyphs.
3211 * pwLogClust [O] Array of logical cluster info.
3212 * psva [O] Array of visual attributes.
3213 * pcGlyphs [O] Number of glyphs returned.
3215 * RETURNS
3216 * Success: S_OK
3217 * Failure: Non-zero HRESULT value.
3219 HRESULT WINAPI ScriptShape(HDC hdc, SCRIPT_CACHE *psc, const WCHAR *pwcChars,
3220 int cChars, int cMaxGlyphs,
3221 SCRIPT_ANALYSIS *psa, WORD *pwOutGlyphs, WORD *pwLogClust,
3222 SCRIPT_VISATTR *psva, int *pcGlyphs)
3224 HRESULT hr;
3225 int i;
3226 SCRIPT_CHARPROP *charProps;
3227 SCRIPT_GLYPHPROP *glyphProps;
3229 if (!psva || !pcGlyphs) return E_INVALIDARG;
3230 if (cChars > cMaxGlyphs) return E_OUTOFMEMORY;
3232 charProps = heap_alloc_zero(sizeof(SCRIPT_CHARPROP)*cChars);
3233 if (!charProps) return E_OUTOFMEMORY;
3234 glyphProps = heap_alloc_zero(sizeof(SCRIPT_GLYPHPROP)*cMaxGlyphs);
3235 if (!glyphProps)
3237 heap_free(charProps);
3238 return E_OUTOFMEMORY;
3241 hr = ScriptShapeOpenType(hdc, psc, psa, scriptInformation[psa->eScript].scriptTag, 0, NULL, NULL, 0, pwcChars, cChars, cMaxGlyphs, pwLogClust, charProps, pwOutGlyphs, glyphProps, pcGlyphs);
3243 if (SUCCEEDED(hr))
3245 for (i = 0; i < *pcGlyphs; i++)
3246 psva[i] = glyphProps[i].sva;
3249 heap_free(charProps);
3250 heap_free(glyphProps);
3252 return hr;
3255 /***********************************************************************
3256 * ScriptPlaceOpenType (USP10.@)
3258 * Produce advance widths for a run.
3260 * PARAMS
3261 * hdc [I] Device context.
3262 * psc [I/O] Opaque pointer to a script cache.
3263 * psa [I/O] Script analysis.
3264 * tagScript [I] The OpenType tag for the Script
3265 * tagLangSys [I] The OpenType tag for the Language
3266 * rcRangeChars[I] Array of Character counts in each range
3267 * rpRangeProperties [I] Array of TEXTRANGE_PROPERTIES structures
3268 * cRanges [I] Count of ranges
3269 * pwcChars [I] Array of characters specifying the run.
3270 * pwLogClust [I] Array of logical cluster info
3271 * pCharProps [I] Array of character property values
3272 * cChars [I] Number of characters in pwcChars.
3273 * pwGlyphs [I] Array of glyphs.
3274 * pGlyphProps [I] Array of attributes for the retrieved glyphs
3275 * cGlyphs [I] Count of Glyphs
3276 * piAdvance [O] Array of advance widths.
3277 * pGoffset [O] Glyph offsets.
3278 * pABC [O] Combined ABC width.
3280 * RETURNS
3281 * Success: S_OK
3282 * Failure: Non-zero HRESULT value.
3285 HRESULT WINAPI ScriptPlaceOpenType( HDC hdc, SCRIPT_CACHE *psc, SCRIPT_ANALYSIS *psa,
3286 OPENTYPE_TAG tagScript, OPENTYPE_TAG tagLangSys,
3287 int *rcRangeChars, TEXTRANGE_PROPERTIES **rpRangeProperties,
3288 int cRanges, const WCHAR *pwcChars, WORD *pwLogClust,
3289 SCRIPT_CHARPROP *pCharProps, int cChars,
3290 const WORD *pwGlyphs, const SCRIPT_GLYPHPROP *pGlyphProps,
3291 int cGlyphs, int *piAdvance,
3292 GOFFSET *pGoffset, ABC *pABC
3295 HRESULT hr;
3296 int i;
3297 static int once = 0;
3299 TRACE("(%p, %p, %p, %s, %s, %p, %p, %d, %s, %p, %p, %d, %p, %p, %d, %p %p %p)\n",
3300 hdc, psc, psa,
3301 debugstr_an((char*)&tagScript,4), debugstr_an((char*)&tagLangSys,4),
3302 rcRangeChars, rpRangeProperties, cRanges, debugstr_wn(pwcChars, cChars),
3303 pwLogClust, pCharProps, cChars, pwGlyphs, pGlyphProps, cGlyphs, piAdvance,
3304 pGoffset, pABC);
3306 if (!pGlyphProps) return E_INVALIDARG;
3307 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
3308 if (!pGoffset) return E_FAIL;
3310 if (cRanges)
3311 if (!once++) FIXME("Ranges not supported yet\n");
3313 ((ScriptCache *)*psc)->userScript = tagScript;
3314 ((ScriptCache *)*psc)->userLang = tagLangSys;
3316 if (pABC) memset(pABC, 0, sizeof(ABC));
3317 for (i = 0; i < cGlyphs; i++)
3319 ABC abc;
3320 if (pGlyphProps[i].sva.fZeroWidth)
3322 abc.abcA = abc.abcB = abc.abcC = 0;
3324 else if (!get_cache_glyph_widths(psc, pwGlyphs[i], &abc))
3326 if (!hdc) return E_PENDING;
3327 if ((get_cache_pitch_family(psc) & TMPF_TRUETYPE) && !psa->fNoGlyphIndex)
3329 if (!GetCharABCWidthsI(hdc, 0, 1, (WORD *)&pwGlyphs[i], &abc)) return S_FALSE;
3331 else
3333 INT width;
3334 if (!GetCharWidth32W(hdc, pwGlyphs[i], pwGlyphs[i], &width)) return S_FALSE;
3335 abc.abcB = width;
3336 abc.abcA = abc.abcC = 0;
3338 set_cache_glyph_widths(psc, pwGlyphs[i], &abc);
3340 if (pABC)
3342 pABC->abcA += abc.abcA;
3343 pABC->abcB += abc.abcB;
3344 pABC->abcC += abc.abcC;
3346 /* FIXME: set to more reasonable values */
3347 pGoffset[i].du = pGoffset[i].dv = 0;
3348 if (piAdvance) piAdvance[i] = abc.abcA + abc.abcB + abc.abcC;
3351 SHAPE_ApplyOpenTypePositions(hdc, (ScriptCache *)*psc, psa, pwGlyphs, cGlyphs, piAdvance, pGoffset);
3353 if (pABC) TRACE("Total for run: abcA=%d, abcB=%d, abcC=%d\n", pABC->abcA, pABC->abcB, pABC->abcC);
3354 return S_OK;
3357 /***********************************************************************
3358 * ScriptPlace (USP10.@)
3360 * Produce advance widths for a run.
3362 * PARAMS
3363 * hdc [I] Device context.
3364 * psc [I/O] Opaque pointer to a script cache.
3365 * pwGlyphs [I] Array of glyphs.
3366 * cGlyphs [I] Number of glyphs in pwGlyphs.
3367 * psva [I] Array of visual attributes.
3368 * psa [I/O] String analysis.
3369 * piAdvance [O] Array of advance widths.
3370 * pGoffset [O] Glyph offsets.
3371 * pABC [O] Combined ABC width.
3373 * RETURNS
3374 * Success: S_OK
3375 * Failure: Non-zero HRESULT value.
3377 HRESULT WINAPI ScriptPlace(HDC hdc, SCRIPT_CACHE *psc, const WORD *pwGlyphs,
3378 int cGlyphs, const SCRIPT_VISATTR *psva,
3379 SCRIPT_ANALYSIS *psa, int *piAdvance, GOFFSET *pGoffset, ABC *pABC )
3381 HRESULT hr;
3382 SCRIPT_GLYPHPROP *glyphProps;
3383 int i;
3385 TRACE("(%p, %p, %p, %d, %p, %p, %p, %p, %p)\n", hdc, psc, pwGlyphs, cGlyphs, psva, psa,
3386 piAdvance, pGoffset, pABC);
3388 if (!psva) return E_INVALIDARG;
3389 if (!pGoffset) return E_FAIL;
3391 glyphProps = heap_alloc(sizeof(SCRIPT_GLYPHPROP)*cGlyphs);
3392 if (!glyphProps) return E_OUTOFMEMORY;
3394 for (i = 0; i < cGlyphs; i++)
3395 glyphProps[i].sva = psva[i];
3397 hr = ScriptPlaceOpenType(hdc, psc, psa, scriptInformation[psa->eScript].scriptTag, 0, NULL, NULL, 0, NULL, NULL, NULL, 0, pwGlyphs, glyphProps, cGlyphs, piAdvance, pGoffset, pABC);
3399 heap_free(glyphProps);
3401 return hr;
3404 /***********************************************************************
3405 * ScriptGetCMap (USP10.@)
3407 * Retrieve glyph indices.
3409 * PARAMS
3410 * hdc [I] Device context.
3411 * psc [I/O] Opaque pointer to a script cache.
3412 * pwcInChars [I] Array of Unicode characters.
3413 * cChars [I] Number of characters in pwcInChars.
3414 * dwFlags [I] Flags.
3415 * pwOutGlyphs [O] Buffer to receive the array of glyph indices.
3417 * RETURNS
3418 * Success: S_OK
3419 * Failure: Non-zero HRESULT value.
3421 HRESULT WINAPI ScriptGetCMap(HDC hdc, SCRIPT_CACHE *psc, const WCHAR *pwcInChars,
3422 int cChars, DWORD dwFlags, WORD *pwOutGlyphs)
3424 HRESULT hr;
3425 int i;
3427 TRACE("(%p,%p,%s,%d,0x%x,%p)\n", hdc, psc, debugstr_wn(pwcInChars, cChars),
3428 cChars, dwFlags, pwOutGlyphs);
3430 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
3432 hr = S_OK;
3434 if ((get_cache_pitch_family(psc) & TMPF_TRUETYPE))
3436 for (i = 0; i < cChars; i++)
3438 WCHAR inChar;
3439 if (dwFlags == SGCM_RTL)
3440 inChar = mirror_char(pwcInChars[i]);
3441 else
3442 inChar = pwcInChars[i];
3443 if (!(pwOutGlyphs[i] = get_cache_glyph(psc, inChar)))
3445 WORD glyph;
3446 if (!hdc) return E_PENDING;
3447 if (GetGlyphIndicesW(hdc, &inChar, 1, &glyph, GGI_MARK_NONEXISTING_GLYPHS) == GDI_ERROR) return S_FALSE;
3448 if (glyph == 0xffff)
3450 hr = S_FALSE;
3451 glyph = 0x0;
3453 pwOutGlyphs[i] = set_cache_glyph(psc, inChar, glyph);
3457 else
3459 TRACE("no glyph translation\n");
3460 for (i = 0; i < cChars; i++)
3462 WCHAR inChar;
3463 if (dwFlags == SGCM_RTL)
3464 inChar = mirror_char(pwcInChars[i]);
3465 else
3466 inChar = pwcInChars[i];
3467 pwOutGlyphs[i] = inChar;
3470 return hr;
3473 /***********************************************************************
3474 * ScriptTextOut (USP10.@)
3477 HRESULT WINAPI ScriptTextOut(const HDC hdc, SCRIPT_CACHE *psc, int x, int y, UINT fuOptions,
3478 const RECT *lprc, const SCRIPT_ANALYSIS *psa, const WCHAR *pwcReserved,
3479 int iReserved, const WORD *pwGlyphs, int cGlyphs, const int *piAdvance,
3480 const int *piJustify, const GOFFSET *pGoffset)
3482 HRESULT hr = S_OK;
3483 INT i, dir = 1;
3484 INT *lpDx;
3485 WORD *reordered_glyphs = (WORD *)pwGlyphs;
3487 TRACE("(%p, %p, %d, %d, %08x, %s, %p, %p, %d, %p, %d, %p, %p, %p)\n",
3488 hdc, psc, x, y, fuOptions, wine_dbgstr_rect(lprc), psa, pwcReserved, iReserved, pwGlyphs, cGlyphs,
3489 piAdvance, piJustify, pGoffset);
3491 if (!hdc || !psc) return E_INVALIDARG;
3492 if (!piAdvance || !psa || !pwGlyphs) return E_INVALIDARG;
3494 fuOptions &= ETO_CLIPPED + ETO_OPAQUE;
3495 fuOptions |= ETO_IGNORELANGUAGE;
3496 if (!psa->fNoGlyphIndex) /* Have Glyphs? */
3497 fuOptions |= ETO_GLYPH_INDEX; /* Say don't do translation to glyph */
3499 lpDx = heap_alloc(cGlyphs * sizeof(INT) * 2);
3500 if (!lpDx) return E_OUTOFMEMORY;
3501 fuOptions |= ETO_PDY;
3503 if (psa->fRTL && psa->fLogicalOrder)
3505 reordered_glyphs = heap_alloc( cGlyphs * sizeof(WORD) );
3506 if (!reordered_glyphs)
3508 heap_free( lpDx );
3509 return E_OUTOFMEMORY;
3512 for (i = 0; i < cGlyphs; i++)
3513 reordered_glyphs[i] = pwGlyphs[cGlyphs - 1 - i];
3514 dir = -1;
3517 for (i = 0; i < cGlyphs; i++)
3519 int orig_index = (dir > 0) ? i : cGlyphs - 1 - i;
3520 lpDx[i * 2] = piAdvance[orig_index];
3521 lpDx[i * 2 + 1] = 0;
3523 if (pGoffset)
3525 if (i == 0)
3527 x += pGoffset[orig_index].du * dir;
3528 y += pGoffset[orig_index].dv;
3530 else
3532 lpDx[(i - 1) * 2] += pGoffset[orig_index].du * dir;
3533 lpDx[(i - 1) * 2 + 1] += pGoffset[orig_index].dv;
3535 lpDx[i * 2] -= pGoffset[orig_index].du * dir;
3536 lpDx[i * 2 + 1] -= pGoffset[orig_index].dv;
3540 if (!ExtTextOutW(hdc, x, y, fuOptions, lprc, reordered_glyphs, cGlyphs, lpDx))
3541 hr = S_FALSE;
3543 if (reordered_glyphs != pwGlyphs) heap_free( reordered_glyphs );
3544 heap_free(lpDx);
3546 return hr;
3549 /***********************************************************************
3550 * ScriptCacheGetHeight (USP10.@)
3552 * Retrieve the height of the font in the cache.
3554 * PARAMS
3555 * hdc [I] Device context.
3556 * psc [I/O] Opaque pointer to a script cache.
3557 * height [O] Receives font height.
3559 * RETURNS
3560 * Success: S_OK
3561 * Failure: Non-zero HRESULT value.
3563 HRESULT WINAPI ScriptCacheGetHeight(HDC hdc, SCRIPT_CACHE *psc, LONG *height)
3565 HRESULT hr;
3567 TRACE("(%p, %p, %p)\n", hdc, psc, height);
3569 if (!height) return E_INVALIDARG;
3570 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
3572 *height = get_cache_height(psc);
3573 return S_OK;
3576 /***********************************************************************
3577 * ScriptGetGlyphABCWidth (USP10.@)
3579 * Retrieve the width of a glyph.
3581 * PARAMS
3582 * hdc [I] Device context.
3583 * psc [I/O] Opaque pointer to a script cache.
3584 * glyph [I] Glyph to retrieve the width for.
3585 * abc [O] ABC widths of the glyph.
3587 * RETURNS
3588 * Success: S_OK
3589 * Failure: Non-zero HRESULT value.
3591 HRESULT WINAPI ScriptGetGlyphABCWidth(HDC hdc, SCRIPT_CACHE *psc, WORD glyph, ABC *abc)
3593 HRESULT hr;
3595 TRACE("(%p, %p, 0x%04x, %p)\n", hdc, psc, glyph, abc);
3597 if (!abc) return E_INVALIDARG;
3598 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
3600 if (!get_cache_glyph_widths(psc, glyph, abc))
3602 if (!hdc) return E_PENDING;
3603 if ((get_cache_pitch_family(psc) & TMPF_TRUETYPE))
3605 if (!GetCharABCWidthsI(hdc, 0, 1, &glyph, abc)) return S_FALSE;
3607 else
3609 INT width;
3610 if (!GetCharWidth32W(hdc, glyph, glyph, &width)) return S_FALSE;
3611 abc->abcB = width;
3612 abc->abcA = abc->abcC = 0;
3614 set_cache_glyph_widths(psc, glyph, abc);
3616 return S_OK;
3619 /***********************************************************************
3620 * ScriptLayout (USP10.@)
3622 * Map embedding levels to visual and/or logical order.
3624 * PARAMS
3625 * runs [I] Size of level array.
3626 * level [I] Array of embedding levels.
3627 * vistolog [O] Map of embedding levels from visual to logical order.
3628 * logtovis [O] Map of embedding levels from logical to visual order.
3630 * RETURNS
3631 * Success: S_OK
3632 * Failure: Non-zero HRESULT value.
3635 HRESULT WINAPI ScriptLayout(int runs, const BYTE *level, int *vistolog, int *logtovis)
3637 int* indexs;
3638 int ich;
3640 TRACE("(%d, %p, %p, %p)\n", runs, level, vistolog, logtovis);
3642 if (!level || (!vistolog && !logtovis))
3643 return E_INVALIDARG;
3645 indexs = heap_alloc(sizeof(int) * runs);
3646 if (!indexs)
3647 return E_OUTOFMEMORY;
3649 if (vistolog)
3651 for( ich = 0; ich < runs; ich++)
3652 indexs[ich] = ich;
3654 ich = 0;
3655 while (ich < runs)
3656 ich += BIDI_ReorderV2lLevel(0, indexs+ich, level+ich, runs - ich, FALSE);
3657 memcpy(vistolog, indexs, runs * sizeof(*vistolog));
3660 if (logtovis)
3662 for( ich = 0; ich < runs; ich++)
3663 indexs[ich] = ich;
3665 ich = 0;
3666 while (ich < runs)
3667 ich += BIDI_ReorderL2vLevel(0, indexs+ich, level+ich, runs - ich, FALSE);
3668 memcpy(logtovis, indexs, runs * sizeof(*logtovis));
3670 heap_free(indexs);
3672 return S_OK;
3675 /***********************************************************************
3676 * ScriptStringGetLogicalWidths (USP10.@)
3678 * Returns logical widths from a string analysis.
3680 * PARAMS
3681 * ssa [I] string analysis.
3682 * piDx [O] logical widths returned.
3684 * RETURNS
3685 * Success: S_OK
3686 * Failure: a non-zero HRESULT.
3688 HRESULT WINAPI ScriptStringGetLogicalWidths(SCRIPT_STRING_ANALYSIS ssa, int *piDx)
3690 int i, j, next = 0;
3691 StringAnalysis *analysis = ssa;
3693 TRACE("%p, %p\n", ssa, piDx);
3695 if (!analysis) return S_FALSE;
3696 if (!(analysis->dwFlags & SSA_GLYPHS)) return S_FALSE;
3698 for (i = 0; i < analysis->numItems; i++)
3700 int cChar = analysis->pItem[i+1].iCharPos - analysis->pItem[i].iCharPos;
3701 int direction = 1;
3703 if (analysis->pItem[i].a.fRTL && ! analysis->pItem[i].a.fLogicalOrder)
3704 direction = -1;
3706 for (j = 0; j < cChar; j++)
3708 int k;
3709 int glyph = analysis->glyphs[i].pwLogClust[j];
3710 int clust_size = get_cluster_size(analysis->glyphs[i].pwLogClust,
3711 cChar, j, direction, NULL, NULL);
3712 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);
3714 for (k = 0; k < clust_size; k++)
3716 piDx[next] = advance / clust_size;
3717 next++;
3718 if (k) j++;
3722 return S_OK;
3725 /***********************************************************************
3726 * ScriptStringValidate (USP10.@)
3728 * Validate a string analysis.
3730 * PARAMS
3731 * ssa [I] string analysis.
3733 * RETURNS
3734 * Success: S_OK
3735 * Failure: S_FALSE if invalid sequences are found
3736 * or a non-zero HRESULT if it fails.
3738 HRESULT WINAPI ScriptStringValidate(SCRIPT_STRING_ANALYSIS ssa)
3740 StringAnalysis *analysis = ssa;
3742 TRACE("(%p)\n", ssa);
3744 if (!analysis) return E_INVALIDARG;
3745 return (analysis->invalid) ? S_FALSE : S_OK;
3748 /***********************************************************************
3749 * ScriptString_pSize (USP10.@)
3751 * Retrieve width and height of an analysed string.
3753 * PARAMS
3754 * ssa [I] string analysis.
3756 * RETURNS
3757 * Success: Pointer to a SIZE structure.
3758 * Failure: NULL
3760 const SIZE * WINAPI ScriptString_pSize(SCRIPT_STRING_ANALYSIS ssa)
3762 int i, j;
3763 StringAnalysis *analysis = ssa;
3765 TRACE("(%p)\n", ssa);
3767 if (!analysis) return NULL;
3768 if (!(analysis->dwFlags & SSA_GLYPHS)) return NULL;
3770 if (!analysis->sz)
3772 if (!(analysis->sz = heap_alloc(sizeof(SIZE)))) return NULL;
3773 analysis->sz->cy = analysis->glyphs[0].sc->tm.tmHeight;
3775 analysis->sz->cx = 0;
3776 for (i = 0; i < analysis->numItems; i++)
3778 if (analysis->glyphs[i].sc->tm.tmHeight > analysis->sz->cy)
3779 analysis->sz->cy = analysis->glyphs[i].sc->tm.tmHeight;
3780 for (j = 0; j < analysis->glyphs[i].numGlyphs; j++)
3781 analysis->sz->cx += analysis->glyphs[i].piAdvance[j];
3784 return analysis->sz;
3787 /***********************************************************************
3788 * ScriptString_pLogAttr (USP10.@)
3790 * Retrieve logical attributes of an analysed string.
3792 * PARAMS
3793 * ssa [I] string analysis.
3795 * RETURNS
3796 * Success: Pointer to an array of SCRIPT_LOGATTR structures.
3797 * Failure: NULL
3799 const SCRIPT_LOGATTR * WINAPI ScriptString_pLogAttr(SCRIPT_STRING_ANALYSIS ssa)
3801 StringAnalysis *analysis = ssa;
3803 TRACE("(%p)\n", ssa);
3805 if (!analysis) return NULL;
3806 if (!(analysis->dwFlags & SSA_BREAK)) return NULL;
3807 return analysis->logattrs;
3810 /***********************************************************************
3811 * ScriptString_pcOutChars (USP10.@)
3813 * Retrieve the length of a string after clipping.
3815 * PARAMS
3816 * ssa [I] String analysis.
3818 * RETURNS
3819 * Success: Pointer to the length.
3820 * Failure: NULL
3822 const int * WINAPI ScriptString_pcOutChars(SCRIPT_STRING_ANALYSIS ssa)
3824 StringAnalysis *analysis = ssa;
3826 TRACE("(%p)\n", ssa);
3828 if (!analysis) return NULL;
3829 return &analysis->clip_len;
3832 /***********************************************************************
3833 * ScriptStringGetOrder (USP10.@)
3835 * Retrieve a glyph order map.
3837 * PARAMS
3838 * ssa [I] String analysis.
3839 * order [I/O] Array of glyph positions.
3841 * RETURNS
3842 * Success: S_OK
3843 * Failure: a non-zero HRESULT.
3845 HRESULT WINAPI ScriptStringGetOrder(SCRIPT_STRING_ANALYSIS ssa, UINT *order)
3847 int i, j;
3848 unsigned int k;
3849 StringAnalysis *analysis = ssa;
3851 TRACE("(%p)\n", ssa);
3853 if (!analysis) return S_FALSE;
3854 if (!(analysis->dwFlags & SSA_GLYPHS)) return S_FALSE;
3856 /* FIXME: handle RTL scripts */
3857 for (i = 0, k = 0; i < analysis->numItems; i++)
3858 for (j = 0; j < analysis->glyphs[i].numGlyphs; j++, k++)
3859 order[k] = k;
3861 return S_OK;
3864 /***********************************************************************
3865 * ScriptGetLogicalWidths (USP10.@)
3867 * Convert advance widths to logical widths.
3869 * PARAMS
3870 * sa [I] Script analysis.
3871 * nbchars [I] Number of characters.
3872 * nbglyphs [I] Number of glyphs.
3873 * glyph_width [I] Array of glyph widths.
3874 * log_clust [I] Array of logical clusters.
3875 * sva [I] Visual attributes.
3876 * widths [O] Array of logical widths.
3878 * RETURNS
3879 * Success: S_OK
3880 * Failure: a non-zero HRESULT.
3882 HRESULT WINAPI ScriptGetLogicalWidths(const SCRIPT_ANALYSIS *sa, int nbchars, int nbglyphs,
3883 const int *glyph_width, const WORD *log_clust,
3884 const SCRIPT_VISATTR *sva, int *widths)
3886 int i;
3888 TRACE("(%p, %d, %d, %p, %p, %p, %p)\n",
3889 sa, nbchars, nbglyphs, glyph_width, log_clust, sva, widths);
3891 /* FIXME */
3892 for (i = 0; i < nbchars; i++) widths[i] = glyph_width[i];
3893 return S_OK;
3896 /***********************************************************************
3897 * ScriptApplyLogicalWidth (USP10.@)
3899 * Generate glyph advance widths.
3901 * PARAMS
3902 * dx [I] Array of logical advance widths.
3903 * num_chars [I] Number of characters.
3904 * num_glyphs [I] Number of glyphs.
3905 * log_clust [I] Array of logical clusters.
3906 * sva [I] Visual attributes.
3907 * advance [I] Array of glyph advance widths.
3908 * sa [I] Script analysis.
3909 * abc [I/O] Summed ABC widths.
3910 * justify [O] Array of glyph advance widths.
3912 * RETURNS
3913 * Success: S_OK
3914 * Failure: a non-zero HRESULT.
3916 HRESULT WINAPI ScriptApplyLogicalWidth(const int *dx, int num_chars, int num_glyphs,
3917 const WORD *log_clust, const SCRIPT_VISATTR *sva,
3918 const int *advance, const SCRIPT_ANALYSIS *sa,
3919 ABC *abc, int *justify)
3921 int i;
3923 FIXME("(%p, %d, %d, %p, %p, %p, %p, %p, %p)\n",
3924 dx, num_chars, num_glyphs, log_clust, sva, advance, sa, abc, justify);
3926 for (i = 0; i < num_chars; i++) justify[i] = advance[i];
3927 return S_OK;
3930 HRESULT WINAPI ScriptJustify(const SCRIPT_VISATTR *sva, const int *advance,
3931 int num_glyphs, int dx, int min_kashida, int *justify)
3933 int i;
3935 FIXME("(%p, %p, %d, %d, %d, %p)\n", sva, advance, num_glyphs, dx, min_kashida, justify);
3937 for (i = 0; i < num_glyphs; i++) justify[i] = advance[i];
3938 return S_OK;
3941 HRESULT WINAPI ScriptGetFontScriptTags( HDC hdc, SCRIPT_CACHE *psc, SCRIPT_ANALYSIS *psa, int cMaxTags, OPENTYPE_TAG *pScriptTags, int *pcTags)
3943 HRESULT hr;
3944 if (!pScriptTags || !pcTags || cMaxTags == 0) return E_INVALIDARG;
3945 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
3947 return SHAPE_GetFontScriptTags(hdc, (ScriptCache *)*psc, psa, cMaxTags, pScriptTags, pcTags);
3950 HRESULT WINAPI ScriptGetFontLanguageTags( HDC hdc, SCRIPT_CACHE *psc, SCRIPT_ANALYSIS *psa, OPENTYPE_TAG tagScript, int cMaxTags, OPENTYPE_TAG *pLangSysTags, int *pcTags)
3952 HRESULT hr;
3953 if (!pLangSysTags || !pcTags || cMaxTags == 0) return E_INVALIDARG;
3954 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
3956 return SHAPE_GetFontLanguageTags(hdc, (ScriptCache *)*psc, psa, tagScript, cMaxTags, pLangSysTags, pcTags);
3959 HRESULT WINAPI ScriptGetFontFeatureTags( HDC hdc, SCRIPT_CACHE *psc, SCRIPT_ANALYSIS *psa, OPENTYPE_TAG tagScript, OPENTYPE_TAG tagLangSys, int cMaxTags, OPENTYPE_TAG *pFeatureTags, int *pcTags)
3961 HRESULT hr;
3962 if (!pFeatureTags || !pcTags || cMaxTags == 0) return E_INVALIDARG;
3963 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
3965 return SHAPE_GetFontFeatureTags(hdc, (ScriptCache *)*psc, psa, tagScript, tagLangSys, cMaxTags, pFeatureTags, pcTags);