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