usp10: Rewrite ScriptXtoCP.
[wine.git] / dlls / usp10 / usp10.c
blob8e7bb187259d126f95a9f714a71db64960fd0a47
1 /*
2 * Implementation of Uniscribe Script Processor (usp10.dll)
4 * Copyright 2005 Steven Edwards for CodeWeavers
5 * Copyright 2006 Hans Leidekker
6 * Copyright 2010 CodeWeavers, Aric Stewart
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 * Notes:
23 * Uniscribe allows for processing of complex scripts such as joining
24 * and filtering characters and bi-directional text with custom line breaks.
27 #include <stdarg.h>
28 #include <stdlib.h>
29 #include <math.h>
31 #include "windef.h"
32 #include "winbase.h"
33 #include "wingdi.h"
34 #include "winuser.h"
35 #include "winnls.h"
36 #include "winreg.h"
37 #include "usp10.h"
39 #include "usp10_internal.h"
41 #include "wine/debug.h"
42 #include "wine/unicode.h"
44 WINE_DEFAULT_DEBUG_CHANNEL(uniscribe);
46 typedef struct _scriptRange
48 WORD script;
49 DWORD rangeFirst;
50 DWORD rangeLast;
51 WORD numericScript;
52 WORD punctScript;
53 } scriptRange;
55 static const scriptRange scriptRanges[] = {
56 /* Basic Latin: U+0000–U+007A */
57 { Script_Latin, 0x00, 0x07a , Script_Numeric, Script_Punctuation},
58 /* Latin-1 Supplement: U+0080–U+00FF */
59 /* Latin Extended-A: U+0100–U+017F */
60 /* Latin Extended-B: U+0180–U+024F */
61 /* IPA Extensions: U+0250–U+02AF */
62 /* Spacing Modifier Letters:U+02B0–U+02FF */
63 { Script_Latin, 0x80, 0x2ff , Script_Numeric2, Script_Punctuation},
64 /* Combining Diacritical Marks : U+0300–U+036F */
65 { Script_Diacritical,0x300, 0x36f, 0, 0},
66 /* Greek: U+0370–U+03FF */
67 { Script_Greek, 0x370, 0x3ff, 0, 0},
68 /* Cyrillic: U+0400–U+04FF */
69 /* Cyrillic Supplement: U+0500–U+052F */
70 { Script_Cyrillic, 0x400, 0x52f, 0, 0},
71 /* Armenian: U+0530–U+058F */
72 { Script_Armenian, 0x530, 0x58f, 0, 0},
73 /* Hebrew: U+0590–U+05FF */
74 { Script_Hebrew, 0x590, 0x5ff, 0, 0},
75 /* Arabic: U+0600–U+06FF */
76 { Script_Arabic, 0x600, 0x6ef, Script_Arabic_Numeric, 0},
77 /* Defined by Windows */
78 { Script_Persian, 0x6f0, 0x6f9, 0, 0},
79 /* Continue Arabic: U+0600–U+06FF */
80 { Script_Arabic, 0x6fa, 0x6ff, 0, 0},
81 /* Syriac: U+0700–U+074F*/
82 { Script_Syriac, 0x700, 0x74f, 0, 0},
83 /* Arabic Supplement: U+0750–U+077F */
84 { Script_Arabic, 0x750, 0x77f, 0, 0},
85 /* Thaana: U+0780–U+07BF */
86 { Script_Thaana, 0x780, 0x7bf, 0, 0},
87 /* N’Ko: U+07C0–U+07FF */
88 { Script_NKo, 0x7c0, 0x7ff, 0, 0},
89 /* Devanagari: U+0900–U+097F */
90 { Script_Devanagari, 0x900, 0x97f, Script_Devanagari_Numeric, 0},
91 /* Bengali: U+0980–U+09FF */
92 { Script_Bengali, 0x980, 0x9ff, Script_Bengali_Numeric, 0},
93 /* Gurmukhi: U+0A00–U+0A7F*/
94 { Script_Gurmukhi, 0xa00, 0xa7f, Script_Gurmukhi_Numeric, 0},
95 /* Gujarati: U+0A80–U+0AFF*/
96 { Script_Gujarati, 0xa80, 0xaff, Script_Gujarati_Numeric, 0},
97 /* Oriya: U+0B00–U+0B7F */
98 { Script_Oriya, 0xb00, 0xb7f, Script_Oriya_Numeric, 0},
99 /* Tamil: U+0B80–U+0BFF */
100 { Script_Tamil, 0xb80, 0xbff, Script_Tamil_Numeric, 0},
101 /* Telugu: U+0C00–U+0C7F */
102 { Script_Telugu, 0xc00, 0xc7f, Script_Telugu_Numeric, 0},
103 /* Kannada: U+0C80–U+0CFF */
104 { Script_Kannada, 0xc80, 0xcff, Script_Kannada_Numeric, 0},
105 /* Malayalam: U+0D00–U+0D7F */
106 { Script_Malayalam, 0xd00, 0xd7f, Script_Malayalam_Numeric, 0},
107 /* Sinhala: U+0D80–U+0DFF */
108 { Script_Sinhala, 0xd80, 0xdff, 0, 0},
109 /* Thai: U+0E00–U+0E7F */
110 { Script_Thai, 0xe00, 0xe7f, Script_Thai_Numeric, 0},
111 /* Lao: U+0E80–U+0EFF */
112 { Script_Lao, 0xe80, 0xeff, Script_Lao_Numeric, 0},
113 /* Tibetan: U+0F00–U+0FFF */
114 { Script_Tibetan, 0xf00, 0xfff, 0, 0},
115 /* Myanmar: U+1000–U+109F */
116 { Script_Myanmar, 0x1000, 0x109f, Script_Myanmar_Numeric, 0},
117 /* Georgian: U+10A0–U+10FF */
118 { Script_Georgian, 0x10a0, 0x10ff, 0, 0},
119 /* Hangul Jamo: U+1100–U+11FF */
120 { Script_Hangul, 0x1100, 0x11ff, 0, 0},
121 /* Ethiopic: U+1200–U+137F */
122 /* Ethiopic Extensions: U+1380–U+139F */
123 { Script_Ethiopic, 0x1200, 0x139f, 0, 0},
124 /* Cherokee: U+13A0–U+13FF */
125 { Script_Cherokee, 0x13a0, 0x13ff, 0, 0},
126 /* Canadian Aboriginal Syllabics: U+1400–U+167F */
127 { Script_Canadian, 0x1400, 0x167f, 0, 0},
128 /* Ogham: U+1680–U+169F */
129 { Script_Ogham, 0x1680, 0x169f, 0, 0},
130 /* Runic: U+16A0–U+16F0 */
131 { Script_Runic, 0x16a0, 0x16f0, 0, 0},
132 /* Khmer: U+1780–U+17FF */
133 { Script_Khmer, 0x1780, 0x17ff, Script_Khmer_Numeric, 0},
134 /* Mongolian: U+1800–U+18AF */
135 { Script_Mongolian, 0x1800, 0x18af, Script_Mongolian_Numeric, 0},
136 /* Canadian Aboriginal Syllabics Extended: U+18B0–U+18FF */
137 { Script_Canadian, 0x18b0, 0x18ff, 0, 0},
138 /* Tai Le: U+1950–U+197F */
139 { Script_Tai_Le, 0x1950, 0x197f, 0, 0},
140 /* New Tai Lue: U+1980–U+19DF */
141 { Script_New_Tai_Lue,0x1980, 0x19df, Script_New_Tai_Lue_Numeric, 0},
142 /* Khmer Symbols: U+19E0–U+19FF */
143 { Script_Khmer, 0x19e0, 0x19ff, Script_Khmer_Numeric, 0},
144 /* Vedic Extensions: U+1CD0-U+1CFF */
145 { Script_Devanagari, 0x1cd0, 0x1cff, Script_Devanagari_Numeric, 0},
146 /* Phonetic Extensions: U+1D00–U+1DBF */
147 { Script_Latin, 0x1d00, 0x1dbf, 0, 0},
148 /* Combining Diacritical Marks Supplement: U+1DC0–U+1DFF */
149 { Script_Diacritical,0x1dc0, 0x1dff, 0, 0},
150 /* Latin Extended Additional: U+1E00–U+1EFF */
151 { Script_Latin, 0x1e00, 0x1eff, 0, 0},
152 /* Greek Extended: U+1F00–U+1FFF */
153 { Script_Greek, 0x1f00, 0x1fff, 0, 0},
154 /* General Punctuation: U+2000 –U+206f */
155 { Script_Latin, 0x2000, 0x206f, 0, 0},
156 /* Superscripts and Subscripts : U+2070 –U+209f */
157 /* Currency Symbols : U+20a0 –U+20cf */
158 { Script_Numeric2, 0x2070, 0x2070, 0, 0},
159 { Script_Latin, 0x2071, 0x2073, 0, 0},
160 { Script_Numeric2, 0x2074, 0x2079, 0, 0},
161 { Script_Latin, 0x207a, 0x207f, 0, 0},
162 { Script_Numeric2, 0x2080, 0x2089, 0, 0},
163 { Script_Latin, 0x208a, 0x20cf, 0, 0},
164 /* Letterlike Symbols : U+2100 –U+214f */
165 /* Number Forms : U+2150 –U+218f */
166 /* Arrows : U+2190 –U+21ff */
167 /* Mathematical Operators : U+2200 –U+22ff */
168 /* Miscellaneous Technical : U+2300 –U+23ff */
169 /* Control Pictures : U+2400 –U+243f */
170 /* Optical Character Recognition : U+2440 –U+245f */
171 /* Enclosed Alphanumerics : U+2460 –U+24ff */
172 /* Box Drawing : U+2500 –U+25ff */
173 /* Block Elements : U+2580 –U+259f */
174 /* Geometric Shapes : U+25a0 –U+25ff */
175 /* Miscellaneous Symbols : U+2600 –U+26ff */
176 /* Dingbats : U+2700 –U+27bf */
177 /* Miscellaneous Mathematical Symbols-A : U+27c0 –U+27ef */
178 /* Supplemental Arrows-A : U+27f0 –U+27ff */
179 { Script_Latin, 0x2100, 0x27ff, 0, 0},
180 /* Braille Patterns: U+2800–U+28FF */
181 { Script_Braille, 0x2800, 0x28ff, 0, 0},
182 /* Supplemental Arrows-B : U+2900 –U+297f */
183 /* Miscellaneous Mathematical Symbols-B : U+2980 –U+29ff */
184 /* Supplemental Mathematical Operators : U+2a00 –U+2aff */
185 /* Miscellaneous Symbols and Arrows : U+2b00 –U+2bff */
186 { Script_Latin, 0x2900, 0x2bff, 0, 0},
187 /* Latin Extended-C: U+2C60–U+2C7F */
188 { Script_Latin, 0x2c60, 0x2c7f, 0, 0},
189 /* Georgian: U+2D00–U+2D2F */
190 { Script_Georgian, 0x2d00, 0x2d2f, 0, 0},
191 /* Tifinagh: U+2D30–U+2D7F */
192 { Script_Tifinagh, 0x2d30, 0x2d7f, 0, 0},
193 /* Ethiopic Extensions: U+2D80–U+2DDF */
194 { Script_Ethiopic, 0x2d80, 0x2ddf, 0, 0},
195 /* Cyrillic Extended-A: U+2DE0–U+2DFF */
196 { Script_Cyrillic, 0x2de0, 0x2dff, 0, 0},
197 /* CJK Radicals Supplement: U+2E80–U+2EFF */
198 /* Kangxi Radicals: U+2F00–U+2FDF */
199 { Script_CJK_Han, 0x2e80, 0x2fdf, 0, 0},
200 /* Ideographic Description Characters: U+2FF0–U+2FFF */
201 { Script_Ideograph ,0x2ff0, 0x2fff, 0, 0},
202 /* CJK Symbols and Punctuation: U+3000–U+303F */
203 { Script_Ideograph ,0x3000, 0x3004, 0, 0},
204 { Script_CJK_Han ,0x3005, 0x3005, 0, 0},
205 { Script_Ideograph ,0x3006, 0x3006, 0, 0},
206 { Script_CJK_Han ,0x3007, 0x3007, 0, 0},
207 { Script_Ideograph ,0x3008, 0x3020, 0, 0},
208 { Script_CJK_Han ,0x3021, 0x3029, 0, 0},
209 { Script_Ideograph ,0x302a, 0x3030, 0, 0},
210 /* Kana Marks: */
211 { Script_Kana ,0x3031, 0x3035, 0, 0},
212 { Script_Ideograph ,0x3036, 0x3037, 0, 0},
213 { Script_CJK_Han ,0x3038, 0x303b, 0, 0},
214 { Script_Ideograph ,0x303c, 0x303f, 0, 0},
215 /* Hiragana: U+3040–U+309F */
216 /* Katakana: U+30A0–U+30FF */
217 { Script_Kana ,0x3040, 0x30ff, 0, 0},
218 /* Bopomofo: U+3100–U+312F */
219 { Script_Bopomofo ,0x3100, 0x312f, 0, 0},
220 /* Hangul Compatibility Jamo: U+3130–U+318F */
221 { Script_Hangul ,0x3130, 0x318f, 0, 0},
222 /* Kanbun: U+3190–U+319F */
223 { Script_Ideograph ,0x3190, 0x319f, 0, 0},
224 /* Bopomofo Extended: U+31A0–U+31BF */
225 { Script_Bopomofo ,0x31a0, 0x31bf, 0, 0},
226 /* CJK Strokes: U+31C0–U+31EF */
227 { Script_Ideograph ,0x31c0, 0x31ef, 0, 0},
228 /* Katakana Phonetic Extensions: U+31F0–U+31FF */
229 { Script_Kana ,0x31f0, 0x31ff, 0, 0},
230 /* Enclosed CJK Letters and Months: U+3200–U+32FF */
231 { Script_Hangul ,0x3200, 0x321f, 0, 0},
232 { Script_Ideograph ,0x3220, 0x325f, 0, 0},
233 { Script_Hangul ,0x3260, 0x327f, 0, 0},
234 { Script_Ideograph ,0x3280, 0x32ef, 0, 0},
235 { Script_Kana ,0x32d0, 0x31ff, 0, 0},
236 /* CJK Compatibility: U+3300–U+33FF*/
237 { Script_Kana ,0x3300, 0x3357, 0, 0},
238 { Script_Ideograph ,0x3358, 0x33ff, 0, 0},
239 /* CJK Unified Ideographs Extension A: U+3400–U+4DBF */
240 { Script_CJK_Han ,0x3400, 0x4dbf, 0, 0},
241 /* CJK Unified Ideographs: U+4E00–U+9FFF */
242 { Script_CJK_Han ,0x4e00, 0x9fff, 0, 0},
243 /* Yi: U+A000–U+A4CF */
244 { Script_Yi ,0xa000, 0xa4cf, 0, 0},
245 /* Vai: U+A500–U+A63F */
246 { Script_Vai ,0xa500, 0xa63f, Script_Vai_Numeric, 0},
247 /* Cyrillic Extended-B: U+A640–U+A69F */
248 { Script_Cyrillic, 0xa640, 0xa69f, 0, 0},
249 /* Modifier Tone Letters: U+A700–U+A71F */
250 /* Latin Extended-D: U+A720–U+A7FF */
251 { Script_Latin, 0xa700, 0xa7ff, 0, 0},
252 /* Phags-pa: U+A840–U+A87F */
253 { Script_Phags_pa, 0xa840, 0xa87f, 0, 0},
254 /* Devanagari Extended: U+A8E0-U+A8FF */
255 { Script_Devanagari, 0xa8e0, 0xa8ff, Script_Devanagari_Numeric, 0},
256 /* Myanmar Extended-A: U+AA60–U+AA7F */
257 { Script_Myanmar, 0xaa60, 0xaa7f, Script_Myanmar_Numeric, 0},
258 /* Hangul Jamo Extended-A: U+A960–U+A97F */
259 { Script_Hangul, 0xa960, 0xa97f, 0, 0},
260 /* Hangul Syllables: U+AC00–U+D7A3 */
261 { Script_Hangul, 0xac00, 0xd7a3, 0, 0},
262 /* Hangul Jamo Extended-B: U+D7B0–U+D7FF */
263 { Script_Hangul, 0xd7b0, 0xd7ff, 0, 0},
264 /* Surrogates Area: U+D800–U+DFFF */
265 { Script_Surrogates, 0xd800, 0xdbfe, 0, 0},
266 { Script_Private, 0xdbff, 0xdc00, 0, 0},
267 { Script_Surrogates, 0xdc01, 0xdfff, 0, 0},
268 /* Private Use Area: U+E000–U+F8FF */
269 { Script_Private, 0xe000, 0xf8ff, 0, 0},
270 /* CJK Compatibility Ideographs: U+F900–U+FAFF */
271 { Script_CJK_Han ,0xf900, 0xfaff, 0, 0},
272 /* Latin Ligatures: U+FB00–U+FB06 */
273 { Script_Latin, 0xfb00, 0xfb06, 0, 0},
274 /* Armenian ligatures U+FB13..U+FB17 */
275 { Script_Armenian, 0xfb13, 0xfb17, 0, 0},
276 /* Alphabetic Presentation Forms: U+FB1D–U+FB4F */
277 { Script_Hebrew, 0xfb1d, 0xfb4f, 0, 0},
278 /* Arabic Presentation Forms-A: U+FB50–U+FDFF*/
279 { Script_Arabic, 0xfb50, 0xfdff, 0, 0},
280 /* Vertical Forms: U+FE10–U+FE1F */
281 /* Combining Half Marks: U+FE20–U+FE2F */
282 /* CJK Compatibility Forms: U+FE30–U+FE4F */
283 /* Small Form Variants: U+FE50–U+FE6F */
284 { Script_Ideograph ,0xfe10, 0xfe6f, 0, 0},
285 /* Arabic Presentation Forms-B: U+FE70–U+FEFF*/
286 { Script_Arabic, 0xfe70, 0xfeff, 0, 0},
287 /* Halfwidth and Fullwidth Forms: U+FF00–FFEF */
288 { Script_Ideograph ,0xff00, 0xff64, Script_Numeric2, 0},
289 { Script_Kana ,0xff65, 0xff9f, 0, 0},
290 { Script_Hangul ,0xffa0, 0xffdf, 0, 0},
291 { Script_Ideograph ,0xffe0, 0xffef, 0, 0},
292 /* Plane - 1 */
293 /* Deseret: U+10400–U+1044F */
294 { Script_Deseret, 0x10400, 0x1044F, 0, 0},
295 /* Osmanya: U+10480–U+104AF */
296 { Script_Osmanya, 0x10480, 0x104AF, Script_Osmanya_Numeric, 0},
297 /* Mathematical Alphanumeric Symbols: U+1D400–U+1D7FF */
298 { Script_MathAlpha, 0x1D400, 0x1D7FF, 0, 0},
299 /* END */
300 { SCRIPT_UNDEFINED, 0, 0, 0}
303 /* the must be in order so that the index matches the Script value */
304 const scriptData scriptInformation[] = {
305 {{SCRIPT_UNDEFINED, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
306 {LANG_NEUTRAL, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
307 0x00000000,
308 {0}},
309 {{Script_Latin, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
310 {LANG_ENGLISH, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
311 MS_MAKE_TAG('l','a','t','n'),
312 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
313 {{Script_CR, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
314 {LANG_NEUTRAL, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
315 0x00000000,
316 {0}},
317 {{Script_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
318 {LANG_ENGLISH, 1, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
319 0x00000000,
320 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
321 {{Script_Control, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
322 {LANG_ENGLISH, 0, 1, 0, 0, ANSI_CHARSET, 1, 0, 0, 0, 0, 0, 1, 0, 0},
323 0x00000000,
324 {0}},
325 {{Script_Punctuation, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
326 {LANG_NEUTRAL, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
327 0x00000000,
328 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
329 {{Script_Arabic, 1, 1, 0, 0, 0, 0, { 1,0,0,0,0,0,0,0,0,0,0}},
330 {LANG_ARABIC, 0, 1, 0, 0, ARABIC_CHARSET, 0, 0, 0, 0, 0, 0, 1, 1, 0},
331 MS_MAKE_TAG('a','r','a','b'),
332 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
333 {{Script_Arabic_Numeric, 1, 1, 0, 0, 0, 0, { 1,0,0,0,0,0,0,0,0,0,0}},
334 {LANG_ARABIC, 1, 1, 0, 0, ARABIC_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
335 MS_MAKE_TAG('a','r','a','b'),
336 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
337 {{Script_Hebrew, 1, 1, 0, 0, 0, 0, { 1,0,0,0,0,0,0,0,0,0,0}},
338 {LANG_HEBREW, 0, 1, 0, 1, HEBREW_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
339 MS_MAKE_TAG('h','e','b','r'),
340 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
341 {{Script_Syriac, 1, 1, 0, 0, 0, 0, { 1,0,0,0,0,0,0,0,0,0,0}},
342 {LANG_SYRIAC, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 1, 0},
343 MS_MAKE_TAG('s','y','r','c'),
344 {'E','s','t','r','a','n','g','e','l','o',' ','E','d','e','s','s','a',0}},
345 {{Script_Persian, 1, 1, 0, 0, 0, 0, { 1,0,0,0,0,0,0,0,0,0,0}},
346 {LANG_PERSIAN, 1, 1, 0, 0, ARABIC_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
347 MS_MAKE_TAG('s','y','r','c'),
348 {'E','s','t','r','a','n','g','e','l','o',' ','E','d','e','s','s','a',0}},
349 {{Script_Thaana, 1, 1, 0, 0, 0, 0, { 1,0,0,0,0,0,0,0,0,0,0}},
350 {LANG_DIVEHI, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
351 MS_MAKE_TAG('t','h','a','a'),
352 {'M','V',' ','B','o','l','i',0}},
353 {{Script_Greek, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
354 {LANG_GREEK, 0, 0, 0, 0, GREEK_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
355 MS_MAKE_TAG('g','r','e','k'),
356 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
357 {{Script_Cyrillic, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
358 {LANG_RUSSIAN, 0, 0, 0, 0, RUSSIAN_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
359 MS_MAKE_TAG('c','y','r','l'),
360 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
361 {{Script_Armenian, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
362 {LANG_ARMENIAN, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
363 MS_MAKE_TAG('a','r','m','n'),
364 {'S','y','l','f','a','e','n',0}},
365 {{Script_Georgian, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
366 {LANG_GEORGIAN, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
367 MS_MAKE_TAG('g','e','o','r'),
368 {'S','y','l','f','a','e','n',0}},
369 {{Script_Sinhala, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
370 {LANG_SINHALESE, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
371 MS_MAKE_TAG('s','i','n','h'),
372 {'I','s','k','o','o','l','a',' ','P','o','t','a',0}},
373 {{Script_Tibetan, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
374 {LANG_TIBETAN, 0, 1, 1, 1, DEFAULT_CHARSET, 0, 0, 1, 0, 1, 0, 0, 0, 0},
375 MS_MAKE_TAG('t','i','b','t'),
376 {'M','i','c','r','o','s','o','f','t',' ','H','i','m','a','l','a','y','a',0}},
377 {{Script_Tibetan_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
378 {LANG_TIBETAN, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
379 MS_MAKE_TAG('t','i','b','t'),
380 {'M','i','c','r','o','s','o','f','t',' ','H','i','m','a','l','a','y','a',0}},
381 {{Script_Phags_pa, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
382 {LANG_MONGOLIAN, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
383 MS_MAKE_TAG('p','h','a','g'),
384 {'M','i','c','r','o','s','o','f','t',' ','P','h','a','g','s','P','a',0}},
385 {{Script_Thai, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
386 {LANG_THAI, 0, 1, 1, 1, THAI_CHARSET, 0, 0, 1, 0, 1, 0, 0, 0, 1},
387 MS_MAKE_TAG('t','h','a','i'),
388 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
389 {{Script_Thai_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
390 {LANG_THAI, 1, 1, 0, 0, THAI_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
391 MS_MAKE_TAG('t','h','a','i'),
392 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
393 {{Script_Lao, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
394 {LANG_LAO, 0, 1, 1, 1, DEFAULT_CHARSET, 0, 0, 1, 0, 1, 0, 0, 0, 0},
395 MS_MAKE_TAG('l','a','o',' '),
396 {'D','o','k','C','h','a','m','p','a',0}},
397 {{Script_Lao_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
398 {LANG_LAO, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
399 MS_MAKE_TAG('l','a','o',' '),
400 {'D','o','k','C','h','a','m','p','a',0}},
401 {{Script_Devanagari, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
402 {LANG_HINDI, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
403 MS_MAKE_TAG('d','e','v','a'),
404 {'M','a','n','g','a','l',0}},
405 {{Script_Devanagari_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
406 {LANG_HINDI, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
407 MS_MAKE_TAG('d','e','v','a'),
408 {'M','a','n','g','a','l',0}},
409 {{Script_Bengali, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
410 {LANG_BENGALI, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
411 MS_MAKE_TAG('b','e','n','g'),
412 {'V','r','i','n','d','a',0}},
413 {{Script_Bengali_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
414 {LANG_BENGALI, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
415 MS_MAKE_TAG('b','e','n','g'),
416 {'V','r','i','n','d','a',0}},
417 {{Script_Bengali_Currency, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
418 {LANG_BENGALI, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
419 MS_MAKE_TAG('b','e','n','g'),
420 {'V','r','i','n','d','a',0}},
421 {{Script_Gurmukhi, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
422 {LANG_PUNJABI, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
423 MS_MAKE_TAG('g','u','r','u'),
424 {'R','a','a','v','i',0}},
425 {{Script_Gurmukhi_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
426 {LANG_PUNJABI, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
427 MS_MAKE_TAG('g','u','r','u'),
428 {'R','a','a','v','i',0}},
429 {{Script_Gujarati, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
430 {LANG_GUJARATI, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
431 MS_MAKE_TAG('g','u','j','r'),
432 {'S','h','r','u','t','i',0}},
433 {{Script_Gujarati_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
434 {LANG_GUJARATI, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
435 MS_MAKE_TAG('g','u','j','r'),
436 {'S','h','r','u','t','i',0}},
437 {{Script_Gujarati_Currency, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
438 {LANG_GUJARATI, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
439 MS_MAKE_TAG('g','u','j','r'),
440 {'S','h','r','u','t','i',0}},
441 {{Script_Oriya, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
442 {LANG_ORIYA, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
443 MS_MAKE_TAG('o','r','y','a'),
444 {'K','a','l','i','n','g','a',0}},
445 {{Script_Oriya_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
446 {LANG_ORIYA, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
447 MS_MAKE_TAG('o','r','y','a'),
448 {'K','a','l','i','n','g','a',0}},
449 {{Script_Tamil, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
450 {LANG_TAMIL, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
451 MS_MAKE_TAG('t','a','m','l'),
452 {'L','a','t','h','a',0}},
453 {{Script_Tamil_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
454 {LANG_TAMIL, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
455 MS_MAKE_TAG('t','a','m','l'),
456 {'L','a','t','h','a',0}},
457 {{Script_Telugu, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
458 {LANG_TELUGU, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
459 MS_MAKE_TAG('t','e','l','u'),
460 {'G','a','u','t','a','m','i',0}},
461 {{Script_Telugu_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
462 {LANG_TELUGU, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
463 MS_MAKE_TAG('t','e','l','u'),
464 {'G','a','u','t','a','m','i',0}},
465 {{Script_Kannada, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
466 {LANG_KANNADA, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
467 MS_MAKE_TAG('k','n','d','a'),
468 {'T','u','n','g','a',0}},
469 {{Script_Kannada_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
470 {LANG_KANNADA, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
471 MS_MAKE_TAG('k','n','d','a'),
472 {'T','u','n','g','a',0}},
473 {{Script_Malayalam, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
474 {LANG_MALAYALAM, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
475 MS_MAKE_TAG('m','l','y','m'),
476 {'K','a','r','t','i','k','a',0}},
477 {{Script_Malayalam_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
478 {LANG_MALAYALAM, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
479 MS_MAKE_TAG('m','l','y','m'),
480 {'K','a','r','t','i','k','a',0}},
481 {{Script_Diacritical, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
482 {LANG_ENGLISH, 0, 1, 0, 1, ANSI_CHARSET, 0, 0, 0, 0, 0, 1, 1, 0, 0},
483 0x00000000,
484 {0}},
485 {{Script_Punctuation2, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
486 {LANG_ENGLISH, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
487 MS_MAKE_TAG('l','a','t','n'),
488 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
489 {{Script_Numeric2, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
490 {LANG_ENGLISH, 1, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
491 0x00000000,
492 {0}},
493 {{Script_Myanmar, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
494 {0x55, 0, 1, 1, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
495 MS_MAKE_TAG('m','y','m','r'),
496 {'M','y','a','n','m','a','r',' ','T','e','x','t',0}},
497 {{Script_Myanmar_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
498 {0x55, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
499 MS_MAKE_TAG('m','y','m','r'),
500 {0}},
501 {{Script_Tai_Le, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
502 {0, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
503 MS_MAKE_TAG('t','a','l','e'),
504 {'M','i','c','r','o','s','o','f','t',' ','T','a','i',' ','L','e',0}},
505 {{Script_New_Tai_Lue, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
506 {0, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
507 MS_MAKE_TAG('t','a','l','u'),
508 {'M','i','c','r','o','s','o','f','t',' ','N','e','w',' ','T','a','i',' ','L','u','e',0}},
509 {{Script_New_Tai_Lue_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
510 {0, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
511 MS_MAKE_TAG('t','a','l','u'),
512 {'M','i','c','r','o','s','o','f','t',' ','N','e','w',' ','T','a','i',' ','L','u','e',0}},
513 {{Script_Khmer, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
514 {0x53, 0, 1, 1, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
515 MS_MAKE_TAG('k','h','m','r'),
516 {'D','a','u','n','P','e','n','h',0}},
517 {{Script_Khmer_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
518 {0x53, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
519 MS_MAKE_TAG('k','h','m','r'),
520 {'D','a','u','n','P','e','n','h',0}},
521 {{Script_CJK_Han, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
522 {LANG_ENGLISH, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
523 MS_MAKE_TAG('h','a','n','i'),
524 {0}},
525 {{Script_Ideograph, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
526 {LANG_ENGLISH, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
527 MS_MAKE_TAG('h','a','n','i'),
528 {0}},
529 {{Script_Bopomofo, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
530 {LANG_ENGLISH, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
531 MS_MAKE_TAG('b','o','p','o'),
532 {0}},
533 {{Script_Kana, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
534 {LANG_ENGLISH, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
535 MS_MAKE_TAG('k','a','n','a'),
536 {0}},
537 {{Script_Hangul, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
538 {LANG_KOREAN, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
539 MS_MAKE_TAG('h','a','n','g'),
540 {0}},
541 {{Script_Yi, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
542 {LANG_ENGLISH, 0, 0, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
543 MS_MAKE_TAG('y','i',' ',' '),
544 {'M','i','c','r','o','s','o','f','t',' ','Y','i',' ','B','a','i','t','i',0}},
545 {{Script_Ethiopic, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
546 {0x5e, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
547 MS_MAKE_TAG('e','t','h','i'),
548 {'N','y','a','l','a',0}},
549 {{Script_Ethiopic_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
550 {0x5e, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
551 MS_MAKE_TAG('e','t','h','i'),
552 {'N','y','a','l','a',0}},
553 {{Script_Mongolian, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
554 {LANG_MONGOLIAN, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
555 MS_MAKE_TAG('m','o','n','g'),
556 {'M','o','n','g','o','l','i','a','n',' ','B','a','i','t','i',0}},
557 {{Script_Mongolian_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
558 {LANG_MONGOLIAN, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
559 MS_MAKE_TAG('m','o','n','g'),
560 {'M','o','n','g','o','l','i','a','n',' ','B','a','i','t','i',0}},
561 {{Script_Tifinagh, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
562 {0, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
563 MS_MAKE_TAG('t','f','n','g'),
564 {'E','b','r','i','m','a',0}},
565 {{Script_NKo, 1, 1, 0, 0, 0, 0, { 1,0,0,0,0,0,0,0,0,0,0}},
566 {0, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
567 MS_MAKE_TAG('n','k','o',' '),
568 {'E','b','r','i','m','a',0}},
569 {{Script_Vai, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
570 {0, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
571 MS_MAKE_TAG('v','a','i',' '),
572 {'E','b','r','i','m','a',0}},
573 {{Script_Vai_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
574 {0, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
575 MS_MAKE_TAG('v','a','i',' '),
576 {'E','b','r','i','m','a',0}},
577 {{Script_Cherokee, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
578 {0x5c, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
579 MS_MAKE_TAG('c','h','e','r'),
580 {'P','l','a','n','t','a','g','e','n','e','t',' ','C','h','e','r','o','k','e','e',0}},
581 {{Script_Canadian, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
582 {0x5d, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
583 MS_MAKE_TAG('c','a','n','s'),
584 {'E','u','p','h','e','m','i','a',0}},
585 {{Script_Ogham, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
586 {0, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
587 MS_MAKE_TAG('o','g','a','m'),
588 {'S','e','g','o','e',' ','U','I',' ','S','y','m','b','o','l',0}},
589 {{Script_Runic, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
590 {0, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
591 MS_MAKE_TAG('r','u','n','r'),
592 {'S','e','g','o','e',' ','U','I',' ','S','y','m','b','o','l',0}},
593 {{Script_Braille, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
594 {LANG_ENGLISH, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
595 MS_MAKE_TAG('b','r','a','i'),
596 {'S','e','g','o','e',' ','U','I',' ','S','y','m','b','o','l',0}},
597 {{Script_Surrogates, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
598 {LANG_ENGLISH, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
599 0x00000000,
600 {0}},
601 {{Script_Private, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
602 {0, 0, 0, 0, 0, DEFAULT_CHARSET, 0, 1, 0, 0, 0, 0, 1, 0, 0},
603 0x00000000,
604 {0}},
605 {{Script_Deseret, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
606 {0, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
607 MS_MAKE_TAG('d','s','r','t'),
608 {'S','e','g','o','e',' ','U','I',' ','S','y','m','b','o','l',0}},
609 {{Script_Osmanya, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
610 {0, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
611 MS_MAKE_TAG('o','s','m','a'),
612 {'E','b','r','i','m','a',0}},
613 {{Script_Osmanya_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
614 {0, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
615 MS_MAKE_TAG('o','s','m','a'),
616 {'E','b','r','i','m','a',0}},
617 {{Script_MathAlpha, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
618 {0, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
619 MS_MAKE_TAG('m','a','t','h'),
620 {'C','a','m','b','r','i','a',' ','M','a','t','h',0}},
621 {{Script_Hebrew_Currency, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
622 {LANG_HEBREW, 0, 1, 0, 0, HEBREW_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
623 MS_MAKE_TAG('h','e','b','r'),
624 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
625 {{Script_Vietnamese_Currency, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
626 {LANG_VIETNAMESE, 0, 0, 0, 0, VIETNAMESE_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
627 MS_MAKE_TAG('l','a','t','n'),
628 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
629 {{Script_Thai_Currency, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
630 {LANG_THAI, 0, 1, 0, 0, THAI_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
631 MS_MAKE_TAG('t','h','a','i'),
632 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
635 static const SCRIPT_PROPERTIES *script_props[] =
637 &scriptInformation[0].props, &scriptInformation[1].props,
638 &scriptInformation[2].props, &scriptInformation[3].props,
639 &scriptInformation[4].props, &scriptInformation[5].props,
640 &scriptInformation[6].props, &scriptInformation[7].props,
641 &scriptInformation[8].props, &scriptInformation[9].props,
642 &scriptInformation[10].props, &scriptInformation[11].props,
643 &scriptInformation[12].props, &scriptInformation[13].props,
644 &scriptInformation[14].props, &scriptInformation[15].props,
645 &scriptInformation[16].props, &scriptInformation[17].props,
646 &scriptInformation[18].props, &scriptInformation[19].props,
647 &scriptInformation[20].props, &scriptInformation[21].props,
648 &scriptInformation[22].props, &scriptInformation[23].props,
649 &scriptInformation[24].props, &scriptInformation[25].props,
650 &scriptInformation[26].props, &scriptInformation[27].props,
651 &scriptInformation[28].props, &scriptInformation[29].props,
652 &scriptInformation[30].props, &scriptInformation[31].props,
653 &scriptInformation[32].props, &scriptInformation[33].props,
654 &scriptInformation[34].props, &scriptInformation[35].props,
655 &scriptInformation[36].props, &scriptInformation[37].props,
656 &scriptInformation[38].props, &scriptInformation[39].props,
657 &scriptInformation[40].props, &scriptInformation[41].props,
658 &scriptInformation[42].props, &scriptInformation[43].props,
659 &scriptInformation[44].props, &scriptInformation[45].props,
660 &scriptInformation[46].props, &scriptInformation[47].props,
661 &scriptInformation[48].props, &scriptInformation[49].props,
662 &scriptInformation[50].props, &scriptInformation[51].props,
663 &scriptInformation[52].props, &scriptInformation[53].props,
664 &scriptInformation[54].props, &scriptInformation[55].props,
665 &scriptInformation[56].props, &scriptInformation[57].props,
666 &scriptInformation[58].props, &scriptInformation[59].props,
667 &scriptInformation[60].props, &scriptInformation[61].props,
668 &scriptInformation[62].props, &scriptInformation[63].props,
669 &scriptInformation[64].props, &scriptInformation[65].props,
670 &scriptInformation[66].props, &scriptInformation[67].props,
671 &scriptInformation[68].props, &scriptInformation[69].props,
672 &scriptInformation[70].props, &scriptInformation[71].props,
673 &scriptInformation[72].props, &scriptInformation[73].props,
674 &scriptInformation[74].props, &scriptInformation[75].props,
675 &scriptInformation[76].props, &scriptInformation[77].props,
676 &scriptInformation[78].props, &scriptInformation[79].props,
677 &scriptInformation[80].props, &scriptInformation[81].props
680 typedef struct {
681 ScriptCache *sc;
682 int numGlyphs;
683 WORD* glyphs;
684 WORD* pwLogClust;
685 int* piAdvance;
686 SCRIPT_VISATTR* psva;
687 GOFFSET* pGoffset;
688 ABC* abc;
689 int iMaxPosX;
690 HFONT fallbackFont;
691 } StringGlyphs;
693 typedef struct {
694 HDC hdc;
695 DWORD dwFlags;
696 BOOL invalid;
697 int clip_len;
698 int cItems;
699 int cMaxGlyphs;
700 SCRIPT_ITEM* pItem;
701 int numItems;
702 StringGlyphs* glyphs;
703 SCRIPT_LOGATTR* logattrs;
704 SIZE* sz;
705 int* logical2visual;
706 } StringAnalysis;
708 typedef struct {
709 BOOL ascending;
710 WORD target;
711 } FindGlyph_struct;
713 static inline void *heap_alloc(SIZE_T size)
715 return HeapAlloc(GetProcessHeap(), 0, size);
718 static inline void *heap_alloc_zero(SIZE_T size)
720 return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);
723 static inline BOOL heap_free(LPVOID mem)
725 return HeapFree(GetProcessHeap(), 0, mem);
728 /* TODO Fix font properties on Arabic locale */
729 static inline BOOL set_cache_font_properties(const HDC hdc, ScriptCache *sc)
731 if (!sc->sfnt)
733 sc->sfp.wgBlank = sc->tm.tmBreakChar;
734 sc->sfp.wgDefault = sc->tm.tmDefaultChar;
735 sc->sfp.wgInvalid = sc->sfp.wgBlank;
736 sc->sfp.wgKashida = 0xFFFF;
737 sc->sfp.iKashidaWidth = 0;
739 else
741 static const WCHAR chars[4] = {0x0020, 0x200B, 0xF71B, 0x0640};
742 /* U+0020: numeric space
743 U+200B: zero width space
744 U+F71B: unknow char found by black box testing
745 U+0640: kashida */
746 WORD gi[4];
748 if (GetGlyphIndicesW(hdc, chars, 4, gi, GGI_MARK_NONEXISTING_GLYPHS) != GDI_ERROR)
750 if(gi[0] != 0xFFFF) /* 0xFFFF: index of default non exist char */
751 sc->sfp.wgBlank = gi[0];
752 else
753 sc->sfp.wgBlank = 0;
755 sc->sfp.wgDefault = 0;
757 if (gi[2] != 0xFFFF)
758 sc->sfp.wgInvalid = gi[2];
759 else if (gi[1] != 0xFFFF)
760 sc->sfp.wgInvalid = gi[1];
761 else if (gi[0] != 0xFFFF)
762 sc->sfp.wgInvalid = gi[0];
763 else
764 sc->sfp.wgInvalid = 0;
766 sc->sfp.wgKashida = gi[3];
768 sc->sfp.iKashidaWidth = 0; /* TODO */
770 else
771 return FALSE;
773 return TRUE;
776 static inline void get_cache_font_properties(SCRIPT_FONTPROPERTIES *sfp, ScriptCache *sc)
778 sfp->wgBlank = sc->sfp.wgBlank;
779 sfp->wgDefault = sc->sfp.wgDefault;
780 sfp->wgInvalid = sc->sfp.wgInvalid;
781 sfp->wgKashida = sc->sfp.wgKashida;
782 sfp->iKashidaWidth = sc->sfp.iKashidaWidth;
785 static inline LONG get_cache_height(SCRIPT_CACHE *psc)
787 return ((ScriptCache *)*psc)->tm.tmHeight;
790 static inline BYTE get_cache_pitch_family(SCRIPT_CACHE *psc)
792 return ((ScriptCache *)*psc)->tm.tmPitchAndFamily;
795 static inline WORD get_cache_glyph(SCRIPT_CACHE *psc, DWORD c)
797 CacheGlyphPage *page = ((ScriptCache *)*psc)->page[c / 0x10000];
798 WORD *block;
800 if (!page) return 0;
801 block = page->glyphs[(c % 0x10000) >> GLYPH_BLOCK_SHIFT];
802 if (!block) return 0;
803 return block[(c % 0x10000) & GLYPH_BLOCK_MASK];
806 static inline WORD set_cache_glyph(SCRIPT_CACHE *psc, WCHAR c, WORD glyph)
808 CacheGlyphPage **page = &((ScriptCache *)*psc)->page[c / 0x10000];
809 WORD **block;
810 if (!*page && !(*page = heap_alloc_zero(sizeof(CacheGlyphPage)))) return 0;
812 block = &(*page)->glyphs[(c % 0x10000) >> GLYPH_BLOCK_SHIFT];
813 if (!*block && !(*block = heap_alloc_zero(sizeof(WORD) * GLYPH_BLOCK_SIZE))) return 0;
814 return ((*block)[(c % 0x10000) & GLYPH_BLOCK_MASK] = glyph);
817 static inline BOOL get_cache_glyph_widths(SCRIPT_CACHE *psc, WORD glyph, ABC *abc)
819 static const ABC nil;
820 ABC *block = ((ScriptCache *)*psc)->widths[glyph >> GLYPH_BLOCK_SHIFT];
822 if (!block || !memcmp(&block[glyph & GLYPH_BLOCK_MASK], &nil, sizeof(ABC))) return FALSE;
823 memcpy(abc, &block[glyph & GLYPH_BLOCK_MASK], sizeof(ABC));
824 return TRUE;
827 static inline BOOL set_cache_glyph_widths(SCRIPT_CACHE *psc, WORD glyph, ABC *abc)
829 ABC **block = &((ScriptCache *)*psc)->widths[glyph >> GLYPH_BLOCK_SHIFT];
831 if (!*block && !(*block = heap_alloc_zero(sizeof(ABC) * GLYPH_BLOCK_SIZE))) return FALSE;
832 memcpy(&(*block)[glyph & GLYPH_BLOCK_MASK], abc, sizeof(ABC));
833 return TRUE;
836 static HRESULT init_script_cache(const HDC hdc, SCRIPT_CACHE *psc)
838 ScriptCache *sc;
839 int size;
841 if (!psc) return E_INVALIDARG;
842 if (*psc) return S_OK;
843 if (!hdc) return E_PENDING;
845 if (!(sc = heap_alloc_zero(sizeof(ScriptCache)))) return E_OUTOFMEMORY;
846 if (!GetTextMetricsW(hdc, &sc->tm))
848 heap_free(sc);
849 return E_INVALIDARG;
851 size = GetOutlineTextMetricsW(hdc, 0, NULL);
852 if (size)
854 sc->otm = heap_alloc(size);
855 sc->otm->otmSize = size;
856 GetOutlineTextMetricsW(hdc, size, sc->otm);
858 if (!GetObjectW(GetCurrentObject(hdc, OBJ_FONT), sizeof(LOGFONTW), &sc->lf))
860 heap_free(sc);
861 return E_INVALIDARG;
863 sc->sfnt = (GetFontData(hdc, MS_MAKE_TAG('h','e','a','d'), 0, NULL, 0)!=GDI_ERROR);
864 if (!set_cache_font_properties(hdc, sc))
866 heap_free(sc);
867 return E_INVALIDARG;
869 *psc = sc;
870 TRACE("<- %p\n", sc);
871 return S_OK;
874 static WCHAR mirror_char( WCHAR ch )
876 extern const WCHAR wine_mirror_map[];
877 return ch + wine_mirror_map[wine_mirror_map[ch >> 8] + (ch & 0xff)];
880 static inline DWORD decode_surrogate_pair(LPCWSTR str, INT index, INT end)
882 if (index < end-1 && IS_SURROGATE_PAIR(str[index],str[index+1]))
884 DWORD ch = 0x10000 + ((str[index] - 0xd800) << 10) + (str[index+1] - 0xdc00);
885 TRACE("Surrogate Pair %x %x => %x\n",str[index], str[index+1], ch);
886 return ch;
888 return 0;
891 static WORD get_char_script( LPCWSTR str, INT index, INT end, INT *consumed)
893 static const WCHAR latin_punc[] = {'#','$','&','\'',',',';','<','>','?','@','\\','^','_','`','{','|','}','~', 0x00a0, 0};
894 WORD type = 0;
895 DWORD ch;
896 int i;
898 *consumed = 1;
900 if (str[index] == 0xc || str[index] == 0x20 || str[index] == 0x202f)
901 return Script_CR;
903 /* These punctuation characters are separated out as Latin punctuation */
904 if (strchrW(latin_punc,str[index]))
905 return Script_Punctuation2;
907 /* These chars are itemized as Punctuation by Windows */
908 if (str[index] == 0x2212 || str[index] == 0x2044)
909 return Script_Punctuation;
911 /* Currency Symboles by Unicode point */
912 switch (str[index])
914 case 0x09f2:
915 case 0x09f3: return Script_Bengali_Currency;
916 case 0x0af1: return Script_Gujarati_Currency;
917 case 0x0e3f: return Script_Thai_Currency;
918 case 0x20aa: return Script_Hebrew_Currency;
919 case 0x20ab: return Script_Vietnamese_Currency;
920 case 0xfb29: return Script_Hebrew_Currency;
923 GetStringTypeW(CT_CTYPE1, &str[index], 1, &type);
925 if (type == 0)
926 return SCRIPT_UNDEFINED;
928 if (type & C1_CNTRL)
929 return Script_Control;
931 ch = decode_surrogate_pair(str, index, end);
932 if (ch)
933 *consumed = 2;
934 else
935 ch = str[index];
937 i = 0;
940 if (ch < scriptRanges[i].rangeFirst || scriptRanges[i].script == SCRIPT_UNDEFINED)
941 break;
943 if (ch >= scriptRanges[i].rangeFirst && ch <= scriptRanges[i].rangeLast)
945 if (scriptRanges[i].numericScript && type & C1_DIGIT)
946 return scriptRanges[i].numericScript;
947 if (scriptRanges[i].punctScript && type & C1_PUNCT)
948 return scriptRanges[i].punctScript;
949 return scriptRanges[i].script;
951 i++;
952 } while (1);
954 return SCRIPT_UNDEFINED;
957 static int compare_FindGlyph(const void *a, const void* b)
959 const FindGlyph_struct *find = (FindGlyph_struct*)a;
960 const WORD *idx= (WORD*)b;
961 int rc = 0;
963 if ( find->target > *idx)
964 rc = 1;
965 else if (find->target < *idx)
966 rc = -1;
968 if (!find->ascending)
969 rc *= -1;
970 return rc;
973 int USP10_FindGlyphInLogClust(const WORD* pwLogClust, int cChars, WORD target)
975 FindGlyph_struct fgs;
976 WORD *ptr;
977 INT k;
979 if (pwLogClust[0] < pwLogClust[cChars-1])
980 fgs.ascending = TRUE;
981 else
982 fgs.ascending = FALSE;
984 fgs.target = target;
985 ptr = bsearch(&fgs, pwLogClust, cChars, sizeof(WORD), compare_FindGlyph);
987 if (!ptr)
988 return -1;
990 for (k = (ptr - pwLogClust)-1; k >= 0 && pwLogClust[k] == target; k--)
992 k++;
994 return k;
997 /***********************************************************************
998 * ScriptFreeCache (USP10.@)
1000 * Free a script cache.
1002 * PARAMS
1003 * psc [I/O] Script cache.
1005 * RETURNS
1006 * Success: S_OK
1007 * Failure: Non-zero HRESULT value.
1009 HRESULT WINAPI ScriptFreeCache(SCRIPT_CACHE *psc)
1011 TRACE("%p\n", psc);
1013 if (psc && *psc)
1015 unsigned int i;
1016 INT n;
1017 for (i = 0; i < GLYPH_MAX / GLYPH_BLOCK_SIZE; i++)
1019 heap_free(((ScriptCache *)*psc)->widths[i]);
1021 for (i = 0; i < 0x10; i++)
1023 unsigned int j;
1024 if (((ScriptCache *)*psc)->page[i])
1025 for (j = 0; j < GLYPH_MAX / GLYPH_BLOCK_SIZE; j++)
1026 heap_free(((ScriptCache *)*psc)->page[i]->glyphs[j]);
1027 heap_free(((ScriptCache *)*psc)->page[i]);
1029 heap_free(((ScriptCache *)*psc)->GSUB_Table);
1030 heap_free(((ScriptCache *)*psc)->GDEF_Table);
1031 heap_free(((ScriptCache *)*psc)->CMAP_Table);
1032 heap_free(((ScriptCache *)*psc)->GPOS_Table);
1033 for (n = 0; n < ((ScriptCache *)*psc)->script_count; n++)
1035 int j;
1036 for (j = 0; j < ((ScriptCache *)*psc)->scripts[n].language_count; j++)
1038 int k;
1039 for (k = 0; k < ((ScriptCache *)*psc)->scripts[n].languages[j].feature_count; k++)
1040 heap_free(((ScriptCache *)*psc)->scripts[n].languages[j].features[k].lookups);
1041 heap_free(((ScriptCache *)*psc)->scripts[n].languages[j].features);
1043 for (j = 0; j < ((ScriptCache *)*psc)->scripts[n].default_language.feature_count; j++)
1044 heap_free(((ScriptCache *)*psc)->scripts[n].default_language.features[j].lookups);
1045 heap_free(((ScriptCache *)*psc)->scripts[n].default_language.features);
1046 heap_free(((ScriptCache *)*psc)->scripts[n].languages);
1048 heap_free(((ScriptCache *)*psc)->scripts);
1049 heap_free(((ScriptCache *)*psc)->otm);
1050 heap_free(*psc);
1051 *psc = NULL;
1053 return S_OK;
1056 /***********************************************************************
1057 * ScriptGetProperties (USP10.@)
1059 * Retrieve a list of script properties.
1061 * PARAMS
1062 * props [I] Pointer to an array of SCRIPT_PROPERTIES pointers.
1063 * num [I] Pointer to the number of scripts.
1065 * RETURNS
1066 * Success: S_OK
1067 * Failure: Non-zero HRESULT value.
1069 * NOTES
1070 * Behaviour matches WinXP.
1072 HRESULT WINAPI ScriptGetProperties(const SCRIPT_PROPERTIES ***props, int *num)
1074 TRACE("(%p,%p)\n", props, num);
1076 if (!props && !num) return E_INVALIDARG;
1078 if (num) *num = sizeof(script_props)/sizeof(script_props[0]);
1079 if (props) *props = script_props;
1081 return S_OK;
1084 /***********************************************************************
1085 * ScriptGetFontProperties (USP10.@)
1087 * Get information on special glyphs.
1089 * PARAMS
1090 * hdc [I] Device context.
1091 * psc [I/O] Opaque pointer to a script cache.
1092 * sfp [O] Font properties structure.
1094 HRESULT WINAPI ScriptGetFontProperties(HDC hdc, SCRIPT_CACHE *psc, SCRIPT_FONTPROPERTIES *sfp)
1096 HRESULT hr;
1098 TRACE("%p,%p,%p\n", hdc, psc, sfp);
1100 if (!sfp) return E_INVALIDARG;
1101 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
1103 if (sfp->cBytes != sizeof(SCRIPT_FONTPROPERTIES))
1104 return E_INVALIDARG;
1106 get_cache_font_properties(sfp, *psc);
1108 return S_OK;
1111 /***********************************************************************
1112 * ScriptRecordDigitSubstitution (USP10.@)
1114 * Record digit substitution settings for a given locale.
1116 * PARAMS
1117 * locale [I] Locale identifier.
1118 * sds [I] Structure to record substitution settings.
1120 * RETURNS
1121 * Success: S_OK
1122 * Failure: E_POINTER if sds is NULL, E_INVALIDARG otherwise.
1124 * SEE ALSO
1125 * http://blogs.msdn.com/michkap/archive/2006/02/22/536877.aspx
1127 HRESULT WINAPI ScriptRecordDigitSubstitution(LCID locale, SCRIPT_DIGITSUBSTITUTE *sds)
1129 DWORD plgid, sub;
1131 TRACE("0x%x, %p\n", locale, sds);
1133 /* This implementation appears to be correct for all languages, but it's
1134 * not clear if sds->DigitSubstitute is ever set to anything except
1135 * CONTEXT or NONE in reality */
1137 if (!sds) return E_POINTER;
1139 locale = ConvertDefaultLocale(locale);
1141 if (!IsValidLocale(locale, LCID_INSTALLED))
1142 return E_INVALIDARG;
1144 plgid = PRIMARYLANGID(LANGIDFROMLCID(locale));
1145 sds->TraditionalDigitLanguage = plgid;
1147 if (plgid == LANG_ARABIC || plgid == LANG_FARSI)
1148 sds->NationalDigitLanguage = plgid;
1149 else
1150 sds->NationalDigitLanguage = LANG_ENGLISH;
1152 if (!GetLocaleInfoW(locale, LOCALE_IDIGITSUBSTITUTION | LOCALE_RETURN_NUMBER,
1153 (LPWSTR)&sub, sizeof(sub)/sizeof(WCHAR))) return E_INVALIDARG;
1155 switch (sub)
1157 case 0:
1158 if (plgid == LANG_ARABIC || plgid == LANG_FARSI)
1159 sds->DigitSubstitute = SCRIPT_DIGITSUBSTITUTE_CONTEXT;
1160 else
1161 sds->DigitSubstitute = SCRIPT_DIGITSUBSTITUTE_NONE;
1162 break;
1163 case 1:
1164 sds->DigitSubstitute = SCRIPT_DIGITSUBSTITUTE_NONE;
1165 break;
1166 case 2:
1167 sds->DigitSubstitute = SCRIPT_DIGITSUBSTITUTE_NATIONAL;
1168 break;
1169 default:
1170 sds->DigitSubstitute = SCRIPT_DIGITSUBSTITUTE_TRADITIONAL;
1171 break;
1174 sds->dwReserved = 0;
1175 return S_OK;
1178 /***********************************************************************
1179 * ScriptApplyDigitSubstitution (USP10.@)
1181 * Apply digit substitution settings.
1183 * PARAMS
1184 * sds [I] Structure with recorded substitution settings.
1185 * sc [I] Script control structure.
1186 * ss [I] Script state structure.
1188 * RETURNS
1189 * Success: S_OK
1190 * Failure: E_INVALIDARG if sds is invalid. Otherwise an HRESULT.
1192 HRESULT WINAPI ScriptApplyDigitSubstitution(const SCRIPT_DIGITSUBSTITUTE *sds,
1193 SCRIPT_CONTROL *sc, SCRIPT_STATE *ss)
1195 SCRIPT_DIGITSUBSTITUTE psds;
1197 TRACE("%p, %p, %p\n", sds, sc, ss);
1199 if (!sc || !ss) return E_POINTER;
1200 if (!sds)
1202 sds = &psds;
1203 if (ScriptRecordDigitSubstitution(LOCALE_USER_DEFAULT, &psds) != S_OK)
1204 return E_INVALIDARG;
1207 sc->uDefaultLanguage = LANG_ENGLISH;
1208 sc->fContextDigits = 0;
1209 ss->fDigitSubstitute = 0;
1211 switch (sds->DigitSubstitute) {
1212 case SCRIPT_DIGITSUBSTITUTE_CONTEXT:
1213 case SCRIPT_DIGITSUBSTITUTE_NATIONAL:
1214 case SCRIPT_DIGITSUBSTITUTE_NONE:
1215 case SCRIPT_DIGITSUBSTITUTE_TRADITIONAL:
1216 return S_OK;
1217 default:
1218 return E_INVALIDARG;
1222 static inline BOOL is_indic(WORD script)
1224 return (script >= Script_Devanagari && script <= Script_Malayalam_Numeric);
1227 static inline WORD base_indic(WORD script)
1229 switch (script)
1231 case Script_Devanagari:
1232 case Script_Devanagari_Numeric: return Script_Devanagari;
1233 case Script_Bengali:
1234 case Script_Bengali_Numeric:
1235 case Script_Bengali_Currency: return Script_Bengali;
1236 case Script_Gurmukhi:
1237 case Script_Gurmukhi_Numeric: return Script_Gurmukhi;
1238 case Script_Gujarati:
1239 case Script_Gujarati_Numeric:
1240 case Script_Gujarati_Currency: return Script_Gujarati;
1241 case Script_Oriya:
1242 case Script_Oriya_Numeric: return Script_Oriya;
1243 case Script_Tamil:
1244 case Script_Tamil_Numeric: return Script_Tamil;
1245 case Script_Telugu:
1246 case Script_Telugu_Numeric: return Script_Telugu;
1247 case Script_Kannada:
1248 case Script_Kannada_Numeric: return Script_Kannada;
1249 case Script_Malayalam:
1250 case Script_Malayalam_Numeric: return Script_Malayalam;
1251 default:
1252 return -1;
1257 static HRESULT _ItemizeInternal(const WCHAR *pwcInChars, int cInChars,
1258 int cMaxItems, const SCRIPT_CONTROL *psControl,
1259 const SCRIPT_STATE *psState, SCRIPT_ITEM *pItems,
1260 OPENTYPE_TAG *pScriptTags, int *pcItems)
1263 #define Numeric_space 0x0020
1264 #define ZWNJ 0x200C
1265 #define ZWJ 0x200D
1267 int cnt = 0, index = 0, str = 0;
1268 int New_Script = -1;
1269 int i;
1270 WORD *levels = NULL;
1271 WORD *strength = NULL;
1272 WORD *scripts = NULL;
1273 WORD baselevel = 0;
1274 BOOL new_run;
1275 WORD last_indic = -1;
1276 WORD layoutRTL = 0;
1277 BOOL forceLevels = FALSE;
1278 INT consumed = 0;
1279 HRESULT res = E_OUTOFMEMORY;
1281 TRACE("%s,%d,%d,%p,%p,%p,%p\n", debugstr_wn(pwcInChars, cInChars), cInChars, cMaxItems,
1282 psControl, psState, pItems, pcItems);
1284 if (!pwcInChars || !cInChars || !pItems || cMaxItems < 2)
1285 return E_INVALIDARG;
1287 scripts = heap_alloc(cInChars * sizeof(WORD));
1288 if (!scripts)
1289 return E_OUTOFMEMORY;
1291 for (i = 0; i < cInChars; i++)
1293 if (consumed <= 0)
1295 scripts[i] = get_char_script(pwcInChars,i,cInChars,&consumed);
1296 consumed --;
1298 else
1300 scripts[i] = scripts[i-1];
1301 consumed --;
1303 /* Devanagari danda (U+0964) and double danda (U+0965) are used for
1304 all Indic scripts */
1305 if ((pwcInChars[i] == 0x964 || pwcInChars[i] ==0x965) && last_indic > 0)
1306 scripts[i] = last_indic;
1307 else if (is_indic(scripts[i]))
1308 last_indic = base_indic(scripts[i]);
1310 /* Some unicode points (Zero Width Space U+200B -
1311 Right-to-Left Mark U+200F) will force us into bidi mode */
1312 if (!forceLevels && pwcInChars[i] >= 0x200B && pwcInChars[i] <= 0x200F)
1313 forceLevels = TRUE;
1315 /* Diacritical marks merge with other scripts */
1316 if (scripts[i] == Script_Diacritical)
1318 if (i > 0)
1320 if (pScriptTags)
1321 scripts[i] = scripts[i-1];
1322 else
1324 int j;
1325 BOOL asian = FALSE;
1326 WORD first_script = scripts[i-1];
1327 for (j = i-1; j >= 0 && scripts[j] == first_script && pwcInChars[j] != Numeric_space; j--)
1329 WORD original = scripts[j];
1330 if (original == Script_Ideograph || original == Script_Kana || original == Script_Yi || original == Script_CJK_Han || original == Script_Bopomofo)
1332 asian = TRUE;
1333 break;
1335 if (original != Script_MathAlpha && scriptInformation[scripts[j]].props.fComplex)
1336 break;
1337 scripts[j] = scripts[i];
1338 if (original == Script_Punctuation2)
1339 break;
1341 if (j >= 0 && (scriptInformation[scripts[j]].props.fComplex || asian))
1342 scripts[i] = scripts[j];
1348 for (i = 0; i < cInChars; i++)
1350 /* Joiners get merged preferencially right */
1351 if (i > 0 && (pwcInChars[i] == ZWJ || pwcInChars[i] == ZWNJ))
1353 int j;
1354 if (i+1 == cInChars)
1355 scripts[i] = scripts[i-1];
1356 else
1358 for (j = i+1; j < cInChars; j++)
1360 if (pwcInChars[j] != ZWJ && pwcInChars[j] != ZWNJ && pwcInChars[j] != Numeric_space)
1362 scripts[i] = scripts[j];
1363 break;
1370 if (psState && psControl)
1372 levels = heap_alloc_zero(cInChars * sizeof(WORD));
1373 if (!levels)
1374 goto nomemory;
1376 BIDI_DetermineLevels(pwcInChars, cInChars, psState, psControl, levels);
1377 baselevel = levels[0];
1378 for (i = 0; i < cInChars; i++)
1379 if (levels[i]!=levels[0])
1380 break;
1381 if (i >= cInChars && !odd(baselevel) && !odd(psState->uBidiLevel) && !forceLevels)
1383 heap_free(levels);
1384 levels = NULL;
1386 else
1388 BOOL inNumber = FALSE;
1389 static const WCHAR math_punc[] = {'#','$','%','+',',','-','.','/',':',0x2212, 0x2044, 0x00a0,0};
1391 strength = heap_alloc_zero(cInChars * sizeof(WORD));
1392 if (!strength)
1393 goto nomemory;
1394 BIDI_GetStrengths(pwcInChars, cInChars, psControl, strength);
1396 /* We currently mis-level leading Diacriticals */
1397 if (scripts[0] == Script_Diacritical)
1398 for (i = 0; i < cInChars && scripts[0] == Script_Diacritical; i++)
1400 levels[i] = odd(levels[i])?levels[i]+1:levels[i];
1401 strength[i] = BIDI_STRONG;
1404 for (i = 0; i < cInChars; i++)
1406 /* Script_Numeric and select puncuation at level 0 get bumped to level 2 */
1407 if ((levels[i] == 0 || (odd(psState->uBidiLevel) && levels[i] == psState->uBidiLevel+1)) && inNumber && strchrW(math_punc,pwcInChars[i]))
1409 scripts[i] = Script_Numeric;
1410 levels[i] = 2;
1412 else if ((levels[i] == 0 || (odd(psState->uBidiLevel) && levels[i] == psState->uBidiLevel+1)) && scripts[i] == Script_Numeric)
1414 levels[i] = 2;
1415 inNumber = TRUE;
1417 else
1418 inNumber = FALSE;
1420 /* Joiners get merged preferencially right */
1421 if (i > 0 && (pwcInChars[i] == ZWJ || pwcInChars[i] == ZWNJ))
1423 int j;
1424 if (i+1 == cInChars && levels[i-1] == levels[i])
1425 strength[i] = strength[i-1];
1426 else
1427 for (j = i+1; j < cInChars && levels[i] == levels[j]; j++)
1428 if (pwcInChars[j] != ZWJ && pwcInChars[j] != ZWNJ && pwcInChars[j] != Numeric_space)
1430 strength[i] = strength[j];
1431 break;
1435 if (psControl->fMergeNeutralItems)
1437 /* Merge the neutrals */
1438 for (i = 0; i < cInChars; i++)
1440 if (strength[i] == BIDI_NEUTRAL || strength[i] == BIDI_WEAK)
1442 int j;
1443 for (j = i; j > 0; j--)
1445 if (levels[i] != levels[j])
1446 break;
1447 if ((strength[j] == BIDI_STRONG) || (strength[i] == BIDI_NEUTRAL && strength[j] == BIDI_WEAK))
1449 scripts[i] = scripts[j];
1450 strength[i] = strength[j];
1451 break;
1455 /* Try going the other way */
1456 if (strength[i] == BIDI_NEUTRAL || strength[i] == BIDI_WEAK)
1458 int j;
1459 for (j = i; j < cInChars; j++)
1461 if (levels[i] != levels[j])
1462 break;
1463 if ((strength[j] == BIDI_STRONG) || (strength[i] == BIDI_NEUTRAL && strength[j] == BIDI_WEAK))
1465 scripts[i] = scripts[j];
1466 strength[i] = strength[j];
1467 break;
1476 while ((!levels || (levels && cnt+1 < cInChars && levels[cnt+1] == levels[0]))
1477 && (cnt < cInChars && pwcInChars[cnt] == Numeric_space))
1478 cnt++;
1480 if (cnt == cInChars) /* All Spaces */
1482 cnt = 0;
1483 New_Script = scripts[cnt];
1486 pItems[index].iCharPos = 0;
1487 pItems[index].a = scriptInformation[scripts[cnt]].a;
1488 if (pScriptTags)
1489 pScriptTags[index] = scriptInformation[scripts[cnt]].scriptTag;
1491 if (strength && strength[cnt] == BIDI_STRONG)
1492 str = strength[cnt];
1493 else if (strength)
1494 str = strength[0];
1496 cnt = 0;
1498 if (levels)
1500 if (strength[cnt] == BIDI_STRONG)
1501 layoutRTL = (odd(levels[cnt]))?1:0;
1502 else
1503 layoutRTL = (psState->uBidiLevel || odd(levels[cnt]))?1:0;
1504 pItems[index].a.fRTL = odd(levels[cnt]);
1505 pItems[index].a.fLayoutRTL = layoutRTL;
1506 pItems[index].a.s.uBidiLevel = levels[cnt];
1508 else if (!pItems[index].a.s.uBidiLevel)
1510 layoutRTL = (odd(baselevel))?1:0;
1511 pItems[index].a.s.uBidiLevel = baselevel;
1512 pItems[index].a.fLayoutRTL = odd(baselevel);
1513 pItems[index].a.fRTL = odd(baselevel);
1516 TRACE("New_Level=%i New_Strength=%i New_Script=%d, eScript=%d index=%d cnt=%d iCharPos=%d\n",
1517 levels?levels[cnt]:-1, str, New_Script, pItems[index].a.eScript, index, cnt,
1518 pItems[index].iCharPos);
1520 for (cnt=1; cnt < cInChars; cnt++)
1522 if(pwcInChars[cnt] != Numeric_space)
1523 New_Script = scripts[cnt];
1524 else if (levels)
1526 int j = 1;
1527 while (cnt + j < cInChars - 1 && pwcInChars[cnt+j] == Numeric_space && levels[cnt] == levels[cnt+j])
1528 j++;
1529 if (cnt + j < cInChars && levels[cnt] == levels[cnt+j])
1530 New_Script = scripts[cnt+j];
1531 else
1532 New_Script = scripts[cnt];
1535 new_run = FALSE;
1536 /* merge space strengths*/
1537 if (strength && strength[cnt] == BIDI_STRONG && str != BIDI_STRONG && New_Script == pItems[index].a.eScript)
1538 str = BIDI_STRONG;
1540 if (strength && strength[cnt] == BIDI_NEUTRAL && str == BIDI_STRONG && pwcInChars[cnt] != Numeric_space && New_Script == pItems[index].a.eScript)
1541 str = BIDI_NEUTRAL;
1543 /* changes in level */
1544 if (levels && (levels[cnt] != pItems[index].a.s.uBidiLevel))
1546 TRACE("Level break(%i/%i)\n",pItems[index].a.s.uBidiLevel,levels[cnt]);
1547 new_run = TRUE;
1549 /* changes in strength */
1550 else if (strength && pwcInChars[cnt] != Numeric_space && str != strength[cnt])
1552 TRACE("Strength break (%i/%i)\n",str,strength[cnt]);
1553 new_run = TRUE;
1555 /* changes in script */
1556 else if (((pwcInChars[cnt] != Numeric_space) && (New_Script != -1) && (New_Script != pItems[index].a.eScript)) || (New_Script == Script_Control))
1558 TRACE("Script break(%i/%i)\n",pItems[index].a.eScript,New_Script);
1559 new_run = TRUE;
1562 if (!new_run && strength && str == BIDI_STRONG)
1564 layoutRTL = odd(levels[cnt])?1:0;
1565 pItems[index].a.fLayoutRTL = layoutRTL;
1568 if (new_run)
1570 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);
1572 index++;
1573 if (index+1 > cMaxItems)
1574 goto nomemory;
1576 if (strength)
1577 str = strength[cnt];
1579 pItems[index].iCharPos = cnt;
1580 memset(&pItems[index].a, 0, sizeof(SCRIPT_ANALYSIS));
1582 pItems[index].a = scriptInformation[New_Script].a;
1583 if (pScriptTags)
1584 pScriptTags[index] = scriptInformation[New_Script].scriptTag;
1585 if (levels)
1587 if (levels[cnt] == 0)
1588 layoutRTL = 0;
1589 else
1590 layoutRTL = (layoutRTL || odd(levels[cnt]))?1:0;
1591 pItems[index].a.fRTL = odd(levels[cnt]);
1592 pItems[index].a.fLayoutRTL = layoutRTL;
1593 pItems[index].a.s.uBidiLevel = levels[cnt];
1595 else if (!pItems[index].a.s.uBidiLevel)
1597 pItems[index].a.s.uBidiLevel = baselevel;
1598 pItems[index].a.fLayoutRTL = layoutRTL;
1599 pItems[index].a.fRTL = odd(baselevel);
1602 TRACE("index=%d cnt=%d iCharPos=%d\n", index, cnt, pItems[index].iCharPos);
1606 /* While not strictly necessary according to the spec, make sure the n+1
1607 * item is set up to prevent random behaviour if the caller erroneously
1608 * checks the n+1 structure */
1609 index++;
1610 if (index + 1 > cMaxItems) goto nomemory;
1611 memset(&pItems[index].a, 0, sizeof(SCRIPT_ANALYSIS));
1613 TRACE("index=%d cnt=%d iCharPos=%d\n", index, cnt, pItems[index].iCharPos);
1615 /* Set one SCRIPT_STATE item being returned */
1616 if (pcItems) *pcItems = index;
1618 /* Set SCRIPT_ITEM */
1619 pItems[index].iCharPos = cnt; /* the last item contains the ptr to the lastchar */
1620 res = S_OK;
1621 nomemory:
1622 heap_free(levels);
1623 heap_free(strength);
1624 heap_free(scripts);
1625 return res;
1628 /***********************************************************************
1629 * ScriptItemizeOpenType (USP10.@)
1631 * Split a Unicode string into shapeable parts.
1633 * PARAMS
1634 * pwcInChars [I] String to split.
1635 * cInChars [I] Number of characters in pwcInChars.
1636 * cMaxItems [I] Maximum number of items to return.
1637 * psControl [I] Pointer to a SCRIPT_CONTROL structure.
1638 * psState [I] Pointer to a SCRIPT_STATE structure.
1639 * pItems [O] Buffer to receive SCRIPT_ITEM structures.
1640 * pScriptTags [O] Buffer to receive OPENTYPE_TAGs.
1641 * pcItems [O] Number of script items returned.
1643 * RETURNS
1644 * Success: S_OK
1645 * Failure: Non-zero HRESULT value.
1647 HRESULT WINAPI ScriptItemizeOpenType(const WCHAR *pwcInChars, int cInChars, int cMaxItems,
1648 const SCRIPT_CONTROL *psControl, const SCRIPT_STATE *psState,
1649 SCRIPT_ITEM *pItems, OPENTYPE_TAG *pScriptTags, int *pcItems)
1651 return _ItemizeInternal(pwcInChars, cInChars, cMaxItems, psControl, psState, pItems, pScriptTags, pcItems);
1654 /***********************************************************************
1655 * ScriptItemize (USP10.@)
1657 * Split a Unicode string into shapeable parts.
1659 * PARAMS
1660 * pwcInChars [I] String to split.
1661 * cInChars [I] Number of characters in pwcInChars.
1662 * cMaxItems [I] Maximum number of items to return.
1663 * psControl [I] Pointer to a SCRIPT_CONTROL structure.
1664 * psState [I] Pointer to a SCRIPT_STATE structure.
1665 * pItems [O] Buffer to receive SCRIPT_ITEM structures.
1666 * pcItems [O] Number of script items returned.
1668 * RETURNS
1669 * Success: S_OK
1670 * Failure: Non-zero HRESULT value.
1672 HRESULT WINAPI ScriptItemize(const WCHAR *pwcInChars, int cInChars, int cMaxItems,
1673 const SCRIPT_CONTROL *psControl, const SCRIPT_STATE *psState,
1674 SCRIPT_ITEM *pItems, int *pcItems)
1676 return _ItemizeInternal(pwcInChars, cInChars, cMaxItems, psControl, psState, pItems, NULL, pcItems);
1679 static inline int getGivenTabWidth(ScriptCache *psc, SCRIPT_TABDEF *pTabdef, int charPos, int current_x)
1681 int defWidth;
1682 int cTabStops=0;
1683 INT *lpTabPos = NULL;
1684 INT nTabOrg = 0;
1685 INT x = 0;
1687 if (pTabdef)
1688 lpTabPos = pTabdef->pTabStops;
1690 if (pTabdef && pTabdef->iTabOrigin)
1692 if (pTabdef->iScale)
1693 nTabOrg = (pTabdef->iTabOrigin * pTabdef->iScale)/4;
1694 else
1695 nTabOrg = pTabdef->iTabOrigin * psc->tm.tmAveCharWidth;
1698 if (pTabdef)
1699 cTabStops = pTabdef->cTabStops;
1701 if (cTabStops == 1)
1703 if (pTabdef->iScale)
1704 defWidth = ((pTabdef->pTabStops[0])*pTabdef->iScale) / 4;
1705 else
1706 defWidth = (pTabdef->pTabStops[0])*psc->tm.tmAveCharWidth;
1707 cTabStops = 0;
1709 else
1710 defWidth = 8 * psc->tm.tmAveCharWidth;
1712 for (; cTabStops>0 ; lpTabPos++, cTabStops--)
1714 int position = *lpTabPos;
1715 if (position < 0)
1716 position = -1 * position;
1717 if (pTabdef->iScale)
1718 position = (position * pTabdef->iScale) / 4;
1719 else
1720 position = position * psc->tm.tmAveCharWidth;
1722 if( nTabOrg + position > current_x)
1724 if( *lpTabPos >= 0)
1726 /* a left aligned tab */
1727 x = (nTabOrg + *lpTabPos) - current_x;
1728 break;
1730 else
1732 FIXME("Negative tabstop\n");
1733 break;
1737 if ((!cTabStops) && (defWidth > 0))
1738 x =((((current_x - nTabOrg) / defWidth)+1) * defWidth) - current_x;
1739 else if ((!cTabStops) && (defWidth < 0))
1740 FIXME("TODO: Negative defWidth\n");
1742 return x;
1745 /***********************************************************************
1746 * Helper function for ScriptStringAnalyse
1748 static BOOL requires_fallback(HDC hdc, SCRIPT_CACHE *psc, SCRIPT_ANALYSIS *psa,
1749 const WCHAR *pwcInChars, int cChars )
1751 /* FIXME: When to properly fallback is still a bit of a mystery */
1752 WORD *glyphs;
1754 if (psa->fNoGlyphIndex)
1755 return FALSE;
1757 if (init_script_cache(hdc, psc) != S_OK)
1758 return FALSE;
1760 if (SHAPE_CheckFontForRequiredFeatures(hdc, (ScriptCache *)*psc, psa) != S_OK)
1761 return TRUE;
1763 glyphs = heap_alloc(sizeof(WORD) * cChars);
1764 if (!glyphs)
1765 return FALSE;
1766 if (ScriptGetCMap(hdc, psc, pwcInChars, cChars, 0, glyphs) != S_OK)
1768 heap_free(glyphs);
1769 return TRUE;
1771 heap_free(glyphs);
1773 return FALSE;
1776 static void find_fallback_font(DWORD scriptid, LPWSTR FaceName)
1778 HKEY hkey;
1780 if (!RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Uniscribe\\Fallback", &hkey))
1782 static const WCHAR szFmt[] = {'%','x',0};
1783 WCHAR value[10];
1784 DWORD count = LF_FACESIZE * sizeof(WCHAR);
1785 DWORD type;
1787 sprintfW(value, szFmt, scriptInformation[scriptid].scriptTag);
1788 if (RegQueryValueExW(hkey, value, 0, &type, (LPBYTE)FaceName, &count))
1789 lstrcpyW(FaceName,scriptInformation[scriptid].fallbackFont);
1790 RegCloseKey(hkey);
1792 else
1793 lstrcpyW(FaceName,scriptInformation[scriptid].fallbackFont);
1796 /***********************************************************************
1797 * ScriptStringAnalyse (USP10.@)
1800 HRESULT WINAPI ScriptStringAnalyse(HDC hdc, const void *pString, int cString,
1801 int cGlyphs, int iCharset, DWORD dwFlags,
1802 int iReqWidth, SCRIPT_CONTROL *psControl,
1803 SCRIPT_STATE *psState, const int *piDx,
1804 SCRIPT_TABDEF *pTabdef, const BYTE *pbInClass,
1805 SCRIPT_STRING_ANALYSIS *pssa)
1807 HRESULT hr = E_OUTOFMEMORY;
1808 StringAnalysis *analysis = NULL;
1809 SCRIPT_CONTROL sControl;
1810 SCRIPT_STATE sState;
1811 int i, num_items = 255;
1812 BYTE *BidiLevel;
1813 WCHAR *iString = NULL;
1815 TRACE("(%p,%p,%d,%d,%d,0x%x,%d,%p,%p,%p,%p,%p,%p)\n",
1816 hdc, pString, cString, cGlyphs, iCharset, dwFlags, iReqWidth,
1817 psControl, psState, piDx, pTabdef, pbInClass, pssa);
1819 if (iCharset != -1)
1821 FIXME("Only Unicode strings are supported\n");
1822 return E_INVALIDARG;
1824 if (cString < 1 || !pString) return E_INVALIDARG;
1825 if ((dwFlags & SSA_GLYPHS) && !hdc) return E_PENDING;
1827 if (!(analysis = heap_alloc_zero(sizeof(StringAnalysis)))) return E_OUTOFMEMORY;
1828 if (!(analysis->pItem = heap_alloc_zero(num_items * sizeof(SCRIPT_ITEM) + 1))) goto error;
1830 /* FIXME: handle clipping */
1831 analysis->clip_len = cString;
1832 analysis->hdc = hdc;
1833 analysis->dwFlags = dwFlags;
1835 if (psState)
1836 sState = *psState;
1837 else
1838 memset(&sState, 0, sizeof(SCRIPT_STATE));
1840 if (psControl)
1841 sControl = *psControl;
1842 else
1843 memset(&sControl, 0, sizeof(SCRIPT_CONTROL));
1845 if (dwFlags & SSA_PASSWORD)
1847 iString = heap_alloc(sizeof(WCHAR)*cString);
1848 if (!iString)
1850 hr = E_OUTOFMEMORY;
1851 goto error;
1853 for (i = 0; i < cString; i++)
1854 iString[i] = *((const WCHAR *)pString);
1855 pString = iString;
1858 hr = ScriptItemize(pString, cString, num_items, &sControl, &sState, analysis->pItem,
1859 &analysis->numItems);
1861 if (FAILED(hr))
1863 if (hr == E_OUTOFMEMORY)
1864 hr = E_INVALIDARG;
1865 goto error;
1868 /* set back to out of memory for default goto error behaviour */
1869 hr = E_OUTOFMEMORY;
1871 if (dwFlags & SSA_BREAK)
1873 if ((analysis->logattrs = heap_alloc(sizeof(SCRIPT_LOGATTR) * cString)))
1875 for (i = 0; i < analysis->numItems; i++)
1876 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]);
1878 else
1879 goto error;
1882 if (!(analysis->logical2visual = heap_alloc_zero(sizeof(int) * analysis->numItems)))
1883 goto error;
1884 if (!(BidiLevel = heap_alloc_zero(analysis->numItems)))
1885 goto error;
1887 if (dwFlags & SSA_GLYPHS)
1889 int tab_x = 0;
1890 if (!(analysis->glyphs = heap_alloc_zero(sizeof(StringGlyphs) * analysis->numItems)))
1892 heap_free(BidiLevel);
1893 goto error;
1896 for (i = 0; i < analysis->numItems; i++)
1898 SCRIPT_CACHE *sc = (SCRIPT_CACHE*)&analysis->glyphs[i].sc;
1899 int cChar = analysis->pItem[i+1].iCharPos - analysis->pItem[i].iCharPos;
1900 int numGlyphs = 1.5 * cChar + 16;
1901 WORD *glyphs = heap_alloc_zero(sizeof(WORD) * numGlyphs);
1902 WORD *pwLogClust = heap_alloc_zero(sizeof(WORD) * cChar);
1903 int *piAdvance = heap_alloc_zero(sizeof(int) * numGlyphs);
1904 SCRIPT_VISATTR *psva = heap_alloc_zero(sizeof(SCRIPT_VISATTR) * numGlyphs);
1905 GOFFSET *pGoffset = heap_alloc_zero(sizeof(GOFFSET) * numGlyphs);
1906 ABC *abc = heap_alloc_zero(sizeof(ABC));
1907 int numGlyphsReturned;
1908 HFONT originalFont = 0x0;
1910 /* FIXME: non unicode strings */
1911 const WCHAR* pStr = (const WCHAR*)pString;
1912 analysis->glyphs[i].fallbackFont = NULL;
1914 if (!glyphs || !pwLogClust || !piAdvance || !psva || !pGoffset || !abc)
1916 heap_free (BidiLevel);
1917 heap_free (glyphs);
1918 heap_free (pwLogClust);
1919 heap_free (piAdvance);
1920 heap_free (psva);
1921 heap_free (pGoffset);
1922 heap_free (abc);
1923 hr = E_OUTOFMEMORY;
1924 goto error;
1927 if ((dwFlags & SSA_FALLBACK) && requires_fallback(hdc, sc, &analysis->pItem[i].a, &pStr[analysis->pItem[i].iCharPos], cChar))
1929 LOGFONTW lf;
1930 GetObjectW(GetCurrentObject(hdc, OBJ_FONT), sizeof(lf), & lf);
1931 lf.lfCharSet = scriptInformation[analysis->pItem[i].a.eScript].props.bCharSet;
1932 lf.lfFaceName[0] = 0;
1933 find_fallback_font(analysis->pItem[i].a.eScript, lf.lfFaceName);
1934 if (lf.lfFaceName[0])
1936 analysis->glyphs[i].fallbackFont = CreateFontIndirectW(&lf);
1937 if (analysis->glyphs[i].fallbackFont)
1939 ScriptFreeCache(sc);
1940 originalFont = SelectObject(hdc, analysis->glyphs[i].fallbackFont);
1945 /* FIXME: When we properly shape Hangul remove this check */
1946 if ((dwFlags & SSA_LINK) && !analysis->glyphs[i].fallbackFont && analysis->pItem[i].a.eScript == Script_Hangul)
1947 analysis->pItem[i].a.fNoGlyphIndex = TRUE;
1949 if ((dwFlags & SSA_LINK) && !analysis->glyphs[i].fallbackFont && !scriptInformation[analysis->pItem[i].a.eScript].props.fComplex && !analysis->pItem[i].a.fRTL)
1950 analysis->pItem[i].a.fNoGlyphIndex = TRUE;
1952 ScriptShape(hdc, sc, &pStr[analysis->pItem[i].iCharPos], cChar, numGlyphs,
1953 &analysis->pItem[i].a, glyphs, pwLogClust, psva, &numGlyphsReturned);
1954 hr = ScriptPlace(hdc, sc, glyphs, numGlyphsReturned, psva, &analysis->pItem[i].a,
1955 piAdvance, pGoffset, abc);
1956 if (originalFont)
1957 SelectObject(hdc,originalFont);
1959 if (dwFlags & SSA_TAB)
1961 int tabi = 0;
1962 for (tabi = 0; tabi < cChar; tabi++)
1964 if (pStr[analysis->pItem[i].iCharPos+tabi] == 0x0009)
1965 piAdvance[tabi] = getGivenTabWidth(analysis->glyphs[i].sc, pTabdef, analysis->pItem[i].iCharPos+tabi, tab_x);
1966 tab_x+=piAdvance[tabi];
1970 analysis->glyphs[i].numGlyphs = numGlyphsReturned;
1971 analysis->glyphs[i].glyphs = glyphs;
1972 analysis->glyphs[i].pwLogClust = pwLogClust;
1973 analysis->glyphs[i].piAdvance = piAdvance;
1974 analysis->glyphs[i].psva = psva;
1975 analysis->glyphs[i].pGoffset = pGoffset;
1976 analysis->glyphs[i].abc = abc;
1977 analysis->glyphs[i].iMaxPosX= -1;
1979 BidiLevel[i] = analysis->pItem[i].a.s.uBidiLevel;
1982 else
1984 for (i = 0; i < analysis->numItems; i++)
1985 BidiLevel[i] = analysis->pItem[i].a.s.uBidiLevel;
1988 ScriptLayout(analysis->numItems, BidiLevel, NULL, analysis->logical2visual);
1989 heap_free(BidiLevel);
1991 *pssa = analysis;
1992 heap_free(iString);
1993 return S_OK;
1995 error:
1996 heap_free(iString);
1997 heap_free(analysis->glyphs);
1998 heap_free(analysis->logattrs);
1999 heap_free(analysis->pItem);
2000 heap_free(analysis->logical2visual);
2001 heap_free(analysis);
2002 return hr;
2005 static inline BOOL does_glyph_start_cluster(const SCRIPT_VISATTR *pva, const WORD *pwLogClust, int cChars, int glyph, int direction)
2007 if (pva[glyph].fClusterStart)
2008 return TRUE;
2009 if (USP10_FindGlyphInLogClust(pwLogClust, cChars, glyph) >= 0)
2010 return TRUE;
2012 return FALSE;
2016 static HRESULT SS_ItemOut( SCRIPT_STRING_ANALYSIS ssa,
2017 int iX,
2018 int iY,
2019 int iItem,
2020 int cStart,
2021 int cEnd,
2022 UINT uOptions,
2023 const RECT *prc,
2024 BOOL fSelected,
2025 BOOL fDisabled)
2027 StringAnalysis *analysis;
2028 int off_x = 0;
2029 HRESULT hr;
2030 COLORREF BkColor = 0x0;
2031 COLORREF TextColor = 0x0;
2032 INT BkMode = 0;
2033 INT runStart, runEnd;
2034 INT iGlyph, cGlyphs;
2035 HFONT oldFont = 0x0;
2036 RECT crc;
2037 int i;
2039 TRACE("(%p,%d,%d,%d,%d,%d, 0x%1x, %d, %d)\n",
2040 ssa, iX, iY, iItem, cStart, cEnd, uOptions, fSelected, fDisabled);
2042 if (!(analysis = ssa)) return E_INVALIDARG;
2044 if ((cStart >= 0 && analysis->pItem[iItem+1].iCharPos <= cStart) ||
2045 (cEnd >= 0 && analysis->pItem[iItem].iCharPos >= cEnd))
2046 return S_OK;
2048 CopyRect(&crc,prc);
2049 if (fSelected)
2051 BkMode = GetBkMode(analysis->hdc);
2052 SetBkMode( analysis->hdc, OPAQUE);
2053 BkColor = GetBkColor(analysis->hdc);
2054 SetBkColor(analysis->hdc, GetSysColor(COLOR_HIGHLIGHT));
2055 if (!fDisabled)
2057 TextColor = GetTextColor(analysis->hdc);
2058 SetTextColor(analysis->hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
2061 if (analysis->glyphs[iItem].fallbackFont)
2062 oldFont = SelectObject(analysis->hdc, analysis->glyphs[iItem].fallbackFont);
2064 if (cStart >= 0 && analysis->pItem[iItem+1].iCharPos > cStart && analysis->pItem[iItem].iCharPos <= cStart)
2065 runStart = cStart - analysis->pItem[iItem].iCharPos;
2066 else
2067 runStart = 0;
2068 if (cEnd >= 0 && analysis->pItem[iItem+1].iCharPos > cEnd && analysis->pItem[iItem].iCharPos <= cEnd)
2069 runEnd = (cEnd-1) - analysis->pItem[iItem].iCharPos;
2070 else
2071 runEnd = (analysis->pItem[iItem+1].iCharPos - analysis->pItem[iItem].iCharPos) - 1;
2073 if (analysis->pItem[iItem].a.fRTL)
2075 if (cEnd >= 0 && cEnd < analysis->pItem[iItem+1].iCharPos)
2076 ScriptStringCPtoX(ssa, cEnd, FALSE, &off_x);
2077 else
2078 ScriptStringCPtoX(ssa, analysis->pItem[iItem+1].iCharPos-1, TRUE, &off_x);
2079 crc.left = iX + off_x;
2081 else
2083 if (cStart >=0 && runStart)
2084 ScriptStringCPtoX(ssa, cStart, FALSE, &off_x);
2085 else
2086 ScriptStringCPtoX(ssa, analysis->pItem[iItem].iCharPos, FALSE, &off_x);
2087 crc.left = iX + off_x;
2090 if (analysis->pItem[iItem].a.fRTL)
2091 iGlyph = analysis->glyphs[iItem].pwLogClust[runEnd];
2092 else
2093 iGlyph = analysis->glyphs[iItem].pwLogClust[runStart];
2095 if (analysis->pItem[iItem].a.fRTL)
2096 cGlyphs = analysis->glyphs[iItem].pwLogClust[runStart] - iGlyph;
2097 else
2098 cGlyphs = analysis->glyphs[iItem].pwLogClust[runEnd] - iGlyph;
2100 cGlyphs++;
2102 /* adjust for cluster glyphs when starting */
2103 if (analysis->pItem[iItem].a.fRTL)
2104 i = analysis->pItem[iItem+1].iCharPos - 1;
2105 else
2106 i = analysis->pItem[iItem].iCharPos;
2108 for (; i >=analysis->pItem[iItem].iCharPos && i < analysis->pItem[iItem+1].iCharPos; (analysis->pItem[iItem].a.fRTL)?i--:i++)
2110 if (analysis->glyphs[iItem].pwLogClust[i - analysis->pItem[iItem].iCharPos] == iGlyph)
2112 if (analysis->pItem[iItem].a.fRTL)
2113 ScriptStringCPtoX(ssa, i, TRUE, &off_x);
2114 else
2115 ScriptStringCPtoX(ssa, i, FALSE, &off_x);
2116 break;
2120 if (cEnd < 0 || scriptInformation[analysis->pItem[iItem].a.eScript].props.fNeedsCaretInfo)
2122 INT direction;
2123 INT clust_glyph;
2125 clust_glyph = iGlyph + cGlyphs;
2126 if (analysis->pItem[iItem].a.fRTL)
2127 direction = -1;
2128 else
2129 direction = 1;
2131 while(clust_glyph < analysis->glyphs[iItem].numGlyphs &&
2132 !does_glyph_start_cluster(analysis->glyphs[iItem].psva, analysis->glyphs[iItem].pwLogClust, (analysis->pItem[iItem+1].iCharPos - analysis->pItem[iItem].iCharPos), clust_glyph, direction))
2134 cGlyphs++;
2135 clust_glyph++;
2139 hr = ScriptTextOut(analysis->hdc,
2140 (SCRIPT_CACHE *)&analysis->glyphs[iItem].sc, iX + off_x,
2141 iY, uOptions, &crc, &analysis->pItem[iItem].a, NULL, 0,
2142 &analysis->glyphs[iItem].glyphs[iGlyph], cGlyphs,
2143 &analysis->glyphs[iItem].piAdvance[iGlyph], NULL,
2144 &analysis->glyphs[iItem].pGoffset[iGlyph]);
2146 TRACE("ScriptTextOut hr=%08x\n", hr);
2148 if (fSelected)
2150 SetBkColor(analysis->hdc, BkColor);
2151 SetBkMode( analysis->hdc, BkMode);
2152 if (!fDisabled)
2153 SetTextColor(analysis->hdc, TextColor);
2155 if (analysis->glyphs[iItem].fallbackFont)
2156 SelectObject(analysis->hdc, oldFont);
2158 return hr;
2161 /***********************************************************************
2162 * ScriptStringOut (USP10.@)
2164 * This function takes the output of ScriptStringAnalyse and joins the segments
2165 * of glyphs and passes the resulting string to ScriptTextOut. ScriptStringOut
2166 * only processes glyphs.
2168 * Parameters:
2169 * ssa [I] buffer to hold the analysed string components
2170 * iX [I] X axis displacement for output
2171 * iY [I] Y axis displacement for output
2172 * uOptions [I] flags controlling output processing
2173 * prc [I] rectangle coordinates
2174 * iMinSel [I] starting pos for substringing output string
2175 * iMaxSel [I] ending pos for substringing output string
2176 * fDisabled [I] controls text highlighting
2178 * RETURNS
2179 * Success: S_OK
2180 * Failure: is the value returned by ScriptTextOut
2182 HRESULT WINAPI ScriptStringOut(SCRIPT_STRING_ANALYSIS ssa,
2183 int iX,
2184 int iY,
2185 UINT uOptions,
2186 const RECT *prc,
2187 int iMinSel,
2188 int iMaxSel,
2189 BOOL fDisabled)
2191 StringAnalysis *analysis;
2192 int item;
2193 HRESULT hr;
2195 TRACE("(%p,%d,%d,0x%1x,%p,%d,%d,%d)\n",
2196 ssa, iX, iY, uOptions, prc, iMinSel, iMaxSel, fDisabled);
2198 if (!(analysis = ssa)) return E_INVALIDARG;
2199 if (!(analysis->dwFlags & SSA_GLYPHS)) return E_INVALIDARG;
2201 for (item = 0; item < analysis->numItems; item++)
2203 hr = SS_ItemOut( ssa, iX, iY, analysis->logical2visual[item], -1, -1, uOptions, prc, FALSE, fDisabled);
2204 if (FAILED(hr))
2205 return hr;
2208 if (iMinSel < iMaxSel && (iMinSel > 0 || iMaxSel > 0))
2210 if (iMaxSel > 0 && iMinSel < 0)
2211 iMinSel = 0;
2212 for (item = 0; item < analysis->numItems; item++)
2214 hr = SS_ItemOut( ssa, iX, iY, analysis->logical2visual[item], iMinSel, iMaxSel, uOptions, prc, TRUE, fDisabled);
2215 if (FAILED(hr))
2216 return hr;
2220 return S_OK;
2223 /***********************************************************************
2224 * ScriptStringCPtoX (USP10.@)
2227 HRESULT WINAPI ScriptStringCPtoX(SCRIPT_STRING_ANALYSIS ssa, int icp, BOOL fTrailing, int* pX)
2229 int item;
2230 int runningX = 0;
2231 StringAnalysis* analysis = ssa;
2233 TRACE("(%p), %d, %d, (%p)\n", ssa, icp, fTrailing, pX);
2235 if (!ssa || !pX) return S_FALSE;
2236 if (!(analysis->dwFlags & SSA_GLYPHS)) return S_FALSE;
2238 /* icp out of range */
2239 if(icp < 0)
2241 analysis->invalid = TRUE;
2242 return E_INVALIDARG;
2245 for(item=0; item<analysis->numItems; item++)
2247 int CP, i;
2248 int offset;
2250 i = analysis->logical2visual[item];
2251 CP = analysis->pItem[i+1].iCharPos - analysis->pItem[i].iCharPos;
2252 /* initialize max extents for uninitialized runs */
2253 if (analysis->glyphs[i].iMaxPosX == -1)
2255 if (analysis->pItem[i].a.fRTL)
2256 ScriptCPtoX(0, FALSE, CP, analysis->glyphs[i].numGlyphs, analysis->glyphs[i].pwLogClust,
2257 analysis->glyphs[i].psva, analysis->glyphs[i].piAdvance,
2258 &analysis->pItem[i].a, &analysis->glyphs[i].iMaxPosX);
2259 else
2260 ScriptCPtoX(CP, TRUE, CP, analysis->glyphs[i].numGlyphs, analysis->glyphs[i].pwLogClust,
2261 analysis->glyphs[i].psva, analysis->glyphs[i].piAdvance,
2262 &analysis->pItem[i].a, &analysis->glyphs[i].iMaxPosX);
2265 if (icp >= analysis->pItem[i+1].iCharPos || icp < analysis->pItem[i].iCharPos)
2267 runningX += analysis->glyphs[i].iMaxPosX;
2268 continue;
2271 icp -= analysis->pItem[i].iCharPos;
2272 ScriptCPtoX(icp, fTrailing, CP, analysis->glyphs[i].numGlyphs, analysis->glyphs[i].pwLogClust,
2273 analysis->glyphs[i].psva, analysis->glyphs[i].piAdvance,
2274 &analysis->pItem[i].a, &offset);
2275 runningX += offset;
2277 *pX = runningX;
2278 return S_OK;
2281 /* icp out of range */
2282 analysis->invalid = TRUE;
2283 return E_INVALIDARG;
2286 /***********************************************************************
2287 * ScriptStringXtoCP (USP10.@)
2290 HRESULT WINAPI ScriptStringXtoCP(SCRIPT_STRING_ANALYSIS ssa, int iX, int* piCh, int* piTrailing)
2292 StringAnalysis* analysis = ssa;
2293 int item;
2295 TRACE("(%p), %d, (%p), (%p)\n", ssa, iX, piCh, piTrailing);
2297 if (!ssa || !piCh || !piTrailing) return S_FALSE;
2298 if (!(analysis->dwFlags & SSA_GLYPHS)) return S_FALSE;
2300 /* out of range */
2301 if(iX < 0)
2303 if (analysis->pItem[0].a.fRTL)
2305 *piCh = 1;
2306 *piTrailing = FALSE;
2308 else
2310 *piCh = -1;
2311 *piTrailing = TRUE;
2313 return S_OK;
2316 for(item=0; item<analysis->numItems; item++)
2318 int i;
2319 int CP;
2321 for (i = 0; i < analysis->numItems && analysis->logical2visual[i] != item; i++)
2322 /* nothing */;
2324 CP = analysis->pItem[i+1].iCharPos - analysis->pItem[i].iCharPos;
2325 /* initialize max extents for uninitialized runs */
2326 if (analysis->glyphs[i].iMaxPosX == -1)
2328 if (analysis->pItem[i].a.fRTL)
2329 ScriptCPtoX(0, FALSE, CP, analysis->glyphs[i].numGlyphs, analysis->glyphs[i].pwLogClust,
2330 analysis->glyphs[i].psva, analysis->glyphs[i].piAdvance,
2331 &analysis->pItem[i].a, &analysis->glyphs[i].iMaxPosX);
2332 else
2333 ScriptCPtoX(CP, TRUE, CP, analysis->glyphs[i].numGlyphs, analysis->glyphs[i].pwLogClust,
2334 analysis->glyphs[i].psva, analysis->glyphs[i].piAdvance,
2335 &analysis->pItem[i].a, &analysis->glyphs[i].iMaxPosX);
2338 if (iX > analysis->glyphs[i].iMaxPosX)
2340 iX -= analysis->glyphs[i].iMaxPosX;
2341 continue;
2344 ScriptXtoCP(iX, CP, analysis->glyphs[i].numGlyphs, analysis->glyphs[i].pwLogClust,
2345 analysis->glyphs[i].psva, analysis->glyphs[i].piAdvance,
2346 &analysis->pItem[i].a, piCh, piTrailing);
2347 *piCh += analysis->pItem[i].iCharPos;
2349 return S_OK;
2352 /* out of range */
2353 *piCh = analysis->pItem[analysis->numItems].iCharPos;
2354 *piTrailing = FALSE;
2356 return S_OK;
2360 /***********************************************************************
2361 * ScriptStringFree (USP10.@)
2363 * Free a string analysis.
2365 * PARAMS
2366 * pssa [I] string analysis.
2368 * RETURNS
2369 * Success: S_OK
2370 * Failure: Non-zero HRESULT value.
2372 HRESULT WINAPI ScriptStringFree(SCRIPT_STRING_ANALYSIS *pssa)
2374 StringAnalysis* analysis;
2375 BOOL invalid;
2376 int i;
2378 TRACE("(%p)\n", pssa);
2380 if (!pssa || !(analysis = *pssa)) return E_INVALIDARG;
2382 invalid = analysis->invalid;
2384 if (analysis->glyphs)
2386 for (i = 0; i < analysis->numItems; i++)
2388 heap_free(analysis->glyphs[i].glyphs);
2389 heap_free(analysis->glyphs[i].pwLogClust);
2390 heap_free(analysis->glyphs[i].piAdvance);
2391 heap_free(analysis->glyphs[i].psva);
2392 heap_free(analysis->glyphs[i].pGoffset);
2393 heap_free(analysis->glyphs[i].abc);
2394 if (analysis->glyphs[i].fallbackFont)
2395 DeleteObject(analysis->glyphs[i].fallbackFont);
2396 ScriptFreeCache((SCRIPT_CACHE *)&analysis->glyphs[i].sc);
2397 heap_free(analysis->glyphs[i].sc);
2399 heap_free(analysis->glyphs);
2402 heap_free(analysis->pItem);
2403 heap_free(analysis->logattrs);
2404 heap_free(analysis->sz);
2405 heap_free(analysis->logical2visual);
2406 heap_free(analysis);
2408 if (invalid) return E_INVALIDARG;
2409 return S_OK;
2412 static inline int get_cluster_size(const WORD *pwLogClust, int cChars, int item,
2413 int direction, int* iCluster, int *check_out)
2415 int clust_size = 1;
2416 int check;
2417 WORD clust = pwLogClust[item];
2419 for (check = item+direction; check < cChars && check >= 0; check+=direction)
2421 if (pwLogClust[check] == clust)
2423 clust_size ++;
2424 if (iCluster && *iCluster == -1)
2425 *iCluster = item;
2427 else break;
2430 if (check_out)
2431 *check_out = check;
2433 return clust_size;
2436 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)
2438 int advance;
2439 int log_clust_max;
2441 advance = piAdvance[glyph];
2443 if (pwLogClust[0] > pwLogClust[cChars-1])
2444 log_clust_max = pwLogClust[0];
2445 else
2446 log_clust_max = pwLogClust[cChars-1];
2448 if (glyph > log_clust_max)
2449 return advance;
2451 for (glyph+=direction; glyph < cGlyphs && glyph >= 0; glyph +=direction)
2454 if (does_glyph_start_cluster(pva, pwLogClust, cChars, glyph, direction))
2455 break;
2456 if (glyph > log_clust_max)
2457 break;
2458 advance += piAdvance[glyph];
2461 return advance;
2464 /***********************************************************************
2465 * ScriptCPtoX (USP10.@)
2468 HRESULT WINAPI ScriptCPtoX(int iCP,
2469 BOOL fTrailing,
2470 int cChars,
2471 int cGlyphs,
2472 const WORD *pwLogClust,
2473 const SCRIPT_VISATTR *psva,
2474 const int *piAdvance,
2475 const SCRIPT_ANALYSIS *psa,
2476 int *piX)
2478 int item;
2479 float iPosX;
2480 int iSpecial = -1;
2481 int iCluster = -1;
2482 int clust_size = 1;
2483 float special_size = 0.0;
2484 int iMaxPos = 0;
2485 int advance = 0;
2486 BOOL rtl = FALSE;
2488 TRACE("(%d,%d,%d,%d,%p,%p,%p,%p,%p)\n",
2489 iCP, fTrailing, cChars, cGlyphs, pwLogClust, psva, piAdvance,
2490 psa, piX);
2492 if (psa->fRTL && ! psa->fLogicalOrder)
2493 rtl = TRUE;
2495 if (fTrailing)
2496 iCP++;
2498 if (rtl)
2500 int max_clust = pwLogClust[0];
2502 for (item=0; item < cGlyphs; item++)
2503 if (pwLogClust[item] > max_clust)
2505 ERR("We do not handle non reversed clusters properly\n");
2506 break;
2509 iMaxPos = 0;
2510 for (item = max_clust; item >=0; item --)
2511 iMaxPos += piAdvance[item];
2514 iPosX = 0.0;
2515 for (item=0; item < iCP && item < cChars; item++)
2517 if (iSpecial == -1 && (iCluster == -1 || (iCluster != -1 && iCluster+clust_size <= item)))
2519 int check;
2520 int clust = pwLogClust[item];
2522 iCluster = -1;
2523 clust_size = get_cluster_size(pwLogClust, cChars, item, 1, &iCluster,
2524 &check);
2526 advance = get_glyph_cluster_advance(piAdvance, psva, pwLogClust, cGlyphs, cChars, clust, 1);
2528 if (check >= cChars && !iMaxPos)
2530 int glyph;
2531 for (glyph = clust; glyph < cGlyphs; glyph++)
2532 special_size += get_glyph_cluster_advance(piAdvance, psva, pwLogClust, cGlyphs, cChars, glyph, 1);
2533 iSpecial = item;
2534 special_size /= (cChars - item);
2535 iPosX += special_size;
2537 else
2539 if (scriptInformation[psa->eScript].props.fNeedsCaretInfo)
2541 clust_size --;
2542 if (clust_size == 0)
2543 iPosX += advance;
2545 else
2546 iPosX += advance / (float)clust_size;
2549 else if (iSpecial != -1)
2550 iPosX += special_size;
2551 else /* (iCluster != -1) */
2553 int adv = get_glyph_cluster_advance(piAdvance, psva, pwLogClust, cGlyphs, cChars, pwLogClust[iCluster], 1);
2554 if (scriptInformation[psa->eScript].props.fNeedsCaretInfo)
2556 clust_size --;
2557 if (clust_size == 0)
2558 iPosX += adv;
2560 else
2561 iPosX += adv / (float)clust_size;
2565 if (iMaxPos > 0)
2567 iPosX = iMaxPos - iPosX;
2568 if (iPosX < 0)
2569 iPosX = 0;
2572 *piX = iPosX;
2573 TRACE("*piX=%d\n", *piX);
2574 return S_OK;
2577 /* Count the number of characters in a cluster and its starting index*/
2578 static inline BOOL get_cluster_data(const WORD *pwLogClust, int cChars, int cluster_index, int *cluster_size, int *start_index)
2580 int size = 0;
2581 int i;
2583 for (i = 0; i < cChars; i++)
2585 if (pwLogClust[i] == cluster_index)
2587 if (!size && start_index)
2589 *start_index = i;
2590 if (!cluster_size)
2591 return TRUE;
2593 size++;
2595 else if (size) break;
2597 if (cluster_size)
2598 *cluster_size = size;
2600 return (size > 0);
2604 To handle multi-glyph clusters we need to find all the glyphs that are
2605 represented in the cluster. This involves finding the glyph whose
2606 index is the cluster index as well as whose glyph indices are greater than
2607 our cluster index but not part of a new cluster.
2609 Then we sum all those glyphs' advances.
2611 static inline int get_cluster_advance(const int* piAdvance,
2612 const SCRIPT_VISATTR *psva,
2613 const WORD *pwLogClust, int cGlyphs,
2614 int cChars, int cluster, int direction)
2616 int glyph_start;
2617 int glyph_end;
2618 int i, advance;
2620 if (direction > 0)
2621 i = 0;
2622 else
2623 i = (cChars - 1);
2625 for (glyph_start = -1, glyph_end = -1; i < cChars && i >= 0 && (glyph_start < 0 || glyph_end < 0); i+=direction)
2627 if (glyph_start < 0 && pwLogClust[i] != cluster) continue;
2628 if (pwLogClust[i] == cluster && glyph_start < 0) glyph_start = pwLogClust[i];
2629 if (glyph_start >= 0 && glyph_end < 0 && pwLogClust[i] != cluster) glyph_end = pwLogClust[i];
2631 if (glyph_end < 0)
2633 if (direction > 0)
2634 glyph_end = cGlyphs;
2635 else
2637 /* Don't fully understand multi-glyph reversed clusters yet,
2638 * do they occur for real or just in our test? */
2639 FIXME("multi-glyph reversed clusters found\n");
2640 glyph_end = glyph_start + 1;
2644 /* Check for fClusterStart, finding this generally would mean a malformed set of data */
2645 for (i = glyph_start+1; i< glyph_end; i++)
2647 if (psva[i].fClusterStart)
2649 glyph_end = i;
2650 break;
2654 for (advance = 0, i = glyph_start; i < glyph_end; i++)
2655 advance += piAdvance[i];
2657 return advance;
2661 /***********************************************************************
2662 * ScriptXtoCP (USP10.@)
2664 * Basic algorithm :
2665 * use piAdvance to find the cluster we are looking at
2666 * Find the character that is the first character of the cluster
2667 * That is our base piCP
2668 * If the script snaps to cluster boundries (Hebrew, Indic, Thai) then we
2669 * are good Otherwise if the cluster is larger than 1 glyph we need to
2670 * determine how far through the cluster to advance the cursor.
2672 HRESULT WINAPI ScriptXtoCP(int iX,
2673 int cChars,
2674 int cGlyphs,
2675 const WORD *pwLogClust,
2676 const SCRIPT_VISATTR *psva,
2677 const int *piAdvance,
2678 const SCRIPT_ANALYSIS *psa,
2679 int *piCP,
2680 int *piTrailing)
2682 int direction = 1;
2683 int iPosX;
2684 int i;
2685 int glyph_index, cluster_index;
2686 int cluster_size;
2688 TRACE("(%d,%d,%d,%p,%p,%p,%p,%p,%p)\n",
2689 iX, cChars, cGlyphs, pwLogClust, psva, piAdvance,
2690 psa, piCP, piTrailing);
2692 if (psa->fRTL && ! psa->fLogicalOrder)
2693 direction = -1;
2695 /* Handle an iX < 0 */
2696 if (iX < 0)
2698 if (direction < 0)
2700 *piCP = cChars;
2701 *piTrailing = 0;
2703 else
2705 *piCP = -1;
2706 *piTrailing = 1;
2708 return S_OK;
2711 /* Looking for non-reversed clusters in a reversed string */
2712 if (direction < 0)
2714 int max_clust = pwLogClust[0];
2715 for (i=0; i< cChars; i++)
2716 if (pwLogClust[i] > max_clust)
2718 FIXME("We do not handle non reversed clusters properly\n");
2719 break;
2723 /* find the glyph_index based in iX */
2724 if (direction > 0)
2726 for (glyph_index = -1, iPosX = iX; iPosX >=0 && glyph_index < cGlyphs; iPosX -= piAdvance[glyph_index+1], glyph_index++)
2729 else
2731 for (glyph_index = -1, iPosX = iX; iPosX > 0 && glyph_index < cGlyphs; iPosX -= piAdvance[glyph_index+1], glyph_index++)
2735 TRACE("iPosX %i -> glyph_index %i (%i)\n", iPosX, glyph_index, cGlyphs);
2737 *piTrailing = 0;
2738 if (glyph_index >= 0 && glyph_index < cGlyphs)
2740 /* find the cluster */
2741 if (direction > 0 )
2742 for (i = 0, cluster_index = pwLogClust[0]; i < cChars && pwLogClust[i] <= glyph_index; cluster_index=pwLogClust[i++])
2744 else
2745 for (i = 0, cluster_index = pwLogClust[0]; i < cChars && pwLogClust[i] >= glyph_index; cluster_index=pwLogClust[i++])
2748 TRACE("cluster_index %i\n", cluster_index);
2750 if (direction < 0 && iPosX >= 0 && glyph_index != cluster_index)
2752 /* We are off the end of the string */
2753 *piCP = -1;
2754 *piTrailing = 1;
2755 return S_OK;
2758 get_cluster_data(pwLogClust, cChars, cluster_index, &cluster_size, &i);
2760 TRACE("first char index %i\n",i);
2761 if (scriptInformation[psa->eScript].props.fNeedsCaretInfo)
2763 /* Check trailing */
2764 if (glyph_index != cluster_index ||
2765 (direction > 0 && abs(iPosX) <= (piAdvance[glyph_index] / 2)) ||
2766 (direction < 0 && abs(iPosX) >= (piAdvance[glyph_index] / 2)))
2767 *piTrailing = cluster_size;
2769 else
2771 if (cluster_size > 1)
2773 /* Be part way through the glyph cluster based on size and position */
2774 int cluster_advance = get_cluster_advance(piAdvance, psva, pwLogClust, cGlyphs, cChars, cluster_index, direction);
2775 double cluster_part_width = cluster_advance / (float)cluster_size;
2776 double adv;
2777 int part_index;
2779 /* back up to the beginning of the cluster */
2780 for (adv = iPosX, part_index = cluster_index; part_index <= glyph_index; part_index++)
2781 adv += piAdvance[part_index];
2782 if (adv > iX) adv = iX;
2784 TRACE("Multi-char cluster, no snap\n");
2785 TRACE("cluster size %i, pre-cluster iPosX %f\n",cluster_size, adv);
2786 TRACE("advance %i divides into %f per char\n", cluster_advance, cluster_part_width);
2787 if (direction > 0)
2789 for (part_index = 0; adv >= 0; adv-=cluster_part_width, part_index++)
2791 if (part_index) part_index--;
2793 else
2795 for (part_index = 0; adv > 0; adv-=cluster_part_width, part_index++)
2797 if (part_index > cluster_size)
2799 adv += cluster_part_width;
2800 part_index=cluster_size;
2804 TRACE("base_char %i part_index %i, leftover advance %f\n",i, part_index, adv);
2806 if (direction > 0)
2807 i += part_index;
2808 else
2809 i += (cluster_size - part_index);
2811 /* Check trailing */
2812 if ((direction > 0 && fabs(adv) <= (cluster_part_width / 2.0)) ||
2813 (direction < 0 && adv && fabs(adv) >= (cluster_part_width / 2.0)))
2814 *piTrailing = 1;
2816 else
2818 /* Check trailing */
2819 if ((direction > 0 && abs(iPosX) <= (piAdvance[glyph_index] / 2)) ||
2820 (direction < 0 && abs(iPosX) >= (piAdvance[glyph_index] / 2)))
2821 *piTrailing = 1;
2825 else
2827 TRACE("Point falls outside of string\n");
2828 if (glyph_index < 0)
2829 i = cChars-1;
2830 else /* (glyph_index >= cGlyphs) */
2831 i = cChars;
2833 /* If not snaping in the reverse direction (such as Hebrew) Then 0
2834 point flow to the next character */
2835 if (direction < 0)
2837 if (!scriptInformation[psa->eScript].props.fNeedsCaretInfo && abs(iPosX) == piAdvance[glyph_index])
2838 i++;
2839 else
2840 *piTrailing = 1;
2844 *piCP = i;
2846 TRACE("*piCP=%d\n", *piCP);
2847 TRACE("*piTrailing=%d\n", *piTrailing);
2848 return S_OK;
2851 /***********************************************************************
2852 * ScriptBreak (USP10.@)
2854 * Retrieve line break information.
2856 * PARAMS
2857 * chars [I] Array of characters.
2858 * sa [I] String analysis.
2859 * la [I] Array of logical attribute structures.
2861 * RETURNS
2862 * Success: S_OK
2863 * Failure: S_FALSE
2865 HRESULT WINAPI ScriptBreak(const WCHAR *chars, int count, const SCRIPT_ANALYSIS *sa, SCRIPT_LOGATTR *la)
2867 TRACE("(%s, %d, %p, %p)\n", debugstr_wn(chars, count), count, sa, la);
2869 if (count < 0 || !la) return E_INVALIDARG;
2870 if (count == 0) return E_FAIL;
2872 BREAK_line(chars, count, sa, la);
2874 return S_OK;
2877 /***********************************************************************
2878 * ScriptIsComplex (USP10.@)
2880 * Determine if a string is complex.
2882 * PARAMS
2883 * chars [I] Array of characters to test.
2884 * len [I] Length in characters.
2885 * flag [I] Flag.
2887 * RETURNS
2888 * Success: S_OK
2889 * Failure: S_FALSE
2892 HRESULT WINAPI ScriptIsComplex(const WCHAR *chars, int len, DWORD flag)
2894 int i;
2895 INT consumed = 0;
2897 TRACE("(%s,%d,0x%x)\n", debugstr_wn(chars, len), len, flag);
2899 for (i = 0; i < len; i+=consumed)
2901 int script;
2902 if (i >= len)
2903 break;
2905 if ((flag & SIC_ASCIIDIGIT) && chars[i] >= 0x30 && chars[i] <= 0x39)
2906 return S_OK;
2908 script = get_char_script(chars,i,len, &consumed);
2909 if ((scriptInformation[script].props.fComplex && (flag & SIC_COMPLEX))||
2910 (!scriptInformation[script].props.fComplex && (flag & SIC_NEUTRAL)))
2911 return S_OK;
2913 return S_FALSE;
2916 /***********************************************************************
2917 * ScriptShapeOpenType (USP10.@)
2919 * Produce glyphs and visual attributes for a run.
2921 * PARAMS
2922 * hdc [I] Device context.
2923 * psc [I/O] Opaque pointer to a script cache.
2924 * psa [I/O] Script analysis.
2925 * tagScript [I] The OpenType tag for the Script
2926 * tagLangSys [I] The OpenType tag for the Language
2927 * rcRangeChars[I] Array of Character counts in each range
2928 * rpRangeProperties [I] Array of TEXTRANGE_PROPERTIES structures
2929 * cRanges [I] Count of ranges
2930 * pwcChars [I] Array of characters specifying the run.
2931 * cChars [I] Number of characters in pwcChars.
2932 * cMaxGlyphs [I] Length of pwOutGlyphs.
2933 * pwLogClust [O] Array of logical cluster info.
2934 * pCharProps [O] Array of character property values
2935 * pwOutGlyphs [O] Array of glyphs.
2936 * pOutGlyphProps [O] Array of attributes for the retrieved glyphs
2937 * pcGlyphs [O] Number of glyphs returned.
2939 * RETURNS
2940 * Success: S_OK
2941 * Failure: Non-zero HRESULT value.
2943 HRESULT WINAPI ScriptShapeOpenType( HDC hdc, SCRIPT_CACHE *psc,
2944 SCRIPT_ANALYSIS *psa, OPENTYPE_TAG tagScript,
2945 OPENTYPE_TAG tagLangSys, int *rcRangeChars,
2946 TEXTRANGE_PROPERTIES **rpRangeProperties,
2947 int cRanges, const WCHAR *pwcChars, int cChars,
2948 int cMaxGlyphs, WORD *pwLogClust,
2949 SCRIPT_CHARPROP *pCharProps, WORD *pwOutGlyphs,
2950 SCRIPT_GLYPHPROP *pOutGlyphProps, int *pcGlyphs)
2952 HRESULT hr;
2953 int i;
2954 unsigned int g;
2955 BOOL rtl;
2956 int cluster;
2957 static int once = 0;
2959 TRACE("(%p, %p, %p, %s, %s, %p, %p, %d, %s, %d, %d, %p, %p, %p, %p, %p )\n",
2960 hdc, psc, psa,
2961 debugstr_an((char*)&tagScript,4), debugstr_an((char*)&tagLangSys,4),
2962 rcRangeChars, rpRangeProperties, cRanges, debugstr_wn(pwcChars, cChars),
2963 cChars, cMaxGlyphs, pwLogClust, pCharProps, pwOutGlyphs, pOutGlyphProps, pcGlyphs);
2965 if (psa) TRACE("psa values: %d, %d, %d, %d, %d, %d, %d\n", psa->eScript, psa->fRTL, psa->fLayoutRTL,
2966 psa->fLinkBefore, psa->fLinkAfter, psa->fLogicalOrder, psa->fNoGlyphIndex);
2968 if (!pOutGlyphProps || !pcGlyphs || !pCharProps) return E_INVALIDARG;
2969 if (cChars > cMaxGlyphs) return E_OUTOFMEMORY;
2971 if (cRanges)
2972 if(!once++) FIXME("Ranges not supported yet\n");
2974 rtl = (psa && !psa->fLogicalOrder && psa->fRTL);
2976 *pcGlyphs = cChars;
2977 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
2978 if (!pwLogClust) return E_FAIL;
2980 ((ScriptCache *)*psc)->userScript = tagScript;
2981 ((ScriptCache *)*psc)->userLang = tagLangSys;
2983 /* set fNoGlyphIndex non truetype/opentype fonts */
2984 if (psa && !psa->fNoGlyphIndex && !((ScriptCache *)*psc)->sfnt)
2985 psa->fNoGlyphIndex = TRUE;
2987 /* Initialize a SCRIPT_VISATTR and LogClust for each char in this run */
2988 for (i = 0; i < cChars; i++)
2990 int idx = i;
2991 if (rtl) idx = cChars - 1 - i;
2992 /* FIXME: set to better values */
2993 pOutGlyphProps[i].sva.uJustification = (pwcChars[idx] == ' ') ? SCRIPT_JUSTIFY_BLANK : SCRIPT_JUSTIFY_CHARACTER;
2994 pOutGlyphProps[i].sva.fClusterStart = 1;
2995 pOutGlyphProps[i].sva.fDiacritic = 0;
2996 pOutGlyphProps[i].sva.fZeroWidth = 0;
2997 pOutGlyphProps[i].sva.fReserved = 0;
2998 pOutGlyphProps[i].sva.fShapeReserved = 0;
3000 /* FIXME: have the shaping engine set this */
3001 pCharProps[i].fCanGlyphAlone = 0;
3003 pwLogClust[i] = idx;
3006 if (psa && !psa->fNoGlyphIndex)
3008 WCHAR *rChars;
3009 if ((hr = SHAPE_CheckFontForRequiredFeatures(hdc, (ScriptCache *)*psc, psa)) != S_OK) return hr;
3011 rChars = heap_alloc(sizeof(WCHAR) * cChars);
3012 if (!rChars) return E_OUTOFMEMORY;
3013 for (i = 0, g = 0, cluster = 0; i < cChars; i++)
3015 int idx = i;
3016 DWORD chInput;
3018 if (rtl) idx = cChars - 1 - i;
3019 if (!cluster)
3021 chInput = decode_surrogate_pair(pwcChars, idx, cChars);
3022 if (!chInput)
3024 if (psa->fRTL)
3025 chInput = mirror_char(pwcChars[idx]);
3026 else
3027 chInput = pwcChars[idx];
3028 /* special case for tabs */
3029 if (chInput == 0x0009)
3030 chInput = 0x0020;
3031 rChars[i] = chInput;
3033 else
3035 rChars[i] = pwcChars[idx];
3036 rChars[i+1] = pwcChars[(rtl)?idx-1:idx+1];
3037 cluster = 1;
3039 if (!(pwOutGlyphs[g] = get_cache_glyph(psc, chInput)))
3041 WORD glyph;
3042 if (!hdc)
3044 heap_free(rChars);
3045 return E_PENDING;
3047 if (OpenType_CMAP_GetGlyphIndex(hdc, (ScriptCache *)*psc, chInput, &glyph, 0) == GDI_ERROR)
3049 heap_free(rChars);
3050 return S_FALSE;
3052 pwOutGlyphs[g] = set_cache_glyph(psc, chInput, glyph);
3054 g++;
3056 else
3058 int k;
3059 cluster--;
3060 pwLogClust[idx] = (rtl)?pwLogClust[idx+1]:pwLogClust[idx-1];
3061 for (k = (rtl)?idx-1:idx+1; k >= 0 && k < cChars; (rtl)?k--:k++)
3062 pwLogClust[k]--;
3065 *pcGlyphs = g;
3067 SHAPE_ContextualShaping(hdc, (ScriptCache *)*psc, psa, rChars, cChars, pwOutGlyphs, pcGlyphs, cMaxGlyphs, pwLogClust);
3068 SHAPE_ApplyDefaultOpentypeFeatures(hdc, (ScriptCache *)*psc, psa, pwOutGlyphs, pcGlyphs, cMaxGlyphs, cChars, pwLogClust);
3069 SHAPE_CharGlyphProp(hdc, (ScriptCache *)*psc, psa, pwcChars, cChars, pwOutGlyphs, *pcGlyphs, pwLogClust, pCharProps, pOutGlyphProps);
3070 heap_free(rChars);
3072 else
3074 TRACE("no glyph translation\n");
3075 for (i = 0; i < cChars; i++)
3077 int idx = i;
3078 /* No mirroring done here */
3079 if (rtl) idx = cChars - 1 - i;
3080 pwOutGlyphs[i] = pwcChars[idx];
3082 /* overwrite some basic control glyphs to blank */
3083 if (psa && psa->eScript == Script_Control &&
3084 pwcChars[idx] < ((ScriptCache *)*psc)->tm.tmFirstChar)
3086 if (pwcChars[idx] == 0x0009 || pwcChars[idx] == 0x000A ||
3087 pwcChars[idx] == 0x000D || pwcChars[idx] >= 0x001C)
3088 pwOutGlyphs[i] = ((ScriptCache *)*psc)->sfp.wgBlank;
3093 return S_OK;
3097 /***********************************************************************
3098 * ScriptShape (USP10.@)
3100 * Produce glyphs and visual attributes for a run.
3102 * PARAMS
3103 * hdc [I] Device context.
3104 * psc [I/O] Opaque pointer to a script cache.
3105 * pwcChars [I] Array of characters specifying the run.
3106 * cChars [I] Number of characters in pwcChars.
3107 * cMaxGlyphs [I] Length of pwOutGlyphs.
3108 * psa [I/O] Script analysis.
3109 * pwOutGlyphs [O] Array of glyphs.
3110 * pwLogClust [O] Array of logical cluster info.
3111 * psva [O] Array of visual attributes.
3112 * pcGlyphs [O] Number of glyphs returned.
3114 * RETURNS
3115 * Success: S_OK
3116 * Failure: Non-zero HRESULT value.
3118 HRESULT WINAPI ScriptShape(HDC hdc, SCRIPT_CACHE *psc, const WCHAR *pwcChars,
3119 int cChars, int cMaxGlyphs,
3120 SCRIPT_ANALYSIS *psa, WORD *pwOutGlyphs, WORD *pwLogClust,
3121 SCRIPT_VISATTR *psva, int *pcGlyphs)
3123 HRESULT hr;
3124 int i;
3125 SCRIPT_CHARPROP *charProps;
3126 SCRIPT_GLYPHPROP *glyphProps;
3128 if (!psva || !pcGlyphs) return E_INVALIDARG;
3129 if (cChars > cMaxGlyphs) return E_OUTOFMEMORY;
3131 charProps = heap_alloc_zero(sizeof(SCRIPT_CHARPROP)*cChars);
3132 if (!charProps) return E_OUTOFMEMORY;
3133 glyphProps = heap_alloc_zero(sizeof(SCRIPT_GLYPHPROP)*cMaxGlyphs);
3134 if (!glyphProps)
3136 heap_free(charProps);
3137 return E_OUTOFMEMORY;
3140 hr = ScriptShapeOpenType(hdc, psc, psa, scriptInformation[psa->eScript].scriptTag, 0, NULL, NULL, 0, pwcChars, cChars, cMaxGlyphs, pwLogClust, charProps, pwOutGlyphs, glyphProps, pcGlyphs);
3142 if (SUCCEEDED(hr))
3144 for (i = 0; i < *pcGlyphs; i++)
3145 psva[i] = glyphProps[i].sva;
3148 heap_free(charProps);
3149 heap_free(glyphProps);
3151 return hr;
3154 /***********************************************************************
3155 * ScriptPlaceOpenType (USP10.@)
3157 * Produce advance widths for a run.
3159 * PARAMS
3160 * hdc [I] Device context.
3161 * psc [I/O] Opaque pointer to a script cache.
3162 * psa [I/O] String analysis.
3163 * tagScript [I] The OpenType tag for the Script
3164 * tagLangSys [I] The OpenType tag for the Language
3165 * rcRangeChars[I] Array of Character counts in each range
3166 * rpRangeProperties [I] Array of TEXTRANGE_PROPERTIES structures
3167 * cRanges [I] Count of ranges
3168 * pwcChars [I] Array of characters specifying the run.
3169 * pwLogClust [I] Array of logical cluster info
3170 * pCharProps [I] Array of character property values
3171 * cChars [I] Number of characters in pwcChars.
3172 * pwGlyphs [I] Array of glyphs.
3173 * pGlyphProps [I] Array of attributes for the retrieved glyphs
3174 * cGlyphs [I] Count of Glyphs
3175 * piAdvance [O] Array of advance widths.
3176 * pGoffset [O] Glyph offsets.
3177 * pABC [O] Combined ABC width.
3179 * RETURNS
3180 * Success: S_OK
3181 * Failure: Non-zero HRESULT value.
3184 HRESULT WINAPI ScriptPlaceOpenType( HDC hdc, SCRIPT_CACHE *psc, SCRIPT_ANALYSIS *psa,
3185 OPENTYPE_TAG tagScript, OPENTYPE_TAG tagLangSys,
3186 int *rcRangeChars, TEXTRANGE_PROPERTIES **rpRangeProperties,
3187 int cRanges, const WCHAR *pwcChars, WORD *pwLogClust,
3188 SCRIPT_CHARPROP *pCharProps, int cChars,
3189 const WORD *pwGlyphs, const SCRIPT_GLYPHPROP *pGlyphProps,
3190 int cGlyphs, int *piAdvance,
3191 GOFFSET *pGoffset, ABC *pABC
3194 HRESULT hr;
3195 int i;
3196 static int once = 0;
3198 TRACE("(%p, %p, %p, %s, %s, %p, %p, %d, %s, %p, %p, %d, %p, %p, %d, %p %p %p)\n",
3199 hdc, psc, psa,
3200 debugstr_an((char*)&tagScript,4), debugstr_an((char*)&tagLangSys,4),
3201 rcRangeChars, rpRangeProperties, cRanges, debugstr_wn(pwcChars, cChars),
3202 pwLogClust, pCharProps, cChars, pwGlyphs, pGlyphProps, cGlyphs, piAdvance,
3203 pGoffset, pABC);
3205 if (!pGlyphProps) return E_INVALIDARG;
3206 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
3207 if (!pGoffset) return E_FAIL;
3209 if (cRanges)
3210 if (!once++) FIXME("Ranges not supported yet\n");
3212 ((ScriptCache *)*psc)->userScript = tagScript;
3213 ((ScriptCache *)*psc)->userLang = tagLangSys;
3215 if (pABC) memset(pABC, 0, sizeof(ABC));
3216 for (i = 0; i < cGlyphs; i++)
3218 ABC abc;
3219 if (!get_cache_glyph_widths(psc, pwGlyphs[i], &abc))
3221 if (!hdc) return E_PENDING;
3222 if ((get_cache_pitch_family(psc) & TMPF_TRUETYPE) && !psa->fNoGlyphIndex)
3224 if (!GetCharABCWidthsI(hdc, 0, 1, (WORD *)&pwGlyphs[i], &abc)) return S_FALSE;
3226 else
3228 INT width;
3229 if (!GetCharWidth32W(hdc, pwGlyphs[i], pwGlyphs[i], &width)) return S_FALSE;
3230 abc.abcB = width;
3231 abc.abcA = abc.abcC = 0;
3233 set_cache_glyph_widths(psc, pwGlyphs[i], &abc);
3235 if (pABC)
3237 pABC->abcA += abc.abcA;
3238 pABC->abcB += abc.abcB;
3239 pABC->abcC += abc.abcC;
3241 /* FIXME: set to more reasonable values */
3242 pGoffset[i].du = pGoffset[i].dv = 0;
3243 if (piAdvance) piAdvance[i] = abc.abcA + abc.abcB + abc.abcC;
3246 SHAPE_ApplyOpenTypePositions(hdc, (ScriptCache *)*psc, psa, pwGlyphs, cGlyphs, piAdvance, pGoffset);
3248 if (pABC) TRACE("Total for run: abcA=%d, abcB=%d, abcC=%d\n", pABC->abcA, pABC->abcB, pABC->abcC);
3249 return S_OK;
3252 /***********************************************************************
3253 * ScriptPlace (USP10.@)
3255 * Produce advance widths for a run.
3257 * PARAMS
3258 * hdc [I] Device context.
3259 * psc [I/O] Opaque pointer to a script cache.
3260 * pwGlyphs [I] Array of glyphs.
3261 * cGlyphs [I] Number of glyphs in pwGlyphs.
3262 * psva [I] Array of visual attributes.
3263 * psa [I/O] String analysis.
3264 * piAdvance [O] Array of advance widths.
3265 * pGoffset [O] Glyph offsets.
3266 * pABC [O] Combined ABC width.
3268 * RETURNS
3269 * Success: S_OK
3270 * Failure: Non-zero HRESULT value.
3272 HRESULT WINAPI ScriptPlace(HDC hdc, SCRIPT_CACHE *psc, const WORD *pwGlyphs,
3273 int cGlyphs, const SCRIPT_VISATTR *psva,
3274 SCRIPT_ANALYSIS *psa, int *piAdvance, GOFFSET *pGoffset, ABC *pABC )
3276 HRESULT hr;
3277 SCRIPT_GLYPHPROP *glyphProps;
3278 int i;
3280 TRACE("(%p, %p, %p, %d, %p, %p, %p, %p, %p)\n", hdc, psc, pwGlyphs, cGlyphs, psva, psa,
3281 piAdvance, pGoffset, pABC);
3283 if (!psva) return E_INVALIDARG;
3284 if (!pGoffset) return E_FAIL;
3286 glyphProps = heap_alloc(sizeof(SCRIPT_GLYPHPROP)*cGlyphs);
3287 if (!glyphProps) return E_OUTOFMEMORY;
3289 for (i = 0; i < cGlyphs; i++)
3290 glyphProps[i].sva = psva[i];
3292 hr = ScriptPlaceOpenType(hdc, psc, psa, scriptInformation[psa->eScript].scriptTag, 0, NULL, NULL, 0, NULL, NULL, NULL, 0, pwGlyphs, glyphProps, cGlyphs, piAdvance, pGoffset, pABC);
3294 heap_free(glyphProps);
3296 return hr;
3299 /***********************************************************************
3300 * ScriptGetCMap (USP10.@)
3302 * Retrieve glyph indices.
3304 * PARAMS
3305 * hdc [I] Device context.
3306 * psc [I/O] Opaque pointer to a script cache.
3307 * pwcInChars [I] Array of Unicode characters.
3308 * cChars [I] Number of characters in pwcInChars.
3309 * dwFlags [I] Flags.
3310 * pwOutGlyphs [O] Buffer to receive the array of glyph indices.
3312 * RETURNS
3313 * Success: S_OK
3314 * Failure: Non-zero HRESULT value.
3316 HRESULT WINAPI ScriptGetCMap(HDC hdc, SCRIPT_CACHE *psc, const WCHAR *pwcInChars,
3317 int cChars, DWORD dwFlags, WORD *pwOutGlyphs)
3319 HRESULT hr;
3320 int i;
3322 TRACE("(%p,%p,%s,%d,0x%x,%p)\n", hdc, psc, debugstr_wn(pwcInChars, cChars),
3323 cChars, dwFlags, pwOutGlyphs);
3325 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
3327 hr = S_OK;
3329 if ((get_cache_pitch_family(psc) & TMPF_TRUETYPE))
3331 for (i = 0; i < cChars; i++)
3333 WCHAR inChar;
3334 if (dwFlags == SGCM_RTL)
3335 inChar = mirror_char(pwcInChars[i]);
3336 else
3337 inChar = pwcInChars[i];
3338 if (!(pwOutGlyphs[i] = get_cache_glyph(psc, inChar)))
3340 WORD glyph;
3341 if (!hdc) return E_PENDING;
3342 if (GetGlyphIndicesW(hdc, &inChar, 1, &glyph, GGI_MARK_NONEXISTING_GLYPHS) == GDI_ERROR) return S_FALSE;
3343 if (glyph == 0xffff)
3345 hr = S_FALSE;
3346 glyph = 0x0;
3348 pwOutGlyphs[i] = set_cache_glyph(psc, inChar, glyph);
3352 else
3354 TRACE("no glyph translation\n");
3355 for (i = 0; i < cChars; i++)
3357 WCHAR inChar;
3358 if (dwFlags == SGCM_RTL)
3359 inChar = mirror_char(pwcInChars[i]);
3360 else
3361 inChar = pwcInChars[i];
3362 pwOutGlyphs[i] = inChar;
3365 return hr;
3368 /***********************************************************************
3369 * ScriptTextOut (USP10.@)
3372 HRESULT WINAPI ScriptTextOut(const HDC hdc, SCRIPT_CACHE *psc, int x, int y, UINT fuOptions,
3373 const RECT *lprc, const SCRIPT_ANALYSIS *psa, const WCHAR *pwcReserved,
3374 int iReserved, const WORD *pwGlyphs, int cGlyphs, const int *piAdvance,
3375 const int *piJustify, const GOFFSET *pGoffset)
3377 HRESULT hr = S_OK;
3378 INT i, dir = 1;
3379 INT *lpDx;
3380 WORD *reordered_glyphs = (WORD *)pwGlyphs;
3382 TRACE("(%p, %p, %d, %d, %04x, %p, %p, %p, %d, %p, %d, %p, %p, %p)\n",
3383 hdc, psc, x, y, fuOptions, lprc, psa, pwcReserved, iReserved, pwGlyphs, cGlyphs,
3384 piAdvance, piJustify, pGoffset);
3386 if (!hdc || !psc) return E_INVALIDARG;
3387 if (!piAdvance || !psa || !pwGlyphs) return E_INVALIDARG;
3389 fuOptions &= ETO_CLIPPED + ETO_OPAQUE;
3390 fuOptions |= ETO_IGNORELANGUAGE;
3391 if (!psa->fNoGlyphIndex) /* Have Glyphs? */
3392 fuOptions |= ETO_GLYPH_INDEX; /* Say don't do translation to glyph */
3394 lpDx = heap_alloc(cGlyphs * sizeof(INT) * 2);
3395 if (!lpDx) return E_OUTOFMEMORY;
3396 fuOptions |= ETO_PDY;
3398 if (psa->fRTL && psa->fLogicalOrder)
3400 reordered_glyphs = heap_alloc( cGlyphs * sizeof(WORD) );
3401 if (!reordered_glyphs)
3403 heap_free( lpDx );
3404 return E_OUTOFMEMORY;
3407 for (i = 0; i < cGlyphs; i++)
3408 reordered_glyphs[i] = pwGlyphs[cGlyphs - 1 - i];
3409 dir = -1;
3412 for (i = 0; i < cGlyphs; i++)
3414 int orig_index = (dir > 0) ? i : cGlyphs - 1 - i;
3415 lpDx[i * 2] = piAdvance[orig_index];
3416 lpDx[i * 2 + 1] = 0;
3418 if (pGoffset)
3420 if (i == 0)
3422 x += pGoffset[orig_index].du * dir;
3423 y += pGoffset[orig_index].dv;
3425 else
3427 lpDx[(i - 1) * 2] += pGoffset[orig_index].du * dir;
3428 lpDx[(i - 1) * 2 + 1] += pGoffset[orig_index].dv;
3430 lpDx[i * 2] -= pGoffset[orig_index].du * dir;
3431 lpDx[i * 2 + 1] -= pGoffset[orig_index].dv;
3435 if (!ExtTextOutW(hdc, x, y, fuOptions, lprc, reordered_glyphs, cGlyphs, lpDx))
3436 hr = S_FALSE;
3438 if (reordered_glyphs != pwGlyphs) heap_free( reordered_glyphs );
3439 heap_free(lpDx);
3441 return hr;
3444 /***********************************************************************
3445 * ScriptCacheGetHeight (USP10.@)
3447 * Retrieve the height of the font in the cache.
3449 * PARAMS
3450 * hdc [I] Device context.
3451 * psc [I/O] Opaque pointer to a script cache.
3452 * height [O] Receives font height.
3454 * RETURNS
3455 * Success: S_OK
3456 * Failure: Non-zero HRESULT value.
3458 HRESULT WINAPI ScriptCacheGetHeight(HDC hdc, SCRIPT_CACHE *psc, LONG *height)
3460 HRESULT hr;
3462 TRACE("(%p, %p, %p)\n", hdc, psc, height);
3464 if (!height) return E_INVALIDARG;
3465 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
3467 *height = get_cache_height(psc);
3468 return S_OK;
3471 /***********************************************************************
3472 * ScriptGetGlyphABCWidth (USP10.@)
3474 * Retrieve the width of a glyph.
3476 * PARAMS
3477 * hdc [I] Device context.
3478 * psc [I/O] Opaque pointer to a script cache.
3479 * glyph [I] Glyph to retrieve the width for.
3480 * abc [O] ABC widths of the glyph.
3482 * RETURNS
3483 * Success: S_OK
3484 * Failure: Non-zero HRESULT value.
3486 HRESULT WINAPI ScriptGetGlyphABCWidth(HDC hdc, SCRIPT_CACHE *psc, WORD glyph, ABC *abc)
3488 HRESULT hr;
3490 TRACE("(%p, %p, 0x%04x, %p)\n", hdc, psc, glyph, abc);
3492 if (!abc) return E_INVALIDARG;
3493 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
3495 if (!get_cache_glyph_widths(psc, glyph, abc))
3497 if (!hdc) return E_PENDING;
3498 if ((get_cache_pitch_family(psc) & TMPF_TRUETYPE))
3500 if (!GetCharABCWidthsI(hdc, 0, 1, &glyph, abc)) return S_FALSE;
3502 else
3504 INT width;
3505 if (!GetCharWidth32W(hdc, glyph, glyph, &width)) return S_FALSE;
3506 abc->abcB = width;
3507 abc->abcA = abc->abcC = 0;
3509 set_cache_glyph_widths(psc, glyph, abc);
3511 return S_OK;
3514 /***********************************************************************
3515 * ScriptLayout (USP10.@)
3517 * Map embedding levels to visual and/or logical order.
3519 * PARAMS
3520 * runs [I] Size of level array.
3521 * level [I] Array of embedding levels.
3522 * vistolog [O] Map of embedding levels from visual to logical order.
3523 * logtovis [O] Map of embedding levels from logical to visual order.
3525 * RETURNS
3526 * Success: S_OK
3527 * Failure: Non-zero HRESULT value.
3530 HRESULT WINAPI ScriptLayout(int runs, const BYTE *level, int *vistolog, int *logtovis)
3532 int* indexs;
3533 int ich;
3535 TRACE("(%d, %p, %p, %p)\n", runs, level, vistolog, logtovis);
3537 if (!level || (!vistolog && !logtovis))
3538 return E_INVALIDARG;
3540 indexs = heap_alloc(sizeof(int) * runs);
3541 if (!indexs)
3542 return E_OUTOFMEMORY;
3545 if (vistolog)
3547 for( ich = 0; ich < runs; ich++)
3548 indexs[ich] = ich;
3550 ich = 0;
3551 while (ich < runs)
3552 ich += BIDI_ReorderV2lLevel(0, indexs+ich, level+ich, runs - ich, FALSE);
3553 for (ich = 0; ich < runs; ich++)
3554 vistolog[ich] = indexs[ich];
3558 if (logtovis)
3560 for( ich = 0; ich < runs; ich++)
3561 indexs[ich] = ich;
3563 ich = 0;
3564 while (ich < runs)
3565 ich += BIDI_ReorderL2vLevel(0, indexs+ich, level+ich, runs - ich, FALSE);
3566 for (ich = 0; ich < runs; ich++)
3567 logtovis[ich] = indexs[ich];
3569 heap_free(indexs);
3571 return S_OK;
3574 /***********************************************************************
3575 * ScriptStringGetLogicalWidths (USP10.@)
3577 * Returns logical widths from a string analysis.
3579 * PARAMS
3580 * ssa [I] string analysis.
3581 * piDx [O] logical widths returned.
3583 * RETURNS
3584 * Success: S_OK
3585 * Failure: a non-zero HRESULT.
3587 HRESULT WINAPI ScriptStringGetLogicalWidths(SCRIPT_STRING_ANALYSIS ssa, int *piDx)
3589 int i, j, next = 0;
3590 StringAnalysis *analysis = ssa;
3592 TRACE("%p, %p\n", ssa, piDx);
3594 if (!analysis) return S_FALSE;
3595 if (!(analysis->dwFlags & SSA_GLYPHS)) return S_FALSE;
3597 for (i = 0; i < analysis->numItems; i++)
3599 int cChar = analysis->pItem[i+1].iCharPos - analysis->pItem[i].iCharPos;
3600 int direction = 1;
3602 if (analysis->pItem[i].a.fRTL && ! analysis->pItem[i].a.fLogicalOrder)
3603 direction = -1;
3605 for (j = 0; j < cChar; j++)
3607 int k;
3608 int glyph = analysis->glyphs[i].pwLogClust[j];
3609 int clust_size = get_cluster_size(analysis->glyphs[i].pwLogClust,
3610 cChar, j, direction, NULL, NULL);
3611 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);
3613 for (k = 0; k < clust_size; k++)
3615 piDx[next] = advance / clust_size;
3616 next++;
3617 if (k) j++;
3621 return S_OK;
3624 /***********************************************************************
3625 * ScriptStringValidate (USP10.@)
3627 * Validate a string analysis.
3629 * PARAMS
3630 * ssa [I] string analysis.
3632 * RETURNS
3633 * Success: S_OK
3634 * Failure: S_FALSE if invalid sequences are found
3635 * or a non-zero HRESULT if it fails.
3637 HRESULT WINAPI ScriptStringValidate(SCRIPT_STRING_ANALYSIS ssa)
3639 StringAnalysis *analysis = ssa;
3641 TRACE("(%p)\n", ssa);
3643 if (!analysis) return E_INVALIDARG;
3644 return (analysis->invalid) ? S_FALSE : S_OK;
3647 /***********************************************************************
3648 * ScriptString_pSize (USP10.@)
3650 * Retrieve width and height of an analysed string.
3652 * PARAMS
3653 * ssa [I] string analysis.
3655 * RETURNS
3656 * Success: Pointer to a SIZE structure.
3657 * Failure: NULL
3659 const SIZE * WINAPI ScriptString_pSize(SCRIPT_STRING_ANALYSIS ssa)
3661 int i, j;
3662 StringAnalysis *analysis = ssa;
3664 TRACE("(%p)\n", ssa);
3666 if (!analysis) return NULL;
3667 if (!(analysis->dwFlags & SSA_GLYPHS)) return NULL;
3669 if (!analysis->sz)
3671 if (!(analysis->sz = heap_alloc(sizeof(SIZE)))) return NULL;
3672 analysis->sz->cy = analysis->glyphs[0].sc->tm.tmHeight;
3674 analysis->sz->cx = 0;
3675 for (i = 0; i < analysis->numItems; i++)
3677 if (analysis->glyphs[i].sc->tm.tmHeight > analysis->sz->cy)
3678 analysis->sz->cy = analysis->glyphs[i].sc->tm.tmHeight;
3679 for (j = 0; j < analysis->glyphs[i].numGlyphs; j++)
3680 analysis->sz->cx += analysis->glyphs[i].piAdvance[j];
3683 return analysis->sz;
3686 /***********************************************************************
3687 * ScriptString_pLogAttr (USP10.@)
3689 * Retrieve logical attributes of an analysed string.
3691 * PARAMS
3692 * ssa [I] string analysis.
3694 * RETURNS
3695 * Success: Pointer to an array of SCRIPT_LOGATTR structures.
3696 * Failure: NULL
3698 const SCRIPT_LOGATTR * WINAPI ScriptString_pLogAttr(SCRIPT_STRING_ANALYSIS ssa)
3700 StringAnalysis *analysis = ssa;
3702 TRACE("(%p)\n", ssa);
3704 if (!analysis) return NULL;
3705 if (!(analysis->dwFlags & SSA_BREAK)) return NULL;
3706 return analysis->logattrs;
3709 /***********************************************************************
3710 * ScriptString_pcOutChars (USP10.@)
3712 * Retrieve the length of a string after clipping.
3714 * PARAMS
3715 * ssa [I] String analysis.
3717 * RETURNS
3718 * Success: Pointer to the length.
3719 * Failure: NULL
3721 const int * WINAPI ScriptString_pcOutChars(SCRIPT_STRING_ANALYSIS ssa)
3723 StringAnalysis *analysis = ssa;
3725 TRACE("(%p)\n", ssa);
3727 if (!analysis) return NULL;
3728 return &analysis->clip_len;
3731 /***********************************************************************
3732 * ScriptStringGetOrder (USP10.@)
3734 * Retrieve a glyph order map.
3736 * PARAMS
3737 * ssa [I] String analysis.
3738 * order [I/O] Array of glyph positions.
3740 * RETURNS
3741 * Success: S_OK
3742 * Failure: a non-zero HRESULT.
3744 HRESULT WINAPI ScriptStringGetOrder(SCRIPT_STRING_ANALYSIS ssa, UINT *order)
3746 int i, j;
3747 unsigned int k;
3748 StringAnalysis *analysis = ssa;
3750 TRACE("(%p)\n", ssa);
3752 if (!analysis) return S_FALSE;
3753 if (!(analysis->dwFlags & SSA_GLYPHS)) return S_FALSE;
3755 /* FIXME: handle RTL scripts */
3756 for (i = 0, k = 0; i < analysis->numItems; i++)
3757 for (j = 0; j < analysis->glyphs[i].numGlyphs; j++, k++)
3758 order[k] = k;
3760 return S_OK;
3763 /***********************************************************************
3764 * ScriptGetLogicalWidths (USP10.@)
3766 * Convert advance widths to logical widths.
3768 * PARAMS
3769 * sa [I] Script analysis.
3770 * nbchars [I] Number of characters.
3771 * nbglyphs [I] Number of glyphs.
3772 * glyph_width [I] Array of glyph widths.
3773 * log_clust [I] Array of logical clusters.
3774 * sva [I] Visual attributes.
3775 * widths [O] Array of logical widths.
3777 * RETURNS
3778 * Success: S_OK
3779 * Failure: a non-zero HRESULT.
3781 HRESULT WINAPI ScriptGetLogicalWidths(const SCRIPT_ANALYSIS *sa, int nbchars, int nbglyphs,
3782 const int *glyph_width, const WORD *log_clust,
3783 const SCRIPT_VISATTR *sva, int *widths)
3785 int i;
3787 TRACE("(%p, %d, %d, %p, %p, %p, %p)\n",
3788 sa, nbchars, nbglyphs, glyph_width, log_clust, sva, widths);
3790 /* FIXME */
3791 for (i = 0; i < nbchars; i++) widths[i] = glyph_width[i];
3792 return S_OK;
3795 /***********************************************************************
3796 * ScriptApplyLogicalWidth (USP10.@)
3798 * Generate glyph advance widths.
3800 * PARAMS
3801 * dx [I] Array of logical advance widths.
3802 * num_chars [I] Number of characters.
3803 * num_glyphs [I] Number of glyphs.
3804 * log_clust [I] Array of logical clusters.
3805 * sva [I] Visual attributes.
3806 * advance [I] Array of glyph advance widths.
3807 * sa [I] Script analysis.
3808 * abc [I/O] Summed ABC widths.
3809 * justify [O] Array of glyph advance widths.
3811 * RETURNS
3812 * Success: S_OK
3813 * Failure: a non-zero HRESULT.
3815 HRESULT WINAPI ScriptApplyLogicalWidth(const int *dx, int num_chars, int num_glyphs,
3816 const WORD *log_clust, const SCRIPT_VISATTR *sva,
3817 const int *advance, const SCRIPT_ANALYSIS *sa,
3818 ABC *abc, int *justify)
3820 int i;
3822 FIXME("(%p, %d, %d, %p, %p, %p, %p, %p, %p)\n",
3823 dx, num_chars, num_glyphs, log_clust, sva, advance, sa, abc, justify);
3825 for (i = 0; i < num_chars; i++) justify[i] = advance[i];
3826 return S_OK;
3829 HRESULT WINAPI ScriptJustify(const SCRIPT_VISATTR *sva, const int *advance,
3830 int num_glyphs, int dx, int min_kashida, int *justify)
3832 int i;
3834 FIXME("(%p, %p, %d, %d, %d, %p)\n", sva, advance, num_glyphs, dx, min_kashida, justify);
3836 for (i = 0; i < num_glyphs; i++) justify[i] = advance[i];
3837 return S_OK;
3840 HRESULT WINAPI ScriptGetFontScriptTags( HDC hdc, SCRIPT_CACHE *psc, SCRIPT_ANALYSIS *psa, int cMaxTags, OPENTYPE_TAG *pScriptTags, int *pcTags)
3842 HRESULT hr;
3843 if (!pScriptTags || !pcTags || cMaxTags == 0) return E_INVALIDARG;
3844 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
3846 return SHAPE_GetFontScriptTags(hdc, (ScriptCache *)*psc, psa, cMaxTags, pScriptTags, pcTags);
3849 HRESULT WINAPI ScriptGetFontLanguageTags( HDC hdc, SCRIPT_CACHE *psc, SCRIPT_ANALYSIS *psa, OPENTYPE_TAG tagScript, int cMaxTags, OPENTYPE_TAG *pLangSysTags, int *pcTags)
3851 HRESULT hr;
3852 if (!pLangSysTags || !pcTags || cMaxTags == 0) return E_INVALIDARG;
3853 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
3855 return SHAPE_GetFontLanguageTags(hdc, (ScriptCache *)*psc, psa, tagScript, cMaxTags, pLangSysTags, pcTags);
3858 HRESULT WINAPI ScriptGetFontFeatureTags( HDC hdc, SCRIPT_CACHE *psc, SCRIPT_ANALYSIS *psa, OPENTYPE_TAG tagScript, OPENTYPE_TAG tagLangSys, int cMaxTags, OPENTYPE_TAG *pFeatureTags, int *pcTags)
3860 HRESULT hr;
3861 if (!pFeatureTags || !pcTags || cMaxTags == 0) return E_INVALIDARG;
3862 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
3864 return SHAPE_GetFontFeatureTags(hdc, (ScriptCache *)*psc, psa, tagScript, tagLangSys, cMaxTags, pFeatureTags, pcTags);