usp10: Directional control codes force complex itemization.
[wine.git] / dlls / usp10 / usp10.c
bloba3ca3c26d0dd5d267b43cc48f9e48f7ead1813e2
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 /* the 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, 1, 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, 1, 1, 0, 0, 0, 0, { 1,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('s','y','r','c'),
348 {'E','s','t','r','a','n','g','e','l','o',' ','E','d','e','s','s','a',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: unknow 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[];
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;
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 Symboles 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);
925 if (type == 0)
926 return SCRIPT_UNDEFINED;
928 if (type & C1_CNTRL)
929 return Script_Control;
931 ch = decode_surrogate_pair(str, index, end);
932 if (ch)
933 *consumed = 2;
934 else
935 ch = str[index];
937 i = 0;
940 if (ch < scriptRanges[i].rangeFirst || scriptRanges[i].script == SCRIPT_UNDEFINED)
941 break;
943 if (ch >= scriptRanges[i].rangeFirst && ch <= scriptRanges[i].rangeLast)
945 if (scriptRanges[i].numericScript && type & C1_DIGIT)
946 return scriptRanges[i].numericScript;
947 if (scriptRanges[i].punctScript && type & C1_PUNCT)
948 return scriptRanges[i].punctScript;
949 return scriptRanges[i].script;
951 i++;
952 } while (1);
954 return SCRIPT_UNDEFINED;
957 static int compare_FindGlyph(const void *a, const void* b)
959 const FindGlyph_struct *find = (FindGlyph_struct*)a;
960 const WORD *idx= (WORD*)b;
961 int rc = 0;
963 if ( find->target > *idx)
964 rc = 1;
965 else if (find->target < *idx)
966 rc = -1;
968 if (!find->ascending)
969 rc *= -1;
970 return rc;
973 int USP10_FindGlyphInLogClust(const WORD* pwLogClust, int cChars, WORD target)
975 FindGlyph_struct fgs;
976 WORD *ptr;
977 INT k;
979 if (pwLogClust[0] < pwLogClust[cChars-1])
980 fgs.ascending = TRUE;
981 else
982 fgs.ascending = FALSE;
984 fgs.target = target;
985 ptr = bsearch(&fgs, pwLogClust, cChars, sizeof(WORD), compare_FindGlyph);
987 if (!ptr)
988 return -1;
990 for (k = (ptr - pwLogClust)-1; k >= 0 && pwLogClust[k] == target; k--)
992 k++;
994 return k;
997 /***********************************************************************
998 * ScriptFreeCache (USP10.@)
1000 * Free a script cache.
1002 * PARAMS
1003 * psc [I/O] Script cache.
1005 * RETURNS
1006 * Success: S_OK
1007 * Failure: Non-zero HRESULT value.
1009 HRESULT WINAPI ScriptFreeCache(SCRIPT_CACHE *psc)
1011 TRACE("%p\n", psc);
1013 if (psc && *psc)
1015 unsigned int i;
1016 INT n;
1017 for (i = 0; i < GLYPH_MAX / GLYPH_BLOCK_SIZE; i++)
1019 heap_free(((ScriptCache *)*psc)->widths[i]);
1021 for (i = 0; i < 0x10; i++)
1023 unsigned int j;
1024 if (((ScriptCache *)*psc)->page[i])
1025 for (j = 0; j < GLYPH_MAX / GLYPH_BLOCK_SIZE; j++)
1026 heap_free(((ScriptCache *)*psc)->page[i]->glyphs[j]);
1027 heap_free(((ScriptCache *)*psc)->page[i]);
1029 heap_free(((ScriptCache *)*psc)->GSUB_Table);
1030 heap_free(((ScriptCache *)*psc)->GDEF_Table);
1031 heap_free(((ScriptCache *)*psc)->CMAP_Table);
1032 heap_free(((ScriptCache *)*psc)->GPOS_Table);
1033 for (n = 0; n < ((ScriptCache *)*psc)->script_count; n++)
1035 int j;
1036 for (j = 0; j < ((ScriptCache *)*psc)->scripts[n].language_count; j++)
1038 int k;
1039 for (k = 0; k < ((ScriptCache *)*psc)->scripts[n].languages[j].feature_count; k++)
1040 heap_free(((ScriptCache *)*psc)->scripts[n].languages[j].features[k].lookups);
1041 heap_free(((ScriptCache *)*psc)->scripts[n].languages[j].features);
1043 for (j = 0; j < ((ScriptCache *)*psc)->scripts[n].default_language.feature_count; j++)
1044 heap_free(((ScriptCache *)*psc)->scripts[n].default_language.features[j].lookups);
1045 heap_free(((ScriptCache *)*psc)->scripts[n].default_language.features);
1046 heap_free(((ScriptCache *)*psc)->scripts[n].languages);
1048 heap_free(((ScriptCache *)*psc)->scripts);
1049 heap_free(((ScriptCache *)*psc)->otm);
1050 heap_free(*psc);
1051 *psc = NULL;
1053 return S_OK;
1056 /***********************************************************************
1057 * ScriptGetProperties (USP10.@)
1059 * Retrieve a list of script properties.
1061 * PARAMS
1062 * props [I] Pointer to an array of SCRIPT_PROPERTIES pointers.
1063 * num [I] Pointer to the number of scripts.
1065 * RETURNS
1066 * Success: S_OK
1067 * Failure: Non-zero HRESULT value.
1069 * NOTES
1070 * Behaviour matches WinXP.
1072 HRESULT WINAPI ScriptGetProperties(const SCRIPT_PROPERTIES ***props, int *num)
1074 TRACE("(%p,%p)\n", props, num);
1076 if (!props && !num) return E_INVALIDARG;
1078 if (num) *num = sizeof(script_props)/sizeof(script_props[0]);
1079 if (props) *props = script_props;
1081 return S_OK;
1084 /***********************************************************************
1085 * ScriptGetFontProperties (USP10.@)
1087 * Get information on special glyphs.
1089 * PARAMS
1090 * hdc [I] Device context.
1091 * psc [I/O] Opaque pointer to a script cache.
1092 * sfp [O] Font properties structure.
1094 HRESULT WINAPI ScriptGetFontProperties(HDC hdc, SCRIPT_CACHE *psc, SCRIPT_FONTPROPERTIES *sfp)
1096 HRESULT hr;
1098 TRACE("%p,%p,%p\n", hdc, psc, sfp);
1100 if (!sfp) return E_INVALIDARG;
1101 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
1103 if (sfp->cBytes != sizeof(SCRIPT_FONTPROPERTIES))
1104 return E_INVALIDARG;
1106 get_cache_font_properties(sfp, *psc);
1108 return S_OK;
1111 /***********************************************************************
1112 * ScriptRecordDigitSubstitution (USP10.@)
1114 * Record digit substitution settings for a given locale.
1116 * PARAMS
1117 * locale [I] Locale identifier.
1118 * sds [I] Structure to record substitution settings.
1120 * RETURNS
1121 * Success: S_OK
1122 * Failure: E_POINTER if sds is NULL, E_INVALIDARG otherwise.
1124 * SEE ALSO
1125 * http://blogs.msdn.com/michkap/archive/2006/02/22/536877.aspx
1127 HRESULT WINAPI ScriptRecordDigitSubstitution(LCID locale, SCRIPT_DIGITSUBSTITUTE *sds)
1129 DWORD plgid, sub;
1131 TRACE("0x%x, %p\n", locale, sds);
1133 /* This implementation appears to be correct for all languages, but it's
1134 * not clear if sds->DigitSubstitute is ever set to anything except
1135 * CONTEXT or NONE in reality */
1137 if (!sds) return E_POINTER;
1139 locale = ConvertDefaultLocale(locale);
1141 if (!IsValidLocale(locale, LCID_INSTALLED))
1142 return E_INVALIDARG;
1144 plgid = PRIMARYLANGID(LANGIDFROMLCID(locale));
1145 sds->TraditionalDigitLanguage = plgid;
1147 if (plgid == LANG_ARABIC || plgid == LANG_FARSI)
1148 sds->NationalDigitLanguage = plgid;
1149 else
1150 sds->NationalDigitLanguage = LANG_ENGLISH;
1152 if (!GetLocaleInfoW(locale, LOCALE_IDIGITSUBSTITUTION | LOCALE_RETURN_NUMBER,
1153 (LPWSTR)&sub, sizeof(sub)/sizeof(WCHAR))) return E_INVALIDARG;
1155 switch (sub)
1157 case 0:
1158 if (plgid == LANG_ARABIC || plgid == LANG_FARSI)
1159 sds->DigitSubstitute = SCRIPT_DIGITSUBSTITUTE_CONTEXT;
1160 else
1161 sds->DigitSubstitute = SCRIPT_DIGITSUBSTITUTE_NONE;
1162 break;
1163 case 1:
1164 sds->DigitSubstitute = SCRIPT_DIGITSUBSTITUTE_NONE;
1165 break;
1166 case 2:
1167 sds->DigitSubstitute = SCRIPT_DIGITSUBSTITUTE_NATIONAL;
1168 break;
1169 default:
1170 sds->DigitSubstitute = SCRIPT_DIGITSUBSTITUTE_TRADITIONAL;
1171 break;
1174 sds->dwReserved = 0;
1175 return S_OK;
1178 /***********************************************************************
1179 * ScriptApplyDigitSubstitution (USP10.@)
1181 * Apply digit substitution settings.
1183 * PARAMS
1184 * sds [I] Structure with recorded substitution settings.
1185 * sc [I] Script control structure.
1186 * ss [I] Script state structure.
1188 * RETURNS
1189 * Success: S_OK
1190 * Failure: E_INVALIDARG if sds is invalid. Otherwise an HRESULT.
1192 HRESULT WINAPI ScriptApplyDigitSubstitution(const SCRIPT_DIGITSUBSTITUTE *sds,
1193 SCRIPT_CONTROL *sc, SCRIPT_STATE *ss)
1195 SCRIPT_DIGITSUBSTITUTE psds;
1197 TRACE("%p, %p, %p\n", sds, sc, ss);
1199 if (!sc || !ss) return E_POINTER;
1200 if (!sds)
1202 sds = &psds;
1203 if (ScriptRecordDigitSubstitution(LOCALE_USER_DEFAULT, &psds) != S_OK)
1204 return E_INVALIDARG;
1207 sc->uDefaultLanguage = LANG_ENGLISH;
1208 sc->fContextDigits = 0;
1209 ss->fDigitSubstitute = 0;
1211 switch (sds->DigitSubstitute) {
1212 case SCRIPT_DIGITSUBSTITUTE_CONTEXT:
1213 case SCRIPT_DIGITSUBSTITUTE_NATIONAL:
1214 case SCRIPT_DIGITSUBSTITUTE_NONE:
1215 case SCRIPT_DIGITSUBSTITUTE_TRADITIONAL:
1216 return S_OK;
1217 default:
1218 return E_INVALIDARG;
1222 static inline BOOL is_indic(WORD script)
1224 return (script >= Script_Devanagari && script <= Script_Malayalam_Numeric);
1227 static inline WORD base_indic(WORD script)
1229 switch (script)
1231 case Script_Devanagari:
1232 case Script_Devanagari_Numeric: return Script_Devanagari;
1233 case Script_Bengali:
1234 case Script_Bengali_Numeric:
1235 case Script_Bengali_Currency: return Script_Bengali;
1236 case Script_Gurmukhi:
1237 case Script_Gurmukhi_Numeric: return Script_Gurmukhi;
1238 case Script_Gujarati:
1239 case Script_Gujarati_Numeric:
1240 case Script_Gujarati_Currency: return Script_Gujarati;
1241 case Script_Oriya:
1242 case Script_Oriya_Numeric: return Script_Oriya;
1243 case Script_Tamil:
1244 case Script_Tamil_Numeric: return Script_Tamil;
1245 case Script_Telugu:
1246 case Script_Telugu_Numeric: return Script_Telugu;
1247 case Script_Kannada:
1248 case Script_Kannada_Numeric: return Script_Kannada;
1249 case Script_Malayalam:
1250 case Script_Malayalam_Numeric: return Script_Malayalam;
1251 default:
1252 return -1;
1257 static HRESULT _ItemizeInternal(const WCHAR *pwcInChars, int cInChars,
1258 int cMaxItems, const SCRIPT_CONTROL *psControl,
1259 const SCRIPT_STATE *psState, SCRIPT_ITEM *pItems,
1260 OPENTYPE_TAG *pScriptTags, int *pcItems)
1263 #define Numeric_space 0x0020
1264 #define ZWNJ 0x200C
1265 #define ZWJ 0x200D
1267 int cnt = 0, index = 0, str = 0;
1268 int New_Script = -1;
1269 int i;
1270 WORD *levels = NULL;
1271 WORD *strength = NULL;
1272 WORD *scripts = NULL;
1273 WORD baselevel = 0;
1274 BOOL new_run;
1275 WORD last_indic = -1;
1276 WORD layoutRTL = 0;
1277 BOOL forceLevels = FALSE;
1278 INT consumed = 0;
1279 HRESULT res = E_OUTOFMEMORY;
1281 TRACE("%s,%d,%d,%p,%p,%p,%p\n", debugstr_wn(pwcInChars, cInChars), cInChars, cMaxItems,
1282 psControl, psState, pItems, pcItems);
1284 if (!pwcInChars || !cInChars || !pItems || cMaxItems < 2)
1285 return E_INVALIDARG;
1287 scripts = heap_alloc(cInChars * sizeof(WORD));
1288 if (!scripts)
1289 return E_OUTOFMEMORY;
1291 for (i = 0; i < cInChars; i++)
1293 if (consumed <= 0)
1295 scripts[i] = get_char_script(pwcInChars,i,cInChars,&consumed);
1296 consumed --;
1298 else
1300 scripts[i] = scripts[i-1];
1301 consumed --;
1303 /* Devanagari danda (U+0964) and double danda (U+0965) are used for
1304 all Indic scripts */
1305 if ((pwcInChars[i] == 0x964 || pwcInChars[i] ==0x965) && last_indic > 0)
1306 scripts[i] = last_indic;
1307 else if (is_indic(scripts[i]))
1308 last_indic = base_indic(scripts[i]);
1310 /* Some unicode points :
1311 (Zero Width Space U+200B - Right-to-Left Mark U+200F)
1312 (Left Right Embed U+202A - Left Right Override U+202D)
1313 (Left Right Isolate U+2066 - Pop Directional Isolate U+2069)
1314 will force us into bidi mode */
1315 if (!forceLevels && ((pwcInChars[i] >= 0x200B && pwcInChars[i] <= 0x200F) ||
1316 (pwcInChars[i] >= 0x202A && pwcInChars[i] <= 0x202E) ||
1317 (pwcInChars[i] >= 0x2066 && pwcInChars[i] <= 0x2069)))
1319 forceLevels = TRUE;
1321 /* Diacritical marks merge with other scripts */
1322 if (scripts[i] == Script_Diacritical)
1324 if (i > 0)
1326 if (pScriptTags)
1327 scripts[i] = scripts[i-1];
1328 else
1330 int j;
1331 BOOL asian = FALSE;
1332 WORD first_script = scripts[i-1];
1333 for (j = i-1; j >= 0 && scripts[j] == first_script && pwcInChars[j] != Numeric_space; j--)
1335 WORD original = scripts[j];
1336 if (original == Script_Ideograph || original == Script_Kana || original == Script_Yi || original == Script_CJK_Han || original == Script_Bopomofo)
1338 asian = TRUE;
1339 break;
1341 if (original != Script_MathAlpha && scriptInformation[scripts[j]].props.fComplex)
1342 break;
1343 scripts[j] = scripts[i];
1344 if (original == Script_Punctuation2)
1345 break;
1347 if (j >= 0 && (scriptInformation[scripts[j]].props.fComplex || asian))
1348 scripts[i] = scripts[j];
1354 for (i = 0; i < cInChars; i++)
1356 /* Joiners get merged preferencially right */
1357 if (i > 0 && (pwcInChars[i] == ZWJ || pwcInChars[i] == ZWNJ))
1359 int j;
1360 if (i+1 == cInChars)
1361 scripts[i] = scripts[i-1];
1362 else
1364 for (j = i+1; j < cInChars; j++)
1366 if (pwcInChars[j] != ZWJ && pwcInChars[j] != ZWNJ && pwcInChars[j] != Numeric_space)
1368 scripts[i] = scripts[j];
1369 break;
1376 if (psState && psControl)
1378 levels = heap_alloc_zero(cInChars * sizeof(WORD));
1379 if (!levels)
1380 goto nomemory;
1382 BIDI_DetermineLevels(pwcInChars, cInChars, psState, psControl, levels);
1383 baselevel = levels[0];
1384 for (i = 0; i < cInChars; i++)
1385 if (levels[i]!=levels[0])
1386 break;
1387 if (i >= cInChars && !odd(baselevel) && !odd(psState->uBidiLevel) && !forceLevels)
1389 heap_free(levels);
1390 levels = NULL;
1392 else
1394 BOOL inNumber = FALSE;
1395 static const WCHAR math_punc[] = {'#','$','%','+',',','-','.','/',':',0x2212, 0x2044, 0x00a0,0};
1397 strength = heap_alloc_zero(cInChars * sizeof(WORD));
1398 if (!strength)
1399 goto nomemory;
1400 BIDI_GetStrengths(pwcInChars, cInChars, psControl, strength);
1402 /* We currently mis-level leading Diacriticals */
1403 if (scripts[0] == Script_Diacritical)
1404 for (i = 0; i < cInChars && scripts[0] == Script_Diacritical; i++)
1406 levels[i] = odd(levels[i])?levels[i]+1:levels[i];
1407 strength[i] = BIDI_STRONG;
1410 for (i = 0; i < cInChars; i++)
1412 /* Script_Numeric and select puncuation at level 0 get bumped to level 2 */
1413 if ((levels[i] == 0 || (odd(psState->uBidiLevel) && levels[i] == psState->uBidiLevel+1)) && inNumber && strchrW(math_punc,pwcInChars[i]))
1415 scripts[i] = Script_Numeric;
1416 levels[i] = 2;
1418 else if ((levels[i] == 0 || (odd(psState->uBidiLevel) && levels[i] == psState->uBidiLevel+1)) && scripts[i] == Script_Numeric)
1420 levels[i] = 2;
1421 inNumber = TRUE;
1423 else
1424 inNumber = FALSE;
1426 /* Joiners get merged preferencially right */
1427 if (i > 0 && (pwcInChars[i] == ZWJ || pwcInChars[i] == ZWNJ))
1429 int j;
1430 if (i+1 == cInChars && levels[i-1] == levels[i])
1431 strength[i] = strength[i-1];
1432 else
1433 for (j = i+1; j < cInChars && levels[i] == levels[j]; j++)
1434 if (pwcInChars[j] != ZWJ && pwcInChars[j] != ZWNJ && pwcInChars[j] != Numeric_space)
1436 strength[i] = strength[j];
1437 break;
1441 if (psControl->fMergeNeutralItems)
1443 /* Merge the neutrals */
1444 for (i = 0; i < cInChars; i++)
1446 if (strength[i] == BIDI_NEUTRAL || strength[i] == BIDI_WEAK)
1448 int j;
1449 for (j = i; j > 0; j--)
1451 if (levels[i] != levels[j])
1452 break;
1453 if ((strength[j] == BIDI_STRONG) || (strength[i] == BIDI_NEUTRAL && strength[j] == BIDI_WEAK))
1455 scripts[i] = scripts[j];
1456 strength[i] = strength[j];
1457 break;
1461 /* Try going the other way */
1462 if (strength[i] == BIDI_NEUTRAL || strength[i] == BIDI_WEAK)
1464 int j;
1465 for (j = i; j < cInChars; j++)
1467 if (levels[i] != levels[j])
1468 break;
1469 if ((strength[j] == BIDI_STRONG) || (strength[i] == BIDI_NEUTRAL && strength[j] == BIDI_WEAK))
1471 scripts[i] = scripts[j];
1472 strength[i] = strength[j];
1473 break;
1482 while ((!levels || (levels && cnt+1 < cInChars && levels[cnt+1] == levels[0]))
1483 && (cnt < cInChars && pwcInChars[cnt] == Numeric_space))
1484 cnt++;
1486 if (cnt == cInChars) /* All Spaces */
1488 cnt = 0;
1489 New_Script = scripts[cnt];
1492 pItems[index].iCharPos = 0;
1493 pItems[index].a = scriptInformation[scripts[cnt]].a;
1494 if (pScriptTags)
1495 pScriptTags[index] = scriptInformation[scripts[cnt]].scriptTag;
1497 if (strength && strength[cnt] == BIDI_STRONG)
1498 str = strength[cnt];
1499 else if (strength)
1500 str = strength[0];
1502 cnt = 0;
1504 if (levels)
1506 if (strength[cnt] == BIDI_STRONG)
1507 layoutRTL = (odd(levels[cnt]))?1:0;
1508 else
1509 layoutRTL = (psState->uBidiLevel || odd(levels[cnt]))?1:0;
1510 pItems[index].a.fRTL = odd(levels[cnt]);
1511 pItems[index].a.fLayoutRTL = layoutRTL;
1512 pItems[index].a.s.uBidiLevel = levels[cnt];
1514 else if (!pItems[index].a.s.uBidiLevel)
1516 layoutRTL = (odd(baselevel))?1:0;
1517 pItems[index].a.s.uBidiLevel = baselevel;
1518 pItems[index].a.fLayoutRTL = odd(baselevel);
1519 pItems[index].a.fRTL = odd(baselevel);
1522 TRACE("New_Level=%i New_Strength=%i New_Script=%d, eScript=%d index=%d cnt=%d iCharPos=%d\n",
1523 levels?levels[cnt]:-1, str, New_Script, pItems[index].a.eScript, index, cnt,
1524 pItems[index].iCharPos);
1526 for (cnt=1; cnt < cInChars; cnt++)
1528 if(pwcInChars[cnt] != Numeric_space)
1529 New_Script = scripts[cnt];
1530 else if (levels)
1532 int j = 1;
1533 while (cnt + j < cInChars - 1 && pwcInChars[cnt+j] == Numeric_space && levels[cnt] == levels[cnt+j])
1534 j++;
1535 if (cnt + j < cInChars && levels[cnt] == levels[cnt+j])
1536 New_Script = scripts[cnt+j];
1537 else
1538 New_Script = scripts[cnt];
1541 new_run = FALSE;
1542 /* merge space strengths*/
1543 if (strength && strength[cnt] == BIDI_STRONG && str != BIDI_STRONG && New_Script == pItems[index].a.eScript)
1544 str = BIDI_STRONG;
1546 if (strength && strength[cnt] == BIDI_NEUTRAL && str == BIDI_STRONG && pwcInChars[cnt] != Numeric_space && New_Script == pItems[index].a.eScript)
1547 str = BIDI_NEUTRAL;
1549 /* changes in level */
1550 if (levels && (levels[cnt] != pItems[index].a.s.uBidiLevel))
1552 TRACE("Level break(%i/%i)\n",pItems[index].a.s.uBidiLevel,levels[cnt]);
1553 new_run = TRUE;
1555 /* changes in strength */
1556 else if (strength && pwcInChars[cnt] != Numeric_space && str != strength[cnt])
1558 TRACE("Strength break (%i/%i)\n",str,strength[cnt]);
1559 new_run = TRUE;
1561 /* changes in script */
1562 else if (((pwcInChars[cnt] != Numeric_space) && (New_Script != -1) && (New_Script != pItems[index].a.eScript)) || (New_Script == Script_Control))
1564 TRACE("Script break(%i/%i)\n",pItems[index].a.eScript,New_Script);
1565 new_run = TRUE;
1568 if (!new_run && strength && str == BIDI_STRONG)
1570 layoutRTL = odd(levels[cnt])?1:0;
1571 pItems[index].a.fLayoutRTL = layoutRTL;
1574 if (new_run)
1576 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);
1578 index++;
1579 if (index+1 > cMaxItems)
1580 goto nomemory;
1582 if (strength)
1583 str = strength[cnt];
1585 pItems[index].iCharPos = cnt;
1586 memset(&pItems[index].a, 0, sizeof(SCRIPT_ANALYSIS));
1588 pItems[index].a = scriptInformation[New_Script].a;
1589 if (pScriptTags)
1590 pScriptTags[index] = scriptInformation[New_Script].scriptTag;
1591 if (levels)
1593 if (levels[cnt] == 0)
1594 layoutRTL = 0;
1595 else
1596 layoutRTL = (layoutRTL || odd(levels[cnt]))?1:0;
1597 pItems[index].a.fRTL = odd(levels[cnt]);
1598 pItems[index].a.fLayoutRTL = layoutRTL;
1599 pItems[index].a.s.uBidiLevel = levels[cnt];
1601 else if (!pItems[index].a.s.uBidiLevel)
1603 pItems[index].a.s.uBidiLevel = baselevel;
1604 pItems[index].a.fLayoutRTL = layoutRTL;
1605 pItems[index].a.fRTL = odd(baselevel);
1608 TRACE("index=%d cnt=%d iCharPos=%d\n", index, cnt, pItems[index].iCharPos);
1612 /* While not strictly necessary according to the spec, make sure the n+1
1613 * item is set up to prevent random behaviour if the caller erroneously
1614 * checks the n+1 structure */
1615 index++;
1616 if (index + 1 > cMaxItems) goto nomemory;
1617 memset(&pItems[index].a, 0, sizeof(SCRIPT_ANALYSIS));
1619 TRACE("index=%d cnt=%d iCharPos=%d\n", index, cnt, pItems[index].iCharPos);
1621 /* Set one SCRIPT_STATE item being returned */
1622 if (pcItems) *pcItems = index;
1624 /* Set SCRIPT_ITEM */
1625 pItems[index].iCharPos = cnt; /* the last item contains the ptr to the lastchar */
1626 res = S_OK;
1627 nomemory:
1628 heap_free(levels);
1629 heap_free(strength);
1630 heap_free(scripts);
1631 return res;
1634 /***********************************************************************
1635 * ScriptItemizeOpenType (USP10.@)
1637 * Split a Unicode string into shapeable parts.
1639 * PARAMS
1640 * pwcInChars [I] String to split.
1641 * cInChars [I] Number of characters in pwcInChars.
1642 * cMaxItems [I] Maximum number of items to return.
1643 * psControl [I] Pointer to a SCRIPT_CONTROL structure.
1644 * psState [I] Pointer to a SCRIPT_STATE structure.
1645 * pItems [O] Buffer to receive SCRIPT_ITEM structures.
1646 * pScriptTags [O] Buffer to receive OPENTYPE_TAGs.
1647 * pcItems [O] Number of script items returned.
1649 * RETURNS
1650 * Success: S_OK
1651 * Failure: Non-zero HRESULT value.
1653 HRESULT WINAPI ScriptItemizeOpenType(const WCHAR *pwcInChars, int cInChars, int cMaxItems,
1654 const SCRIPT_CONTROL *psControl, const SCRIPT_STATE *psState,
1655 SCRIPT_ITEM *pItems, OPENTYPE_TAG *pScriptTags, int *pcItems)
1657 return _ItemizeInternal(pwcInChars, cInChars, cMaxItems, psControl, psState, pItems, pScriptTags, pcItems);
1660 /***********************************************************************
1661 * ScriptItemize (USP10.@)
1663 * Split a Unicode string into shapeable parts.
1665 * PARAMS
1666 * pwcInChars [I] String to split.
1667 * cInChars [I] Number of characters in pwcInChars.
1668 * cMaxItems [I] Maximum number of items to return.
1669 * psControl [I] Pointer to a SCRIPT_CONTROL structure.
1670 * psState [I] Pointer to a SCRIPT_STATE structure.
1671 * pItems [O] Buffer to receive SCRIPT_ITEM structures.
1672 * pcItems [O] Number of script items returned.
1674 * RETURNS
1675 * Success: S_OK
1676 * Failure: Non-zero HRESULT value.
1678 HRESULT WINAPI ScriptItemize(const WCHAR *pwcInChars, int cInChars, int cMaxItems,
1679 const SCRIPT_CONTROL *psControl, const SCRIPT_STATE *psState,
1680 SCRIPT_ITEM *pItems, int *pcItems)
1682 return _ItemizeInternal(pwcInChars, cInChars, cMaxItems, psControl, psState, pItems, NULL, pcItems);
1685 static inline int getGivenTabWidth(ScriptCache *psc, SCRIPT_TABDEF *pTabdef, int charPos, int current_x)
1687 int defWidth;
1688 int cTabStops=0;
1689 INT *lpTabPos = NULL;
1690 INT nTabOrg = 0;
1691 INT x = 0;
1693 if (pTabdef)
1694 lpTabPos = pTabdef->pTabStops;
1696 if (pTabdef && pTabdef->iTabOrigin)
1698 if (pTabdef->iScale)
1699 nTabOrg = (pTabdef->iTabOrigin * pTabdef->iScale)/4;
1700 else
1701 nTabOrg = pTabdef->iTabOrigin * psc->tm.tmAveCharWidth;
1704 if (pTabdef)
1705 cTabStops = pTabdef->cTabStops;
1707 if (cTabStops == 1)
1709 if (pTabdef->iScale)
1710 defWidth = ((pTabdef->pTabStops[0])*pTabdef->iScale) / 4;
1711 else
1712 defWidth = (pTabdef->pTabStops[0])*psc->tm.tmAveCharWidth;
1713 cTabStops = 0;
1715 else
1716 defWidth = 8 * psc->tm.tmAveCharWidth;
1718 for (; cTabStops>0 ; lpTabPos++, cTabStops--)
1720 int position = *lpTabPos;
1721 if (position < 0)
1722 position = -1 * position;
1723 if (pTabdef->iScale)
1724 position = (position * pTabdef->iScale) / 4;
1725 else
1726 position = position * psc->tm.tmAveCharWidth;
1728 if( nTabOrg + position > current_x)
1730 if( *lpTabPos >= 0)
1732 /* a left aligned tab */
1733 x = (nTabOrg + *lpTabPos) - current_x;
1734 break;
1736 else
1738 FIXME("Negative tabstop\n");
1739 break;
1743 if ((!cTabStops) && (defWidth > 0))
1744 x =((((current_x - nTabOrg) / defWidth)+1) * defWidth) - current_x;
1745 else if ((!cTabStops) && (defWidth < 0))
1746 FIXME("TODO: Negative defWidth\n");
1748 return x;
1751 /***********************************************************************
1752 * Helper function for ScriptStringAnalyse
1754 static BOOL requires_fallback(HDC hdc, SCRIPT_CACHE *psc, SCRIPT_ANALYSIS *psa,
1755 const WCHAR *pwcInChars, int cChars )
1757 /* FIXME: When to properly fallback is still a bit of a mystery */
1758 WORD *glyphs;
1760 if (psa->fNoGlyphIndex)
1761 return FALSE;
1763 if (init_script_cache(hdc, psc) != S_OK)
1764 return FALSE;
1766 if (SHAPE_CheckFontForRequiredFeatures(hdc, (ScriptCache *)*psc, psa) != S_OK)
1767 return TRUE;
1769 glyphs = heap_alloc(sizeof(WORD) * cChars);
1770 if (!glyphs)
1771 return FALSE;
1772 if (ScriptGetCMap(hdc, psc, pwcInChars, cChars, 0, glyphs) != S_OK)
1774 heap_free(glyphs);
1775 return TRUE;
1777 heap_free(glyphs);
1779 return FALSE;
1782 static void find_fallback_font(DWORD scriptid, LPWSTR FaceName)
1784 HKEY hkey;
1786 if (!RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Uniscribe\\Fallback", &hkey))
1788 static const WCHAR szFmt[] = {'%','x',0};
1789 WCHAR value[10];
1790 DWORD count = LF_FACESIZE * sizeof(WCHAR);
1791 DWORD type;
1793 sprintfW(value, szFmt, scriptInformation[scriptid].scriptTag);
1794 if (RegQueryValueExW(hkey, value, 0, &type, (LPBYTE)FaceName, &count))
1795 lstrcpyW(FaceName,scriptInformation[scriptid].fallbackFont);
1796 RegCloseKey(hkey);
1798 else
1799 lstrcpyW(FaceName,scriptInformation[scriptid].fallbackFont);
1802 /***********************************************************************
1803 * ScriptStringAnalyse (USP10.@)
1806 HRESULT WINAPI ScriptStringAnalyse(HDC hdc, const void *pString, int cString,
1807 int cGlyphs, int iCharset, DWORD dwFlags,
1808 int iReqWidth, SCRIPT_CONTROL *psControl,
1809 SCRIPT_STATE *psState, const int *piDx,
1810 SCRIPT_TABDEF *pTabdef, const BYTE *pbInClass,
1811 SCRIPT_STRING_ANALYSIS *pssa)
1813 HRESULT hr = E_OUTOFMEMORY;
1814 StringAnalysis *analysis = NULL;
1815 SCRIPT_CONTROL sControl;
1816 SCRIPT_STATE sState;
1817 int i, num_items = 255;
1818 BYTE *BidiLevel;
1819 WCHAR *iString = NULL;
1821 TRACE("(%p,%p,%d,%d,%d,0x%x,%d,%p,%p,%p,%p,%p,%p)\n",
1822 hdc, pString, cString, cGlyphs, iCharset, dwFlags, iReqWidth,
1823 psControl, psState, piDx, pTabdef, pbInClass, pssa);
1825 if (iCharset != -1)
1827 FIXME("Only Unicode strings are supported\n");
1828 return E_INVALIDARG;
1830 if (cString < 1 || !pString) return E_INVALIDARG;
1831 if ((dwFlags & SSA_GLYPHS) && !hdc) return E_PENDING;
1833 if (!(analysis = heap_alloc_zero(sizeof(StringAnalysis)))) return E_OUTOFMEMORY;
1834 if (!(analysis->pItem = heap_alloc_zero(num_items * sizeof(SCRIPT_ITEM) + 1))) goto error;
1836 /* FIXME: handle clipping */
1837 analysis->clip_len = cString;
1838 analysis->hdc = hdc;
1839 analysis->dwFlags = dwFlags;
1841 if (psState)
1842 sState = *psState;
1843 else
1844 memset(&sState, 0, sizeof(SCRIPT_STATE));
1846 if (psControl)
1847 sControl = *psControl;
1848 else
1849 memset(&sControl, 0, sizeof(SCRIPT_CONTROL));
1851 if (dwFlags & SSA_PASSWORD)
1853 iString = heap_alloc(sizeof(WCHAR)*cString);
1854 if (!iString)
1856 hr = E_OUTOFMEMORY;
1857 goto error;
1859 for (i = 0; i < cString; i++)
1860 iString[i] = *((const WCHAR *)pString);
1861 pString = iString;
1864 hr = ScriptItemize(pString, cString, num_items, &sControl, &sState, analysis->pItem,
1865 &analysis->numItems);
1867 if (FAILED(hr))
1869 if (hr == E_OUTOFMEMORY)
1870 hr = E_INVALIDARG;
1871 goto error;
1874 /* set back to out of memory for default goto error behaviour */
1875 hr = E_OUTOFMEMORY;
1877 if (dwFlags & SSA_BREAK)
1879 if ((analysis->logattrs = heap_alloc(sizeof(SCRIPT_LOGATTR) * cString)))
1881 for (i = 0; i < analysis->numItems; i++)
1882 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]);
1884 else
1885 goto error;
1888 if (!(analysis->logical2visual = heap_alloc_zero(sizeof(int) * analysis->numItems)))
1889 goto error;
1890 if (!(BidiLevel = heap_alloc_zero(analysis->numItems)))
1891 goto error;
1893 if (dwFlags & SSA_GLYPHS)
1895 int tab_x = 0;
1896 if (!(analysis->glyphs = heap_alloc_zero(sizeof(StringGlyphs) * analysis->numItems)))
1898 heap_free(BidiLevel);
1899 goto error;
1902 for (i = 0; i < analysis->numItems; i++)
1904 SCRIPT_CACHE *sc = (SCRIPT_CACHE*)&analysis->glyphs[i].sc;
1905 int cChar = analysis->pItem[i+1].iCharPos - analysis->pItem[i].iCharPos;
1906 int numGlyphs = 1.5 * cChar + 16;
1907 WORD *glyphs = heap_alloc_zero(sizeof(WORD) * numGlyphs);
1908 WORD *pwLogClust = heap_alloc_zero(sizeof(WORD) * cChar);
1909 int *piAdvance = heap_alloc_zero(sizeof(int) * numGlyphs);
1910 SCRIPT_VISATTR *psva = heap_alloc_zero(sizeof(SCRIPT_VISATTR) * numGlyphs);
1911 GOFFSET *pGoffset = heap_alloc_zero(sizeof(GOFFSET) * numGlyphs);
1912 ABC *abc = heap_alloc_zero(sizeof(ABC));
1913 int numGlyphsReturned;
1914 HFONT originalFont = 0x0;
1916 /* FIXME: non unicode strings */
1917 const WCHAR* pStr = (const WCHAR*)pString;
1918 analysis->glyphs[i].fallbackFont = NULL;
1920 if (!glyphs || !pwLogClust || !piAdvance || !psva || !pGoffset || !abc)
1922 heap_free (BidiLevel);
1923 heap_free (glyphs);
1924 heap_free (pwLogClust);
1925 heap_free (piAdvance);
1926 heap_free (psva);
1927 heap_free (pGoffset);
1928 heap_free (abc);
1929 hr = E_OUTOFMEMORY;
1930 goto error;
1933 if ((dwFlags & SSA_FALLBACK) && requires_fallback(hdc, sc, &analysis->pItem[i].a, &pStr[analysis->pItem[i].iCharPos], cChar))
1935 LOGFONTW lf;
1936 GetObjectW(GetCurrentObject(hdc, OBJ_FONT), sizeof(lf), & lf);
1937 lf.lfCharSet = scriptInformation[analysis->pItem[i].a.eScript].props.bCharSet;
1938 lf.lfFaceName[0] = 0;
1939 find_fallback_font(analysis->pItem[i].a.eScript, lf.lfFaceName);
1940 if (lf.lfFaceName[0])
1942 analysis->glyphs[i].fallbackFont = CreateFontIndirectW(&lf);
1943 if (analysis->glyphs[i].fallbackFont)
1945 ScriptFreeCache(sc);
1946 originalFont = SelectObject(hdc, analysis->glyphs[i].fallbackFont);
1951 /* FIXME: When we properly shape Hangul remove this check */
1952 if ((dwFlags & SSA_LINK) && !analysis->glyphs[i].fallbackFont && analysis->pItem[i].a.eScript == Script_Hangul)
1953 analysis->pItem[i].a.fNoGlyphIndex = TRUE;
1955 if ((dwFlags & SSA_LINK) && !analysis->glyphs[i].fallbackFont && !scriptInformation[analysis->pItem[i].a.eScript].props.fComplex && !analysis->pItem[i].a.fRTL)
1956 analysis->pItem[i].a.fNoGlyphIndex = TRUE;
1958 ScriptShape(hdc, sc, &pStr[analysis->pItem[i].iCharPos], cChar, numGlyphs,
1959 &analysis->pItem[i].a, glyphs, pwLogClust, psva, &numGlyphsReturned);
1960 hr = ScriptPlace(hdc, sc, glyphs, numGlyphsReturned, psva, &analysis->pItem[i].a,
1961 piAdvance, pGoffset, abc);
1962 if (originalFont)
1963 SelectObject(hdc,originalFont);
1965 if (dwFlags & SSA_TAB)
1967 int tabi = 0;
1968 for (tabi = 0; tabi < cChar; tabi++)
1970 if (pStr[analysis->pItem[i].iCharPos+tabi] == 0x0009)
1971 piAdvance[tabi] = getGivenTabWidth(analysis->glyphs[i].sc, pTabdef, analysis->pItem[i].iCharPos+tabi, tab_x);
1972 tab_x+=piAdvance[tabi];
1976 analysis->glyphs[i].numGlyphs = numGlyphsReturned;
1977 analysis->glyphs[i].glyphs = glyphs;
1978 analysis->glyphs[i].pwLogClust = pwLogClust;
1979 analysis->glyphs[i].piAdvance = piAdvance;
1980 analysis->glyphs[i].psva = psva;
1981 analysis->glyphs[i].pGoffset = pGoffset;
1982 analysis->glyphs[i].abc = abc;
1983 analysis->glyphs[i].iMaxPosX= -1;
1985 BidiLevel[i] = analysis->pItem[i].a.s.uBidiLevel;
1988 else
1990 for (i = 0; i < analysis->numItems; i++)
1991 BidiLevel[i] = analysis->pItem[i].a.s.uBidiLevel;
1994 ScriptLayout(analysis->numItems, BidiLevel, NULL, analysis->logical2visual);
1995 heap_free(BidiLevel);
1997 *pssa = analysis;
1998 heap_free(iString);
1999 return S_OK;
2001 error:
2002 heap_free(iString);
2003 heap_free(analysis->glyphs);
2004 heap_free(analysis->logattrs);
2005 heap_free(analysis->pItem);
2006 heap_free(analysis->logical2visual);
2007 heap_free(analysis);
2008 return hr;
2011 static inline BOOL does_glyph_start_cluster(const SCRIPT_VISATTR *pva, const WORD *pwLogClust, int cChars, int glyph, int direction)
2013 if (pva[glyph].fClusterStart)
2014 return TRUE;
2015 if (USP10_FindGlyphInLogClust(pwLogClust, cChars, glyph) >= 0)
2016 return TRUE;
2018 return FALSE;
2022 static HRESULT SS_ItemOut( SCRIPT_STRING_ANALYSIS ssa,
2023 int iX,
2024 int iY,
2025 int iItem,
2026 int cStart,
2027 int cEnd,
2028 UINT uOptions,
2029 const RECT *prc,
2030 BOOL fSelected,
2031 BOOL fDisabled)
2033 StringAnalysis *analysis;
2034 int off_x = 0;
2035 HRESULT hr;
2036 COLORREF BkColor = 0x0;
2037 COLORREF TextColor = 0x0;
2038 INT BkMode = 0;
2039 INT runStart, runEnd;
2040 INT iGlyph, cGlyphs;
2041 HFONT oldFont = 0x0;
2042 RECT crc;
2043 int i;
2045 TRACE("(%p,%d,%d,%d,%d,%d, 0x%1x, %d, %d)\n",
2046 ssa, iX, iY, iItem, cStart, cEnd, uOptions, fSelected, fDisabled);
2048 if (!(analysis = ssa)) return E_INVALIDARG;
2050 if ((cStart >= 0 && analysis->pItem[iItem+1].iCharPos <= cStart) ||
2051 (cEnd >= 0 && analysis->pItem[iItem].iCharPos >= cEnd))
2052 return S_OK;
2054 CopyRect(&crc,prc);
2055 if (fSelected)
2057 BkMode = GetBkMode(analysis->hdc);
2058 SetBkMode( analysis->hdc, OPAQUE);
2059 BkColor = GetBkColor(analysis->hdc);
2060 SetBkColor(analysis->hdc, GetSysColor(COLOR_HIGHLIGHT));
2061 if (!fDisabled)
2063 TextColor = GetTextColor(analysis->hdc);
2064 SetTextColor(analysis->hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
2067 if (analysis->glyphs[iItem].fallbackFont)
2068 oldFont = SelectObject(analysis->hdc, analysis->glyphs[iItem].fallbackFont);
2070 if (cStart >= 0 && analysis->pItem[iItem+1].iCharPos > cStart && analysis->pItem[iItem].iCharPos <= cStart)
2071 runStart = cStart - analysis->pItem[iItem].iCharPos;
2072 else
2073 runStart = 0;
2074 if (cEnd >= 0 && analysis->pItem[iItem+1].iCharPos > cEnd && analysis->pItem[iItem].iCharPos <= cEnd)
2075 runEnd = (cEnd-1) - analysis->pItem[iItem].iCharPos;
2076 else
2077 runEnd = (analysis->pItem[iItem+1].iCharPos - analysis->pItem[iItem].iCharPos) - 1;
2079 if (analysis->pItem[iItem].a.fRTL)
2081 if (cEnd >= 0 && cEnd < analysis->pItem[iItem+1].iCharPos)
2082 ScriptStringCPtoX(ssa, cEnd, FALSE, &off_x);
2083 else
2084 ScriptStringCPtoX(ssa, analysis->pItem[iItem+1].iCharPos-1, TRUE, &off_x);
2085 crc.left = iX + off_x;
2087 else
2089 if (cStart >=0 && runStart)
2090 ScriptStringCPtoX(ssa, cStart, FALSE, &off_x);
2091 else
2092 ScriptStringCPtoX(ssa, analysis->pItem[iItem].iCharPos, FALSE, &off_x);
2093 crc.left = iX + off_x;
2096 if (analysis->pItem[iItem].a.fRTL)
2097 iGlyph = analysis->glyphs[iItem].pwLogClust[runEnd];
2098 else
2099 iGlyph = analysis->glyphs[iItem].pwLogClust[runStart];
2101 if (analysis->pItem[iItem].a.fRTL)
2102 cGlyphs = analysis->glyphs[iItem].pwLogClust[runStart] - iGlyph;
2103 else
2104 cGlyphs = analysis->glyphs[iItem].pwLogClust[runEnd] - iGlyph;
2106 cGlyphs++;
2108 /* adjust for cluster glyphs when starting */
2109 if (analysis->pItem[iItem].a.fRTL)
2110 i = analysis->pItem[iItem+1].iCharPos - 1;
2111 else
2112 i = analysis->pItem[iItem].iCharPos;
2114 for (; i >=analysis->pItem[iItem].iCharPos && i < analysis->pItem[iItem+1].iCharPos; (analysis->pItem[iItem].a.fRTL)?i--:i++)
2116 if (analysis->glyphs[iItem].pwLogClust[i - analysis->pItem[iItem].iCharPos] == iGlyph)
2118 if (analysis->pItem[iItem].a.fRTL)
2119 ScriptStringCPtoX(ssa, i, TRUE, &off_x);
2120 else
2121 ScriptStringCPtoX(ssa, i, FALSE, &off_x);
2122 break;
2126 if (cEnd < 0 || scriptInformation[analysis->pItem[iItem].a.eScript].props.fNeedsCaretInfo)
2128 INT direction;
2129 INT clust_glyph;
2131 clust_glyph = iGlyph + cGlyphs;
2132 if (analysis->pItem[iItem].a.fRTL)
2133 direction = -1;
2134 else
2135 direction = 1;
2137 while(clust_glyph < analysis->glyphs[iItem].numGlyphs &&
2138 !does_glyph_start_cluster(analysis->glyphs[iItem].psva, analysis->glyphs[iItem].pwLogClust, (analysis->pItem[iItem+1].iCharPos - analysis->pItem[iItem].iCharPos), clust_glyph, direction))
2140 cGlyphs++;
2141 clust_glyph++;
2145 hr = ScriptTextOut(analysis->hdc,
2146 (SCRIPT_CACHE *)&analysis->glyphs[iItem].sc, iX + off_x,
2147 iY, uOptions, &crc, &analysis->pItem[iItem].a, NULL, 0,
2148 &analysis->glyphs[iItem].glyphs[iGlyph], cGlyphs,
2149 &analysis->glyphs[iItem].piAdvance[iGlyph], NULL,
2150 &analysis->glyphs[iItem].pGoffset[iGlyph]);
2152 TRACE("ScriptTextOut hr=%08x\n", hr);
2154 if (fSelected)
2156 SetBkColor(analysis->hdc, BkColor);
2157 SetBkMode( analysis->hdc, BkMode);
2158 if (!fDisabled)
2159 SetTextColor(analysis->hdc, TextColor);
2161 if (analysis->glyphs[iItem].fallbackFont)
2162 SelectObject(analysis->hdc, oldFont);
2164 return hr;
2167 /***********************************************************************
2168 * ScriptStringOut (USP10.@)
2170 * This function takes the output of ScriptStringAnalyse and joins the segments
2171 * of glyphs and passes the resulting string to ScriptTextOut. ScriptStringOut
2172 * only processes glyphs.
2174 * Parameters:
2175 * ssa [I] buffer to hold the analysed string components
2176 * iX [I] X axis displacement for output
2177 * iY [I] Y axis displacement for output
2178 * uOptions [I] flags controlling output processing
2179 * prc [I] rectangle coordinates
2180 * iMinSel [I] starting pos for substringing output string
2181 * iMaxSel [I] ending pos for substringing output string
2182 * fDisabled [I] controls text highlighting
2184 * RETURNS
2185 * Success: S_OK
2186 * Failure: is the value returned by ScriptTextOut
2188 HRESULT WINAPI ScriptStringOut(SCRIPT_STRING_ANALYSIS ssa,
2189 int iX,
2190 int iY,
2191 UINT uOptions,
2192 const RECT *prc,
2193 int iMinSel,
2194 int iMaxSel,
2195 BOOL fDisabled)
2197 StringAnalysis *analysis;
2198 int item;
2199 HRESULT hr;
2201 TRACE("(%p,%d,%d,0x%1x,%p,%d,%d,%d)\n",
2202 ssa, iX, iY, uOptions, prc, iMinSel, iMaxSel, fDisabled);
2204 if (!(analysis = ssa)) return E_INVALIDARG;
2205 if (!(analysis->dwFlags & SSA_GLYPHS)) return E_INVALIDARG;
2207 for (item = 0; item < analysis->numItems; item++)
2209 hr = SS_ItemOut( ssa, iX, iY, analysis->logical2visual[item], -1, -1, uOptions, prc, FALSE, fDisabled);
2210 if (FAILED(hr))
2211 return hr;
2214 if (iMinSel < iMaxSel && (iMinSel > 0 || iMaxSel > 0))
2216 if (iMaxSel > 0 && iMinSel < 0)
2217 iMinSel = 0;
2218 for (item = 0; item < analysis->numItems; item++)
2220 hr = SS_ItemOut( ssa, iX, iY, analysis->logical2visual[item], iMinSel, iMaxSel, uOptions, prc, TRUE, fDisabled);
2221 if (FAILED(hr))
2222 return hr;
2226 return S_OK;
2229 /***********************************************************************
2230 * ScriptStringCPtoX (USP10.@)
2233 HRESULT WINAPI ScriptStringCPtoX(SCRIPT_STRING_ANALYSIS ssa, int icp, BOOL fTrailing, int* pX)
2235 int item;
2236 int runningX = 0;
2237 StringAnalysis* analysis = ssa;
2239 TRACE("(%p), %d, %d, (%p)\n", ssa, icp, fTrailing, pX);
2241 if (!ssa || !pX) return S_FALSE;
2242 if (!(analysis->dwFlags & SSA_GLYPHS)) return S_FALSE;
2244 /* icp out of range */
2245 if(icp < 0)
2247 analysis->invalid = TRUE;
2248 return E_INVALIDARG;
2251 for(item=0; item<analysis->numItems; item++)
2253 int CP, i;
2254 int offset;
2256 i = analysis->logical2visual[item];
2257 CP = analysis->pItem[i+1].iCharPos - analysis->pItem[i].iCharPos;
2258 /* initialize max extents for uninitialized runs */
2259 if (analysis->glyphs[i].iMaxPosX == -1)
2261 if (analysis->pItem[i].a.fRTL)
2262 ScriptCPtoX(0, FALSE, CP, analysis->glyphs[i].numGlyphs, analysis->glyphs[i].pwLogClust,
2263 analysis->glyphs[i].psva, analysis->glyphs[i].piAdvance,
2264 &analysis->pItem[i].a, &analysis->glyphs[i].iMaxPosX);
2265 else
2266 ScriptCPtoX(CP, TRUE, CP, analysis->glyphs[i].numGlyphs, analysis->glyphs[i].pwLogClust,
2267 analysis->glyphs[i].psva, analysis->glyphs[i].piAdvance,
2268 &analysis->pItem[i].a, &analysis->glyphs[i].iMaxPosX);
2271 if (icp >= analysis->pItem[i+1].iCharPos || icp < analysis->pItem[i].iCharPos)
2273 runningX += analysis->glyphs[i].iMaxPosX;
2274 continue;
2277 icp -= analysis->pItem[i].iCharPos;
2278 ScriptCPtoX(icp, fTrailing, CP, analysis->glyphs[i].numGlyphs, analysis->glyphs[i].pwLogClust,
2279 analysis->glyphs[i].psva, analysis->glyphs[i].piAdvance,
2280 &analysis->pItem[i].a, &offset);
2281 runningX += offset;
2283 *pX = runningX;
2284 return S_OK;
2287 /* icp out of range */
2288 analysis->invalid = TRUE;
2289 return E_INVALIDARG;
2292 /***********************************************************************
2293 * ScriptStringXtoCP (USP10.@)
2296 HRESULT WINAPI ScriptStringXtoCP(SCRIPT_STRING_ANALYSIS ssa, int iX, int* piCh, int* piTrailing)
2298 StringAnalysis* analysis = ssa;
2299 int item;
2301 TRACE("(%p), %d, (%p), (%p)\n", ssa, iX, piCh, piTrailing);
2303 if (!ssa || !piCh || !piTrailing) return S_FALSE;
2304 if (!(analysis->dwFlags & SSA_GLYPHS)) return S_FALSE;
2306 /* out of range */
2307 if(iX < 0)
2309 if (analysis->pItem[0].a.fRTL)
2311 *piCh = 1;
2312 *piTrailing = FALSE;
2314 else
2316 *piCh = -1;
2317 *piTrailing = TRUE;
2319 return S_OK;
2322 for(item=0; item<analysis->numItems; item++)
2324 int i;
2325 int CP;
2327 for (i = 0; i < analysis->numItems && analysis->logical2visual[i] != item; i++)
2328 /* nothing */;
2330 CP = analysis->pItem[i+1].iCharPos - analysis->pItem[i].iCharPos;
2331 /* initialize max extents for uninitialized runs */
2332 if (analysis->glyphs[i].iMaxPosX == -1)
2334 if (analysis->pItem[i].a.fRTL)
2335 ScriptCPtoX(0, FALSE, CP, analysis->glyphs[i].numGlyphs, analysis->glyphs[i].pwLogClust,
2336 analysis->glyphs[i].psva, analysis->glyphs[i].piAdvance,
2337 &analysis->pItem[i].a, &analysis->glyphs[i].iMaxPosX);
2338 else
2339 ScriptCPtoX(CP, TRUE, CP, analysis->glyphs[i].numGlyphs, analysis->glyphs[i].pwLogClust,
2340 analysis->glyphs[i].psva, analysis->glyphs[i].piAdvance,
2341 &analysis->pItem[i].a, &analysis->glyphs[i].iMaxPosX);
2344 if (iX > analysis->glyphs[i].iMaxPosX)
2346 iX -= analysis->glyphs[i].iMaxPosX;
2347 continue;
2350 ScriptXtoCP(iX, CP, analysis->glyphs[i].numGlyphs, analysis->glyphs[i].pwLogClust,
2351 analysis->glyphs[i].psva, analysis->glyphs[i].piAdvance,
2352 &analysis->pItem[i].a, piCh, piTrailing);
2353 *piCh += analysis->pItem[i].iCharPos;
2355 return S_OK;
2358 /* out of range */
2359 *piCh = analysis->pItem[analysis->numItems].iCharPos;
2360 *piTrailing = FALSE;
2362 return S_OK;
2366 /***********************************************************************
2367 * ScriptStringFree (USP10.@)
2369 * Free a string analysis.
2371 * PARAMS
2372 * pssa [I] string analysis.
2374 * RETURNS
2375 * Success: S_OK
2376 * Failure: Non-zero HRESULT value.
2378 HRESULT WINAPI ScriptStringFree(SCRIPT_STRING_ANALYSIS *pssa)
2380 StringAnalysis* analysis;
2381 BOOL invalid;
2382 int i;
2384 TRACE("(%p)\n", pssa);
2386 if (!pssa || !(analysis = *pssa)) return E_INVALIDARG;
2388 invalid = analysis->invalid;
2390 if (analysis->glyphs)
2392 for (i = 0; i < analysis->numItems; i++)
2394 heap_free(analysis->glyphs[i].glyphs);
2395 heap_free(analysis->glyphs[i].pwLogClust);
2396 heap_free(analysis->glyphs[i].piAdvance);
2397 heap_free(analysis->glyphs[i].psva);
2398 heap_free(analysis->glyphs[i].pGoffset);
2399 heap_free(analysis->glyphs[i].abc);
2400 if (analysis->glyphs[i].fallbackFont)
2401 DeleteObject(analysis->glyphs[i].fallbackFont);
2402 ScriptFreeCache((SCRIPT_CACHE *)&analysis->glyphs[i].sc);
2403 heap_free(analysis->glyphs[i].sc);
2405 heap_free(analysis->glyphs);
2408 heap_free(analysis->pItem);
2409 heap_free(analysis->logattrs);
2410 heap_free(analysis->sz);
2411 heap_free(analysis->logical2visual);
2412 heap_free(analysis);
2414 if (invalid) return E_INVALIDARG;
2415 return S_OK;
2418 static inline int get_cluster_size(const WORD *pwLogClust, int cChars, int item,
2419 int direction, int* iCluster, int *check_out)
2421 int clust_size = 1;
2422 int check;
2423 WORD clust = pwLogClust[item];
2425 for (check = item+direction; check < cChars && check >= 0; check+=direction)
2427 if (pwLogClust[check] == clust)
2429 clust_size ++;
2430 if (iCluster && *iCluster == -1)
2431 *iCluster = item;
2433 else break;
2436 if (check_out)
2437 *check_out = check;
2439 return clust_size;
2442 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)
2444 int advance;
2445 int log_clust_max;
2447 advance = piAdvance[glyph];
2449 if (pwLogClust[0] > pwLogClust[cChars-1])
2450 log_clust_max = pwLogClust[0];
2451 else
2452 log_clust_max = pwLogClust[cChars-1];
2454 if (glyph > log_clust_max)
2455 return advance;
2457 for (glyph+=direction; glyph < cGlyphs && glyph >= 0; glyph +=direction)
2460 if (does_glyph_start_cluster(pva, pwLogClust, cChars, glyph, direction))
2461 break;
2462 if (glyph > log_clust_max)
2463 break;
2464 advance += piAdvance[glyph];
2467 return advance;
2470 /***********************************************************************
2471 * ScriptCPtoX (USP10.@)
2474 HRESULT WINAPI ScriptCPtoX(int iCP,
2475 BOOL fTrailing,
2476 int cChars,
2477 int cGlyphs,
2478 const WORD *pwLogClust,
2479 const SCRIPT_VISATTR *psva,
2480 const int *piAdvance,
2481 const SCRIPT_ANALYSIS *psa,
2482 int *piX)
2484 int item;
2485 float iPosX;
2486 int iSpecial = -1;
2487 int iCluster = -1;
2488 int clust_size = 1;
2489 float special_size = 0.0;
2490 int iMaxPos = 0;
2491 int advance = 0;
2492 BOOL rtl = FALSE;
2494 TRACE("(%d,%d,%d,%d,%p,%p,%p,%p,%p)\n",
2495 iCP, fTrailing, cChars, cGlyphs, pwLogClust, psva, piAdvance,
2496 psa, piX);
2498 if (psa->fRTL && ! psa->fLogicalOrder)
2499 rtl = TRUE;
2501 if (fTrailing)
2502 iCP++;
2504 if (rtl)
2506 int max_clust = pwLogClust[0];
2508 for (item=0; item < cGlyphs; item++)
2509 if (pwLogClust[item] > max_clust)
2511 ERR("We do not handle non reversed clusters properly\n");
2512 break;
2515 iMaxPos = 0;
2516 for (item = max_clust; item >=0; item --)
2517 iMaxPos += piAdvance[item];
2520 iPosX = 0.0;
2521 for (item=0; item < iCP && item < cChars; item++)
2523 if (iSpecial == -1 && (iCluster == -1 || (iCluster != -1 && iCluster+clust_size <= item)))
2525 int check;
2526 int clust = pwLogClust[item];
2528 iCluster = -1;
2529 clust_size = get_cluster_size(pwLogClust, cChars, item, 1, &iCluster,
2530 &check);
2532 advance = get_glyph_cluster_advance(piAdvance, psva, pwLogClust, cGlyphs, cChars, clust, 1);
2534 if (check >= cChars && !iMaxPos)
2536 int glyph;
2537 for (glyph = clust; glyph < cGlyphs; glyph++)
2538 special_size += get_glyph_cluster_advance(piAdvance, psva, pwLogClust, cGlyphs, cChars, glyph, 1);
2539 iSpecial = item;
2540 special_size /= (cChars - item);
2541 iPosX += special_size;
2543 else
2545 if (scriptInformation[psa->eScript].props.fNeedsCaretInfo)
2547 clust_size --;
2548 if (clust_size == 0)
2549 iPosX += advance;
2551 else
2552 iPosX += advance / (float)clust_size;
2555 else if (iSpecial != -1)
2556 iPosX += special_size;
2557 else /* (iCluster != -1) */
2559 int adv = get_glyph_cluster_advance(piAdvance, psva, pwLogClust, cGlyphs, cChars, pwLogClust[iCluster], 1);
2560 if (scriptInformation[psa->eScript].props.fNeedsCaretInfo)
2562 clust_size --;
2563 if (clust_size == 0)
2564 iPosX += adv;
2566 else
2567 iPosX += adv / (float)clust_size;
2571 if (iMaxPos > 0)
2573 iPosX = iMaxPos - iPosX;
2574 if (iPosX < 0)
2575 iPosX = 0;
2578 *piX = iPosX;
2579 TRACE("*piX=%d\n", *piX);
2580 return S_OK;
2583 /* Count the number of characters in a cluster and its starting index*/
2584 static inline BOOL get_cluster_data(const WORD *pwLogClust, int cChars, int cluster_index, int *cluster_size, int *start_index)
2586 int size = 0;
2587 int i;
2589 for (i = 0; i < cChars; i++)
2591 if (pwLogClust[i] == cluster_index)
2593 if (!size && start_index)
2595 *start_index = i;
2596 if (!cluster_size)
2597 return TRUE;
2599 size++;
2601 else if (size) break;
2603 if (cluster_size)
2604 *cluster_size = size;
2606 return (size > 0);
2610 To handle multi-glyph clusters we need to find all the glyphs that are
2611 represented in the cluster. This involves finding the glyph whose
2612 index is the cluster index as well as whose glyph indices are greater than
2613 our cluster index but not part of a new cluster.
2615 Then we sum all those glyphs' advances.
2617 static inline int get_cluster_advance(const int* piAdvance,
2618 const SCRIPT_VISATTR *psva,
2619 const WORD *pwLogClust, int cGlyphs,
2620 int cChars, int cluster, int direction)
2622 int glyph_start;
2623 int glyph_end;
2624 int i, advance;
2626 if (direction > 0)
2627 i = 0;
2628 else
2629 i = (cChars - 1);
2631 for (glyph_start = -1, glyph_end = -1; i < cChars && i >= 0 && (glyph_start < 0 || glyph_end < 0); i+=direction)
2633 if (glyph_start < 0 && pwLogClust[i] != cluster) continue;
2634 if (pwLogClust[i] == cluster && glyph_start < 0) glyph_start = pwLogClust[i];
2635 if (glyph_start >= 0 && glyph_end < 0 && pwLogClust[i] != cluster) glyph_end = pwLogClust[i];
2637 if (glyph_end < 0)
2639 if (direction > 0)
2640 glyph_end = cGlyphs;
2641 else
2643 /* Don't fully understand multi-glyph reversed clusters yet,
2644 * do they occur for real or just in our test? */
2645 FIXME("multi-glyph reversed clusters found\n");
2646 glyph_end = glyph_start + 1;
2650 /* Check for fClusterStart, finding this generally would mean a malformed set of data */
2651 for (i = glyph_start+1; i< glyph_end; i++)
2653 if (psva[i].fClusterStart)
2655 glyph_end = i;
2656 break;
2660 for (advance = 0, i = glyph_start; i < glyph_end; i++)
2661 advance += piAdvance[i];
2663 return advance;
2667 /***********************************************************************
2668 * ScriptXtoCP (USP10.@)
2670 * Basic algorithm :
2671 * use piAdvance to find the cluster we are looking at
2672 * Find the character that is the first character of the cluster
2673 * That is our base piCP
2674 * If the script snaps to cluster boundries (Hebrew, Indic, Thai) then we
2675 * are good Otherwise if the cluster is larger than 1 glyph we need to
2676 * determine how far through the cluster to advance the cursor.
2678 HRESULT WINAPI ScriptXtoCP(int iX,
2679 int cChars,
2680 int cGlyphs,
2681 const WORD *pwLogClust,
2682 const SCRIPT_VISATTR *psva,
2683 const int *piAdvance,
2684 const SCRIPT_ANALYSIS *psa,
2685 int *piCP,
2686 int *piTrailing)
2688 int direction = 1;
2689 int iPosX;
2690 int i;
2691 int glyph_index, cluster_index;
2692 int cluster_size;
2694 TRACE("(%d,%d,%d,%p,%p,%p,%p,%p,%p)\n",
2695 iX, cChars, cGlyphs, pwLogClust, psva, piAdvance,
2696 psa, piCP, piTrailing);
2698 if (psa->fRTL && ! psa->fLogicalOrder)
2699 direction = -1;
2701 /* Handle an iX < 0 */
2702 if (iX < 0)
2704 if (direction < 0)
2706 *piCP = cChars;
2707 *piTrailing = 0;
2709 else
2711 *piCP = -1;
2712 *piTrailing = 1;
2714 return S_OK;
2717 /* Looking for non-reversed clusters in a reversed string */
2718 if (direction < 0)
2720 int max_clust = pwLogClust[0];
2721 for (i=0; i< cChars; i++)
2722 if (pwLogClust[i] > max_clust)
2724 FIXME("We do not handle non reversed clusters properly\n");
2725 break;
2729 /* find the glyph_index based in iX */
2730 if (direction > 0)
2732 for (glyph_index = -1, iPosX = iX; iPosX >=0 && glyph_index < cGlyphs; iPosX -= piAdvance[glyph_index+1], glyph_index++)
2735 else
2737 for (glyph_index = -1, iPosX = iX; iPosX > 0 && glyph_index < cGlyphs; iPosX -= piAdvance[glyph_index+1], glyph_index++)
2741 TRACE("iPosX %i -> glyph_index %i (%i)\n", iPosX, glyph_index, cGlyphs);
2743 *piTrailing = 0;
2744 if (glyph_index >= 0 && glyph_index < cGlyphs)
2746 /* find the cluster */
2747 if (direction > 0 )
2748 for (i = 0, cluster_index = pwLogClust[0]; i < cChars && pwLogClust[i] <= glyph_index; cluster_index=pwLogClust[i++])
2750 else
2751 for (i = 0, cluster_index = pwLogClust[0]; i < cChars && pwLogClust[i] >= glyph_index; cluster_index=pwLogClust[i++])
2754 TRACE("cluster_index %i\n", cluster_index);
2756 if (direction < 0 && iPosX >= 0 && glyph_index != cluster_index)
2758 /* We are off the end of the string */
2759 *piCP = -1;
2760 *piTrailing = 1;
2761 return S_OK;
2764 get_cluster_data(pwLogClust, cChars, cluster_index, &cluster_size, &i);
2766 TRACE("first char index %i\n",i);
2767 if (scriptInformation[psa->eScript].props.fNeedsCaretInfo)
2769 /* Check trailing */
2770 if (glyph_index != cluster_index ||
2771 (direction > 0 && abs(iPosX) <= (piAdvance[glyph_index] / 2)) ||
2772 (direction < 0 && abs(iPosX) >= (piAdvance[glyph_index] / 2)))
2773 *piTrailing = cluster_size;
2775 else
2777 if (cluster_size > 1)
2779 /* Be part way through the glyph cluster based on size and position */
2780 int cluster_advance = get_cluster_advance(piAdvance, psva, pwLogClust, cGlyphs, cChars, cluster_index, direction);
2781 double cluster_part_width = cluster_advance / (float)cluster_size;
2782 double adv;
2783 int part_index;
2785 /* back up to the beginning of the cluster */
2786 for (adv = iPosX, part_index = cluster_index; part_index <= glyph_index; part_index++)
2787 adv += piAdvance[part_index];
2788 if (adv > iX) adv = iX;
2790 TRACE("Multi-char cluster, no snap\n");
2791 TRACE("cluster size %i, pre-cluster iPosX %f\n",cluster_size, adv);
2792 TRACE("advance %i divides into %f per char\n", cluster_advance, cluster_part_width);
2793 if (direction > 0)
2795 for (part_index = 0; adv >= 0; adv-=cluster_part_width, part_index++)
2797 if (part_index) part_index--;
2799 else
2801 for (part_index = 0; adv > 0; adv-=cluster_part_width, part_index++)
2803 if (part_index > cluster_size)
2805 adv += cluster_part_width;
2806 part_index=cluster_size;
2810 TRACE("base_char %i part_index %i, leftover advance %f\n",i, part_index, adv);
2812 if (direction > 0)
2813 i += part_index;
2814 else
2815 i += (cluster_size - part_index);
2817 /* Check trailing */
2818 if ((direction > 0 && fabs(adv) <= (cluster_part_width / 2.0)) ||
2819 (direction < 0 && adv && fabs(adv) >= (cluster_part_width / 2.0)))
2820 *piTrailing = 1;
2822 else
2824 /* Check trailing */
2825 if ((direction > 0 && abs(iPosX) <= (piAdvance[glyph_index] / 2)) ||
2826 (direction < 0 && abs(iPosX) >= (piAdvance[glyph_index] / 2)))
2827 *piTrailing = 1;
2831 else
2833 TRACE("Point falls outside of string\n");
2834 if (glyph_index < 0)
2835 i = cChars-1;
2836 else /* (glyph_index >= cGlyphs) */
2837 i = cChars;
2839 /* If not snaping in the reverse direction (such as Hebrew) Then 0
2840 point flow to the next character */
2841 if (direction < 0)
2843 if (!scriptInformation[psa->eScript].props.fNeedsCaretInfo && abs(iPosX) == piAdvance[glyph_index])
2844 i++;
2845 else
2846 *piTrailing = 1;
2850 *piCP = i;
2852 TRACE("*piCP=%d\n", *piCP);
2853 TRACE("*piTrailing=%d\n", *piTrailing);
2854 return S_OK;
2857 /***********************************************************************
2858 * ScriptBreak (USP10.@)
2860 * Retrieve line break information.
2862 * PARAMS
2863 * chars [I] Array of characters.
2864 * sa [I] String analysis.
2865 * la [I] Array of logical attribute structures.
2867 * RETURNS
2868 * Success: S_OK
2869 * Failure: S_FALSE
2871 HRESULT WINAPI ScriptBreak(const WCHAR *chars, int count, const SCRIPT_ANALYSIS *sa, SCRIPT_LOGATTR *la)
2873 TRACE("(%s, %d, %p, %p)\n", debugstr_wn(chars, count), count, sa, la);
2875 if (count < 0 || !la) return E_INVALIDARG;
2876 if (count == 0) return E_FAIL;
2878 BREAK_line(chars, count, sa, la);
2880 return S_OK;
2883 /***********************************************************************
2884 * ScriptIsComplex (USP10.@)
2886 * Determine if a string is complex.
2888 * PARAMS
2889 * chars [I] Array of characters to test.
2890 * len [I] Length in characters.
2891 * flag [I] Flag.
2893 * RETURNS
2894 * Success: S_OK
2895 * Failure: S_FALSE
2898 HRESULT WINAPI ScriptIsComplex(const WCHAR *chars, int len, DWORD flag)
2900 int i;
2901 INT consumed = 0;
2903 TRACE("(%s,%d,0x%x)\n", debugstr_wn(chars, len), len, flag);
2905 for (i = 0; i < len; i+=consumed)
2907 int script;
2908 if (i >= len)
2909 break;
2911 if ((flag & SIC_ASCIIDIGIT) && chars[i] >= 0x30 && chars[i] <= 0x39)
2912 return S_OK;
2914 script = get_char_script(chars,i,len, &consumed);
2915 if ((scriptInformation[script].props.fComplex && (flag & SIC_COMPLEX))||
2916 (!scriptInformation[script].props.fComplex && (flag & SIC_NEUTRAL)))
2917 return S_OK;
2919 return S_FALSE;
2922 /***********************************************************************
2923 * ScriptShapeOpenType (USP10.@)
2925 * Produce glyphs and visual attributes for a run.
2927 * PARAMS
2928 * hdc [I] Device context.
2929 * psc [I/O] Opaque pointer to a script cache.
2930 * psa [I/O] Script analysis.
2931 * tagScript [I] The OpenType tag for the Script
2932 * tagLangSys [I] The OpenType tag for the Language
2933 * rcRangeChars[I] Array of Character counts in each range
2934 * rpRangeProperties [I] Array of TEXTRANGE_PROPERTIES structures
2935 * cRanges [I] Count of ranges
2936 * pwcChars [I] Array of characters specifying the run.
2937 * cChars [I] Number of characters in pwcChars.
2938 * cMaxGlyphs [I] Length of pwOutGlyphs.
2939 * pwLogClust [O] Array of logical cluster info.
2940 * pCharProps [O] Array of character property values
2941 * pwOutGlyphs [O] Array of glyphs.
2942 * pOutGlyphProps [O] Array of attributes for the retrieved glyphs
2943 * pcGlyphs [O] Number of glyphs returned.
2945 * RETURNS
2946 * Success: S_OK
2947 * Failure: Non-zero HRESULT value.
2949 HRESULT WINAPI ScriptShapeOpenType( HDC hdc, SCRIPT_CACHE *psc,
2950 SCRIPT_ANALYSIS *psa, OPENTYPE_TAG tagScript,
2951 OPENTYPE_TAG tagLangSys, int *rcRangeChars,
2952 TEXTRANGE_PROPERTIES **rpRangeProperties,
2953 int cRanges, const WCHAR *pwcChars, int cChars,
2954 int cMaxGlyphs, WORD *pwLogClust,
2955 SCRIPT_CHARPROP *pCharProps, WORD *pwOutGlyphs,
2956 SCRIPT_GLYPHPROP *pOutGlyphProps, int *pcGlyphs)
2958 HRESULT hr;
2959 int i;
2960 unsigned int g;
2961 BOOL rtl;
2962 int cluster;
2963 static int once = 0;
2965 TRACE("(%p, %p, %p, %s, %s, %p, %p, %d, %s, %d, %d, %p, %p, %p, %p, %p )\n",
2966 hdc, psc, psa,
2967 debugstr_an((char*)&tagScript,4), debugstr_an((char*)&tagLangSys,4),
2968 rcRangeChars, rpRangeProperties, cRanges, debugstr_wn(pwcChars, cChars),
2969 cChars, cMaxGlyphs, pwLogClust, pCharProps, pwOutGlyphs, pOutGlyphProps, pcGlyphs);
2971 if (psa) TRACE("psa values: %d, %d, %d, %d, %d, %d, %d\n", psa->eScript, psa->fRTL, psa->fLayoutRTL,
2972 psa->fLinkBefore, psa->fLinkAfter, psa->fLogicalOrder, psa->fNoGlyphIndex);
2974 if (!pOutGlyphProps || !pcGlyphs || !pCharProps) return E_INVALIDARG;
2975 if (cChars > cMaxGlyphs) return E_OUTOFMEMORY;
2977 if (cRanges)
2978 if(!once++) FIXME("Ranges not supported yet\n");
2980 rtl = (psa && !psa->fLogicalOrder && psa->fRTL);
2982 *pcGlyphs = cChars;
2983 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
2984 if (!pwLogClust) return E_FAIL;
2986 ((ScriptCache *)*psc)->userScript = tagScript;
2987 ((ScriptCache *)*psc)->userLang = tagLangSys;
2989 /* set fNoGlyphIndex non truetype/opentype fonts */
2990 if (psa && !psa->fNoGlyphIndex && !((ScriptCache *)*psc)->sfnt)
2991 psa->fNoGlyphIndex = TRUE;
2993 /* Initialize a SCRIPT_VISATTR and LogClust for each char in this run */
2994 for (i = 0; i < cChars; i++)
2996 int idx = i;
2997 if (rtl) idx = cChars - 1 - i;
2998 /* FIXME: set to better values */
2999 pOutGlyphProps[i].sva.uJustification = (pwcChars[idx] == ' ') ? SCRIPT_JUSTIFY_BLANK : SCRIPT_JUSTIFY_CHARACTER;
3000 pOutGlyphProps[i].sva.fClusterStart = 1;
3001 pOutGlyphProps[i].sva.fDiacritic = 0;
3002 pOutGlyphProps[i].sva.fZeroWidth = 0;
3003 pOutGlyphProps[i].sva.fReserved = 0;
3004 pOutGlyphProps[i].sva.fShapeReserved = 0;
3006 /* FIXME: have the shaping engine set this */
3007 pCharProps[i].fCanGlyphAlone = 0;
3009 pwLogClust[i] = idx;
3012 if (psa && !psa->fNoGlyphIndex)
3014 WCHAR *rChars;
3015 if ((hr = SHAPE_CheckFontForRequiredFeatures(hdc, (ScriptCache *)*psc, psa)) != S_OK) return hr;
3017 rChars = heap_alloc(sizeof(WCHAR) * cChars);
3018 if (!rChars) return E_OUTOFMEMORY;
3019 for (i = 0, g = 0, cluster = 0; i < cChars; i++)
3021 int idx = i;
3022 DWORD chInput;
3024 if (rtl) idx = cChars - 1 - i;
3025 if (!cluster)
3027 chInput = decode_surrogate_pair(pwcChars, idx, cChars);
3028 if (!chInput)
3030 if (psa->fRTL)
3031 chInput = mirror_char(pwcChars[idx]);
3032 else
3033 chInput = pwcChars[idx];
3034 /* special case for tabs */
3035 if (chInput == 0x0009)
3036 chInput = 0x0020;
3037 rChars[i] = chInput;
3039 else
3041 rChars[i] = pwcChars[idx];
3042 rChars[i+1] = pwcChars[(rtl)?idx-1:idx+1];
3043 cluster = 1;
3045 if (!(pwOutGlyphs[g] = get_cache_glyph(psc, chInput)))
3047 WORD glyph;
3048 if (!hdc)
3050 heap_free(rChars);
3051 return E_PENDING;
3053 if (OpenType_CMAP_GetGlyphIndex(hdc, (ScriptCache *)*psc, chInput, &glyph, 0) == GDI_ERROR)
3055 heap_free(rChars);
3056 return S_FALSE;
3058 pwOutGlyphs[g] = set_cache_glyph(psc, chInput, glyph);
3060 g++;
3062 else
3064 int k;
3065 cluster--;
3066 pwLogClust[idx] = (rtl)?pwLogClust[idx+1]:pwLogClust[idx-1];
3067 for (k = (rtl)?idx-1:idx+1; k >= 0 && k < cChars; (rtl)?k--:k++)
3068 pwLogClust[k]--;
3071 *pcGlyphs = g;
3073 SHAPE_ContextualShaping(hdc, (ScriptCache *)*psc, psa, rChars, cChars, pwOutGlyphs, pcGlyphs, cMaxGlyphs, pwLogClust);
3074 SHAPE_ApplyDefaultOpentypeFeatures(hdc, (ScriptCache *)*psc, psa, pwOutGlyphs, pcGlyphs, cMaxGlyphs, cChars, pwLogClust);
3075 SHAPE_CharGlyphProp(hdc, (ScriptCache *)*psc, psa, pwcChars, cChars, pwOutGlyphs, *pcGlyphs, pwLogClust, pCharProps, pOutGlyphProps);
3076 heap_free(rChars);
3078 else
3080 TRACE("no glyph translation\n");
3081 for (i = 0; i < cChars; i++)
3083 int idx = i;
3084 /* No mirroring done here */
3085 if (rtl) idx = cChars - 1 - i;
3086 pwOutGlyphs[i] = pwcChars[idx];
3088 /* overwrite some basic control glyphs to blank */
3089 if (psa && psa->eScript == Script_Control &&
3090 pwcChars[idx] < ((ScriptCache *)*psc)->tm.tmFirstChar)
3092 if (pwcChars[idx] == 0x0009 || pwcChars[idx] == 0x000A ||
3093 pwcChars[idx] == 0x000D || pwcChars[idx] >= 0x001C)
3094 pwOutGlyphs[i] = ((ScriptCache *)*psc)->sfp.wgBlank;
3099 return S_OK;
3103 /***********************************************************************
3104 * ScriptShape (USP10.@)
3106 * Produce glyphs and visual attributes for a run.
3108 * PARAMS
3109 * hdc [I] Device context.
3110 * psc [I/O] Opaque pointer to a script cache.
3111 * pwcChars [I] Array of characters specifying the run.
3112 * cChars [I] Number of characters in pwcChars.
3113 * cMaxGlyphs [I] Length of pwOutGlyphs.
3114 * psa [I/O] Script analysis.
3115 * pwOutGlyphs [O] Array of glyphs.
3116 * pwLogClust [O] Array of logical cluster info.
3117 * psva [O] Array of visual attributes.
3118 * pcGlyphs [O] Number of glyphs returned.
3120 * RETURNS
3121 * Success: S_OK
3122 * Failure: Non-zero HRESULT value.
3124 HRESULT WINAPI ScriptShape(HDC hdc, SCRIPT_CACHE *psc, const WCHAR *pwcChars,
3125 int cChars, int cMaxGlyphs,
3126 SCRIPT_ANALYSIS *psa, WORD *pwOutGlyphs, WORD *pwLogClust,
3127 SCRIPT_VISATTR *psva, int *pcGlyphs)
3129 HRESULT hr;
3130 int i;
3131 SCRIPT_CHARPROP *charProps;
3132 SCRIPT_GLYPHPROP *glyphProps;
3134 if (!psva || !pcGlyphs) return E_INVALIDARG;
3135 if (cChars > cMaxGlyphs) return E_OUTOFMEMORY;
3137 charProps = heap_alloc_zero(sizeof(SCRIPT_CHARPROP)*cChars);
3138 if (!charProps) return E_OUTOFMEMORY;
3139 glyphProps = heap_alloc_zero(sizeof(SCRIPT_GLYPHPROP)*cMaxGlyphs);
3140 if (!glyphProps)
3142 heap_free(charProps);
3143 return E_OUTOFMEMORY;
3146 hr = ScriptShapeOpenType(hdc, psc, psa, scriptInformation[psa->eScript].scriptTag, 0, NULL, NULL, 0, pwcChars, cChars, cMaxGlyphs, pwLogClust, charProps, pwOutGlyphs, glyphProps, pcGlyphs);
3148 if (SUCCEEDED(hr))
3150 for (i = 0; i < *pcGlyphs; i++)
3151 psva[i] = glyphProps[i].sva;
3154 heap_free(charProps);
3155 heap_free(glyphProps);
3157 return hr;
3160 /***********************************************************************
3161 * ScriptPlaceOpenType (USP10.@)
3163 * Produce advance widths for a run.
3165 * PARAMS
3166 * hdc [I] Device context.
3167 * psc [I/O] Opaque pointer to a script cache.
3168 * psa [I/O] String analysis.
3169 * tagScript [I] The OpenType tag for the Script
3170 * tagLangSys [I] The OpenType tag for the Language
3171 * rcRangeChars[I] Array of Character counts in each range
3172 * rpRangeProperties [I] Array of TEXTRANGE_PROPERTIES structures
3173 * cRanges [I] Count of ranges
3174 * pwcChars [I] Array of characters specifying the run.
3175 * pwLogClust [I] Array of logical cluster info
3176 * pCharProps [I] Array of character property values
3177 * cChars [I] Number of characters in pwcChars.
3178 * pwGlyphs [I] Array of glyphs.
3179 * pGlyphProps [I] Array of attributes for the retrieved glyphs
3180 * cGlyphs [I] Count of Glyphs
3181 * piAdvance [O] Array of advance widths.
3182 * pGoffset [O] Glyph offsets.
3183 * pABC [O] Combined ABC width.
3185 * RETURNS
3186 * Success: S_OK
3187 * Failure: Non-zero HRESULT value.
3190 HRESULT WINAPI ScriptPlaceOpenType( HDC hdc, SCRIPT_CACHE *psc, SCRIPT_ANALYSIS *psa,
3191 OPENTYPE_TAG tagScript, OPENTYPE_TAG tagLangSys,
3192 int *rcRangeChars, TEXTRANGE_PROPERTIES **rpRangeProperties,
3193 int cRanges, const WCHAR *pwcChars, WORD *pwLogClust,
3194 SCRIPT_CHARPROP *pCharProps, int cChars,
3195 const WORD *pwGlyphs, const SCRIPT_GLYPHPROP *pGlyphProps,
3196 int cGlyphs, int *piAdvance,
3197 GOFFSET *pGoffset, ABC *pABC
3200 HRESULT hr;
3201 int i;
3202 static int once = 0;
3204 TRACE("(%p, %p, %p, %s, %s, %p, %p, %d, %s, %p, %p, %d, %p, %p, %d, %p %p %p)\n",
3205 hdc, psc, psa,
3206 debugstr_an((char*)&tagScript,4), debugstr_an((char*)&tagLangSys,4),
3207 rcRangeChars, rpRangeProperties, cRanges, debugstr_wn(pwcChars, cChars),
3208 pwLogClust, pCharProps, cChars, pwGlyphs, pGlyphProps, cGlyphs, piAdvance,
3209 pGoffset, pABC);
3211 if (!pGlyphProps) return E_INVALIDARG;
3212 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
3213 if (!pGoffset) return E_FAIL;
3215 if (cRanges)
3216 if (!once++) FIXME("Ranges not supported yet\n");
3218 ((ScriptCache *)*psc)->userScript = tagScript;
3219 ((ScriptCache *)*psc)->userLang = tagLangSys;
3221 if (pABC) memset(pABC, 0, sizeof(ABC));
3222 for (i = 0; i < cGlyphs; i++)
3224 ABC abc;
3225 if (!get_cache_glyph_widths(psc, pwGlyphs[i], &abc))
3227 if (!hdc) return E_PENDING;
3228 if ((get_cache_pitch_family(psc) & TMPF_TRUETYPE) && !psa->fNoGlyphIndex)
3230 if (!GetCharABCWidthsI(hdc, 0, 1, (WORD *)&pwGlyphs[i], &abc)) return S_FALSE;
3232 else
3234 INT width;
3235 if (!GetCharWidth32W(hdc, pwGlyphs[i], pwGlyphs[i], &width)) return S_FALSE;
3236 abc.abcB = width;
3237 abc.abcA = abc.abcC = 0;
3239 set_cache_glyph_widths(psc, pwGlyphs[i], &abc);
3241 if (pABC)
3243 pABC->abcA += abc.abcA;
3244 pABC->abcB += abc.abcB;
3245 pABC->abcC += abc.abcC;
3247 /* FIXME: set to more reasonable values */
3248 pGoffset[i].du = pGoffset[i].dv = 0;
3249 if (piAdvance) piAdvance[i] = abc.abcA + abc.abcB + abc.abcC;
3252 SHAPE_ApplyOpenTypePositions(hdc, (ScriptCache *)*psc, psa, pwGlyphs, cGlyphs, piAdvance, pGoffset);
3254 if (pABC) TRACE("Total for run: abcA=%d, abcB=%d, abcC=%d\n", pABC->abcA, pABC->abcB, pABC->abcC);
3255 return S_OK;
3258 /***********************************************************************
3259 * ScriptPlace (USP10.@)
3261 * Produce advance widths for a run.
3263 * PARAMS
3264 * hdc [I] Device context.
3265 * psc [I/O] Opaque pointer to a script cache.
3266 * pwGlyphs [I] Array of glyphs.
3267 * cGlyphs [I] Number of glyphs in pwGlyphs.
3268 * psva [I] Array of visual attributes.
3269 * psa [I/O] String analysis.
3270 * piAdvance [O] Array of advance widths.
3271 * pGoffset [O] Glyph offsets.
3272 * pABC [O] Combined ABC width.
3274 * RETURNS
3275 * Success: S_OK
3276 * Failure: Non-zero HRESULT value.
3278 HRESULT WINAPI ScriptPlace(HDC hdc, SCRIPT_CACHE *psc, const WORD *pwGlyphs,
3279 int cGlyphs, const SCRIPT_VISATTR *psva,
3280 SCRIPT_ANALYSIS *psa, int *piAdvance, GOFFSET *pGoffset, ABC *pABC )
3282 HRESULT hr;
3283 SCRIPT_GLYPHPROP *glyphProps;
3284 int i;
3286 TRACE("(%p, %p, %p, %d, %p, %p, %p, %p, %p)\n", hdc, psc, pwGlyphs, cGlyphs, psva, psa,
3287 piAdvance, pGoffset, pABC);
3289 if (!psva) return E_INVALIDARG;
3290 if (!pGoffset) return E_FAIL;
3292 glyphProps = heap_alloc(sizeof(SCRIPT_GLYPHPROP)*cGlyphs);
3293 if (!glyphProps) return E_OUTOFMEMORY;
3295 for (i = 0; i < cGlyphs; i++)
3296 glyphProps[i].sva = psva[i];
3298 hr = ScriptPlaceOpenType(hdc, psc, psa, scriptInformation[psa->eScript].scriptTag, 0, NULL, NULL, 0, NULL, NULL, NULL, 0, pwGlyphs, glyphProps, cGlyphs, piAdvance, pGoffset, pABC);
3300 heap_free(glyphProps);
3302 return hr;
3305 /***********************************************************************
3306 * ScriptGetCMap (USP10.@)
3308 * Retrieve glyph indices.
3310 * PARAMS
3311 * hdc [I] Device context.
3312 * psc [I/O] Opaque pointer to a script cache.
3313 * pwcInChars [I] Array of Unicode characters.
3314 * cChars [I] Number of characters in pwcInChars.
3315 * dwFlags [I] Flags.
3316 * pwOutGlyphs [O] Buffer to receive the array of glyph indices.
3318 * RETURNS
3319 * Success: S_OK
3320 * Failure: Non-zero HRESULT value.
3322 HRESULT WINAPI ScriptGetCMap(HDC hdc, SCRIPT_CACHE *psc, const WCHAR *pwcInChars,
3323 int cChars, DWORD dwFlags, WORD *pwOutGlyphs)
3325 HRESULT hr;
3326 int i;
3328 TRACE("(%p,%p,%s,%d,0x%x,%p)\n", hdc, psc, debugstr_wn(pwcInChars, cChars),
3329 cChars, dwFlags, pwOutGlyphs);
3331 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
3333 hr = S_OK;
3335 if ((get_cache_pitch_family(psc) & TMPF_TRUETYPE))
3337 for (i = 0; i < cChars; i++)
3339 WCHAR inChar;
3340 if (dwFlags == SGCM_RTL)
3341 inChar = mirror_char(pwcInChars[i]);
3342 else
3343 inChar = pwcInChars[i];
3344 if (!(pwOutGlyphs[i] = get_cache_glyph(psc, inChar)))
3346 WORD glyph;
3347 if (!hdc) return E_PENDING;
3348 if (GetGlyphIndicesW(hdc, &inChar, 1, &glyph, GGI_MARK_NONEXISTING_GLYPHS) == GDI_ERROR) return S_FALSE;
3349 if (glyph == 0xffff)
3351 hr = S_FALSE;
3352 glyph = 0x0;
3354 pwOutGlyphs[i] = set_cache_glyph(psc, inChar, glyph);
3358 else
3360 TRACE("no glyph translation\n");
3361 for (i = 0; i < cChars; i++)
3363 WCHAR inChar;
3364 if (dwFlags == SGCM_RTL)
3365 inChar = mirror_char(pwcInChars[i]);
3366 else
3367 inChar = pwcInChars[i];
3368 pwOutGlyphs[i] = inChar;
3371 return hr;
3374 /***********************************************************************
3375 * ScriptTextOut (USP10.@)
3378 HRESULT WINAPI ScriptTextOut(const HDC hdc, SCRIPT_CACHE *psc, int x, int y, UINT fuOptions,
3379 const RECT *lprc, const SCRIPT_ANALYSIS *psa, const WCHAR *pwcReserved,
3380 int iReserved, const WORD *pwGlyphs, int cGlyphs, const int *piAdvance,
3381 const int *piJustify, const GOFFSET *pGoffset)
3383 HRESULT hr = S_OK;
3384 INT i, dir = 1;
3385 INT *lpDx;
3386 WORD *reordered_glyphs = (WORD *)pwGlyphs;
3388 TRACE("(%p, %p, %d, %d, %04x, %p, %p, %p, %d, %p, %d, %p, %p, %p)\n",
3389 hdc, psc, x, y, fuOptions, lprc, psa, pwcReserved, iReserved, pwGlyphs, cGlyphs,
3390 piAdvance, piJustify, pGoffset);
3392 if (!hdc || !psc) return E_INVALIDARG;
3393 if (!piAdvance || !psa || !pwGlyphs) return E_INVALIDARG;
3395 fuOptions &= ETO_CLIPPED + ETO_OPAQUE;
3396 fuOptions |= ETO_IGNORELANGUAGE;
3397 if (!psa->fNoGlyphIndex) /* Have Glyphs? */
3398 fuOptions |= ETO_GLYPH_INDEX; /* Say don't do translation to glyph */
3400 lpDx = heap_alloc(cGlyphs * sizeof(INT) * 2);
3401 if (!lpDx) return E_OUTOFMEMORY;
3402 fuOptions |= ETO_PDY;
3404 if (psa->fRTL && psa->fLogicalOrder)
3406 reordered_glyphs = heap_alloc( cGlyphs * sizeof(WORD) );
3407 if (!reordered_glyphs)
3409 heap_free( lpDx );
3410 return E_OUTOFMEMORY;
3413 for (i = 0; i < cGlyphs; i++)
3414 reordered_glyphs[i] = pwGlyphs[cGlyphs - 1 - i];
3415 dir = -1;
3418 for (i = 0; i < cGlyphs; i++)
3420 int orig_index = (dir > 0) ? i : cGlyphs - 1 - i;
3421 lpDx[i * 2] = piAdvance[orig_index];
3422 lpDx[i * 2 + 1] = 0;
3424 if (pGoffset)
3426 if (i == 0)
3428 x += pGoffset[orig_index].du * dir;
3429 y += pGoffset[orig_index].dv;
3431 else
3433 lpDx[(i - 1) * 2] += pGoffset[orig_index].du * dir;
3434 lpDx[(i - 1) * 2 + 1] += pGoffset[orig_index].dv;
3436 lpDx[i * 2] -= pGoffset[orig_index].du * dir;
3437 lpDx[i * 2 + 1] -= pGoffset[orig_index].dv;
3441 if (!ExtTextOutW(hdc, x, y, fuOptions, lprc, reordered_glyphs, cGlyphs, lpDx))
3442 hr = S_FALSE;
3444 if (reordered_glyphs != pwGlyphs) heap_free( reordered_glyphs );
3445 heap_free(lpDx);
3447 return hr;
3450 /***********************************************************************
3451 * ScriptCacheGetHeight (USP10.@)
3453 * Retrieve the height of the font in the cache.
3455 * PARAMS
3456 * hdc [I] Device context.
3457 * psc [I/O] Opaque pointer to a script cache.
3458 * height [O] Receives font height.
3460 * RETURNS
3461 * Success: S_OK
3462 * Failure: Non-zero HRESULT value.
3464 HRESULT WINAPI ScriptCacheGetHeight(HDC hdc, SCRIPT_CACHE *psc, LONG *height)
3466 HRESULT hr;
3468 TRACE("(%p, %p, %p)\n", hdc, psc, height);
3470 if (!height) return E_INVALIDARG;
3471 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
3473 *height = get_cache_height(psc);
3474 return S_OK;
3477 /***********************************************************************
3478 * ScriptGetGlyphABCWidth (USP10.@)
3480 * Retrieve the width of a glyph.
3482 * PARAMS
3483 * hdc [I] Device context.
3484 * psc [I/O] Opaque pointer to a script cache.
3485 * glyph [I] Glyph to retrieve the width for.
3486 * abc [O] ABC widths of the glyph.
3488 * RETURNS
3489 * Success: S_OK
3490 * Failure: Non-zero HRESULT value.
3492 HRESULT WINAPI ScriptGetGlyphABCWidth(HDC hdc, SCRIPT_CACHE *psc, WORD glyph, ABC *abc)
3494 HRESULT hr;
3496 TRACE("(%p, %p, 0x%04x, %p)\n", hdc, psc, glyph, abc);
3498 if (!abc) return E_INVALIDARG;
3499 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
3501 if (!get_cache_glyph_widths(psc, glyph, abc))
3503 if (!hdc) return E_PENDING;
3504 if ((get_cache_pitch_family(psc) & TMPF_TRUETYPE))
3506 if (!GetCharABCWidthsI(hdc, 0, 1, &glyph, abc)) return S_FALSE;
3508 else
3510 INT width;
3511 if (!GetCharWidth32W(hdc, glyph, glyph, &width)) return S_FALSE;
3512 abc->abcB = width;
3513 abc->abcA = abc->abcC = 0;
3515 set_cache_glyph_widths(psc, glyph, abc);
3517 return S_OK;
3520 /***********************************************************************
3521 * ScriptLayout (USP10.@)
3523 * Map embedding levels to visual and/or logical order.
3525 * PARAMS
3526 * runs [I] Size of level array.
3527 * level [I] Array of embedding levels.
3528 * vistolog [O] Map of embedding levels from visual to logical order.
3529 * logtovis [O] Map of embedding levels from logical to visual order.
3531 * RETURNS
3532 * Success: S_OK
3533 * Failure: Non-zero HRESULT value.
3536 HRESULT WINAPI ScriptLayout(int runs, const BYTE *level, int *vistolog, int *logtovis)
3538 int* indexs;
3539 int ich;
3541 TRACE("(%d, %p, %p, %p)\n", runs, level, vistolog, logtovis);
3543 if (!level || (!vistolog && !logtovis))
3544 return E_INVALIDARG;
3546 indexs = heap_alloc(sizeof(int) * runs);
3547 if (!indexs)
3548 return E_OUTOFMEMORY;
3551 if (vistolog)
3553 for( ich = 0; ich < runs; ich++)
3554 indexs[ich] = ich;
3556 ich = 0;
3557 while (ich < runs)
3558 ich += BIDI_ReorderV2lLevel(0, indexs+ich, level+ich, runs - ich, FALSE);
3559 for (ich = 0; ich < runs; ich++)
3560 vistolog[ich] = indexs[ich];
3564 if (logtovis)
3566 for( ich = 0; ich < runs; ich++)
3567 indexs[ich] = ich;
3569 ich = 0;
3570 while (ich < runs)
3571 ich += BIDI_ReorderL2vLevel(0, indexs+ich, level+ich, runs - ich, FALSE);
3572 for (ich = 0; ich < runs; ich++)
3573 logtovis[ich] = indexs[ich];
3575 heap_free(indexs);
3577 return S_OK;
3580 /***********************************************************************
3581 * ScriptStringGetLogicalWidths (USP10.@)
3583 * Returns logical widths from a string analysis.
3585 * PARAMS
3586 * ssa [I] string analysis.
3587 * piDx [O] logical widths returned.
3589 * RETURNS
3590 * Success: S_OK
3591 * Failure: a non-zero HRESULT.
3593 HRESULT WINAPI ScriptStringGetLogicalWidths(SCRIPT_STRING_ANALYSIS ssa, int *piDx)
3595 int i, j, next = 0;
3596 StringAnalysis *analysis = ssa;
3598 TRACE("%p, %p\n", ssa, piDx);
3600 if (!analysis) return S_FALSE;
3601 if (!(analysis->dwFlags & SSA_GLYPHS)) return S_FALSE;
3603 for (i = 0; i < analysis->numItems; i++)
3605 int cChar = analysis->pItem[i+1].iCharPos - analysis->pItem[i].iCharPos;
3606 int direction = 1;
3608 if (analysis->pItem[i].a.fRTL && ! analysis->pItem[i].a.fLogicalOrder)
3609 direction = -1;
3611 for (j = 0; j < cChar; j++)
3613 int k;
3614 int glyph = analysis->glyphs[i].pwLogClust[j];
3615 int clust_size = get_cluster_size(analysis->glyphs[i].pwLogClust,
3616 cChar, j, direction, NULL, NULL);
3617 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);
3619 for (k = 0; k < clust_size; k++)
3621 piDx[next] = advance / clust_size;
3622 next++;
3623 if (k) j++;
3627 return S_OK;
3630 /***********************************************************************
3631 * ScriptStringValidate (USP10.@)
3633 * Validate a string analysis.
3635 * PARAMS
3636 * ssa [I] string analysis.
3638 * RETURNS
3639 * Success: S_OK
3640 * Failure: S_FALSE if invalid sequences are found
3641 * or a non-zero HRESULT if it fails.
3643 HRESULT WINAPI ScriptStringValidate(SCRIPT_STRING_ANALYSIS ssa)
3645 StringAnalysis *analysis = ssa;
3647 TRACE("(%p)\n", ssa);
3649 if (!analysis) return E_INVALIDARG;
3650 return (analysis->invalid) ? S_FALSE : S_OK;
3653 /***********************************************************************
3654 * ScriptString_pSize (USP10.@)
3656 * Retrieve width and height of an analysed string.
3658 * PARAMS
3659 * ssa [I] string analysis.
3661 * RETURNS
3662 * Success: Pointer to a SIZE structure.
3663 * Failure: NULL
3665 const SIZE * WINAPI ScriptString_pSize(SCRIPT_STRING_ANALYSIS ssa)
3667 int i, j;
3668 StringAnalysis *analysis = ssa;
3670 TRACE("(%p)\n", ssa);
3672 if (!analysis) return NULL;
3673 if (!(analysis->dwFlags & SSA_GLYPHS)) return NULL;
3675 if (!analysis->sz)
3677 if (!(analysis->sz = heap_alloc(sizeof(SIZE)))) return NULL;
3678 analysis->sz->cy = analysis->glyphs[0].sc->tm.tmHeight;
3680 analysis->sz->cx = 0;
3681 for (i = 0; i < analysis->numItems; i++)
3683 if (analysis->glyphs[i].sc->tm.tmHeight > analysis->sz->cy)
3684 analysis->sz->cy = analysis->glyphs[i].sc->tm.tmHeight;
3685 for (j = 0; j < analysis->glyphs[i].numGlyphs; j++)
3686 analysis->sz->cx += analysis->glyphs[i].piAdvance[j];
3689 return analysis->sz;
3692 /***********************************************************************
3693 * ScriptString_pLogAttr (USP10.@)
3695 * Retrieve logical attributes of an analysed string.
3697 * PARAMS
3698 * ssa [I] string analysis.
3700 * RETURNS
3701 * Success: Pointer to an array of SCRIPT_LOGATTR structures.
3702 * Failure: NULL
3704 const SCRIPT_LOGATTR * WINAPI ScriptString_pLogAttr(SCRIPT_STRING_ANALYSIS ssa)
3706 StringAnalysis *analysis = ssa;
3708 TRACE("(%p)\n", ssa);
3710 if (!analysis) return NULL;
3711 if (!(analysis->dwFlags & SSA_BREAK)) return NULL;
3712 return analysis->logattrs;
3715 /***********************************************************************
3716 * ScriptString_pcOutChars (USP10.@)
3718 * Retrieve the length of a string after clipping.
3720 * PARAMS
3721 * ssa [I] String analysis.
3723 * RETURNS
3724 * Success: Pointer to the length.
3725 * Failure: NULL
3727 const int * WINAPI ScriptString_pcOutChars(SCRIPT_STRING_ANALYSIS ssa)
3729 StringAnalysis *analysis = ssa;
3731 TRACE("(%p)\n", ssa);
3733 if (!analysis) return NULL;
3734 return &analysis->clip_len;
3737 /***********************************************************************
3738 * ScriptStringGetOrder (USP10.@)
3740 * Retrieve a glyph order map.
3742 * PARAMS
3743 * ssa [I] String analysis.
3744 * order [I/O] Array of glyph positions.
3746 * RETURNS
3747 * Success: S_OK
3748 * Failure: a non-zero HRESULT.
3750 HRESULT WINAPI ScriptStringGetOrder(SCRIPT_STRING_ANALYSIS ssa, UINT *order)
3752 int i, j;
3753 unsigned int k;
3754 StringAnalysis *analysis = ssa;
3756 TRACE("(%p)\n", ssa);
3758 if (!analysis) return S_FALSE;
3759 if (!(analysis->dwFlags & SSA_GLYPHS)) return S_FALSE;
3761 /* FIXME: handle RTL scripts */
3762 for (i = 0, k = 0; i < analysis->numItems; i++)
3763 for (j = 0; j < analysis->glyphs[i].numGlyphs; j++, k++)
3764 order[k] = k;
3766 return S_OK;
3769 /***********************************************************************
3770 * ScriptGetLogicalWidths (USP10.@)
3772 * Convert advance widths to logical widths.
3774 * PARAMS
3775 * sa [I] Script analysis.
3776 * nbchars [I] Number of characters.
3777 * nbglyphs [I] Number of glyphs.
3778 * glyph_width [I] Array of glyph widths.
3779 * log_clust [I] Array of logical clusters.
3780 * sva [I] Visual attributes.
3781 * widths [O] Array of logical widths.
3783 * RETURNS
3784 * Success: S_OK
3785 * Failure: a non-zero HRESULT.
3787 HRESULT WINAPI ScriptGetLogicalWidths(const SCRIPT_ANALYSIS *sa, int nbchars, int nbglyphs,
3788 const int *glyph_width, const WORD *log_clust,
3789 const SCRIPT_VISATTR *sva, int *widths)
3791 int i;
3793 TRACE("(%p, %d, %d, %p, %p, %p, %p)\n",
3794 sa, nbchars, nbglyphs, glyph_width, log_clust, sva, widths);
3796 /* FIXME */
3797 for (i = 0; i < nbchars; i++) widths[i] = glyph_width[i];
3798 return S_OK;
3801 /***********************************************************************
3802 * ScriptApplyLogicalWidth (USP10.@)
3804 * Generate glyph advance widths.
3806 * PARAMS
3807 * dx [I] Array of logical advance widths.
3808 * num_chars [I] Number of characters.
3809 * num_glyphs [I] Number of glyphs.
3810 * log_clust [I] Array of logical clusters.
3811 * sva [I] Visual attributes.
3812 * advance [I] Array of glyph advance widths.
3813 * sa [I] Script analysis.
3814 * abc [I/O] Summed ABC widths.
3815 * justify [O] Array of glyph advance widths.
3817 * RETURNS
3818 * Success: S_OK
3819 * Failure: a non-zero HRESULT.
3821 HRESULT WINAPI ScriptApplyLogicalWidth(const int *dx, int num_chars, int num_glyphs,
3822 const WORD *log_clust, const SCRIPT_VISATTR *sva,
3823 const int *advance, const SCRIPT_ANALYSIS *sa,
3824 ABC *abc, int *justify)
3826 int i;
3828 FIXME("(%p, %d, %d, %p, %p, %p, %p, %p, %p)\n",
3829 dx, num_chars, num_glyphs, log_clust, sva, advance, sa, abc, justify);
3831 for (i = 0; i < num_chars; i++) justify[i] = advance[i];
3832 return S_OK;
3835 HRESULT WINAPI ScriptJustify(const SCRIPT_VISATTR *sva, const int *advance,
3836 int num_glyphs, int dx, int min_kashida, int *justify)
3838 int i;
3840 FIXME("(%p, %p, %d, %d, %d, %p)\n", sva, advance, num_glyphs, dx, min_kashida, justify);
3842 for (i = 0; i < num_glyphs; i++) justify[i] = advance[i];
3843 return S_OK;
3846 HRESULT WINAPI ScriptGetFontScriptTags( HDC hdc, SCRIPT_CACHE *psc, SCRIPT_ANALYSIS *psa, int cMaxTags, OPENTYPE_TAG *pScriptTags, int *pcTags)
3848 HRESULT hr;
3849 if (!pScriptTags || !pcTags || cMaxTags == 0) return E_INVALIDARG;
3850 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
3852 return SHAPE_GetFontScriptTags(hdc, (ScriptCache *)*psc, psa, cMaxTags, pScriptTags, pcTags);
3855 HRESULT WINAPI ScriptGetFontLanguageTags( HDC hdc, SCRIPT_CACHE *psc, SCRIPT_ANALYSIS *psa, OPENTYPE_TAG tagScript, int cMaxTags, OPENTYPE_TAG *pLangSysTags, int *pcTags)
3857 HRESULT hr;
3858 if (!pLangSysTags || !pcTags || cMaxTags == 0) return E_INVALIDARG;
3859 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
3861 return SHAPE_GetFontLanguageTags(hdc, (ScriptCache *)*psc, psa, tagScript, cMaxTags, pLangSysTags, pcTags);
3864 HRESULT WINAPI ScriptGetFontFeatureTags( HDC hdc, SCRIPT_CACHE *psc, SCRIPT_ANALYSIS *psa, OPENTYPE_TAG tagScript, OPENTYPE_TAG tagLangSys, int cMaxTags, OPENTYPE_TAG *pFeatureTags, int *pcTags)
3866 HRESULT hr;
3867 if (!pFeatureTags || !pcTags || cMaxTags == 0) return E_INVALIDARG;
3868 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
3870 return SHAPE_GetFontFeatureTags(hdc, (ScriptCache *)*psc, psa, tagScript, tagLangSys, cMaxTags, pFeatureTags, pcTags);