usp10: Add CKJ Han and Ideographic scripts.
[wine/multimedia.git] / dlls / usp10 / usp10.c
blob2c860cf46d65f06ac80bdaff63d5ad35314e92f8
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>
29 #include "windef.h"
30 #include "winbase.h"
31 #include "wingdi.h"
32 #include "winuser.h"
33 #include "winnls.h"
34 #include "winreg.h"
35 #include "usp10.h"
37 #include "usp10_internal.h"
39 #include "wine/debug.h"
40 #include "wine/unicode.h"
42 WINE_DEFAULT_DEBUG_CHANNEL(uniscribe);
44 typedef struct _scriptRange
46 WORD script;
47 WORD rangeFirst;
48 WORD rangeLast;
49 WORD numericScript;
50 WORD punctScript;
51 } scriptRange;
53 static const scriptRange scriptRanges[] = {
54 /* Basic Latin: U+0000–U+007A */
55 { Script_Latin, 0x00, 0x07a , Script_Numeric, Script_Punctuation},
56 /* Latin-1 Supplement: U+0080–U+00FF */
57 /* Latin Extended-A: U+0100–U+017F */
58 /* Latin Extended-B: U+0180–U+024F */
59 /* IPA Extensions: U+0250–U+02AF */
60 { Script_Latin, 0x80, 0x2af , Script_Numeric2, Script_Punctuation},
61 /* Combining Diacritical Marks : U+0300–U+036F */
62 { Script_Diacritical,0x300, 0x36f, 0, 0},
63 /* Greek: U+0370–U+03FF */
64 { Script_Greek, 0x370, 0x3ff, 0, 0},
65 /* Cyrillic: U+0400–U+04FF */
66 /* Cyrillic Supplement: U+0500–U+052F */
67 { Script_Cyrillic, 0x400, 0x52f, 0, 0},
68 /* Armenian: U+0530–U+058F */
69 { Script_Armenian, 0x530, 0x58f, 0, 0},
70 /* Hebrew: U+0590–U+05FF */
71 { Script_Hebrew, 0x590, 0x5ff, 0, 0},
72 /* Arabic: U+0600–U+06FF */
73 { Script_Arabic, 0x600, 0x6ef, Script_Arabic_Numeric, 0},
74 /* Defined by Windows */
75 { Script_Persian, 0x6f0, 0x6f9, 0, 0},
76 /* Continue Arabic: U+0600–U+06FF */
77 { Script_Arabic, 0x6fa, 0x6ff, 0, 0},
78 /* Syriac: U+0700–U+074F*/
79 { Script_Syriac, 0x700, 0x74f, 0, 0},
80 /* Arabic Supplement: U+0750–U+077F */
81 { Script_Arabic, 0x750, 0x77f, 0, 0},
82 /* Thaana: U+0780–U+07BF */
83 { Script_Thaana, 0x780, 0x7bf, 0, 0},
84 /* Devanagari: U+0900–U+097F */
85 { Script_Devanagari, 0x900, 0x97f, Script_Devanagari_Numeric, 0},
86 /* Bengali: U+0980–U+09FF */
87 { Script_Bengali, 0x980, 0x9ff, Script_Bengali_Numeric, 0},
88 /* Gurmukhi: U+0A00–U+0A7F*/
89 { Script_Gurmukhi, 0xa00, 0xa7f, Script_Gurmukhi_Numeric, 0},
90 /* Gujarati: U+0A80–U+0AFF*/
91 { Script_Gujarati, 0xa80, 0xaff, Script_Gujarati_Numeric, 0},
92 /* Oriya: U+0B00–U+0B7F */
93 { Script_Oriya, 0xb00, 0xb7f, Script_Oriya_Numeric, 0},
94 /* Tamil: U+0B80–U+0BFF */
95 { Script_Tamil, 0xb80, 0xbff, Script_Tamil_Numeric, 0},
96 /* Telugu: U+0C00–U+0C7F */
97 { Script_Telugu, 0xc00, 0xc7f, Script_Telugu_Numeric, 0},
98 /* Kannada: U+0C80–U+0CFF */
99 { Script_Kannada, 0xc80, 0xcff, Script_Kannada_Numeric, 0},
100 /* Malayalam: U+0D00–U+0D7F */
101 { Script_Malayalam, 0xd00, 0xd7f, Script_Malayalam_Numeric, 0},
102 /* Sinhala: U+0D80–U+0DFF */
103 { Script_Sinhala, 0xd80, 0xdff, 0, 0},
104 /* Thai: U+0E00–U+0E7F */
105 { Script_Thai, 0xe00, 0xe7f, Script_Thai_Numeric, 0},
106 /* Lao: U+0E80–U+0EFF */
107 { Script_Lao, 0xe80, 0xeff, Script_Lao_Numeric, 0},
108 /* Tibetan: U+0F00–U+0FFF */
109 { Script_Tibetan, 0xf00, 0xfff, 0, 0},
110 /* Myanmar: U+1000–U+109F */
111 { Script_Myanmar, 0x1000, 0x109f, Script_Myanmar_Numeric, 0},
112 /* Georgian: U+10A0–U+10FF */
113 { Script_Georgian, 0x10a0, 0x10ff, 0, 0},
114 /* Khmer: U+1780–U+17FF */
115 { Script_Khmer, 0x1780, 0x17ff, Script_Khmer_Numeric, 0},
116 /* Tai Le: U+1950–U+197F */
117 { Script_Tai_Le, 0x1950, 0x197f, 0, 0},
118 /* New Tai Lue: U+1980–U+19DF */
119 { Script_New_Tai_Lue,0x1980, 0x19df, Script_New_Tai_Lue_Numeric, 0},
120 /* Khmer Symbols: U+19E0–U+19FF */
121 { Script_Khmer, 0x19e0, 0x19ff, Script_Khmer_Numeric, 0},
122 /* Vedic Extensions: U+1CD0-U+1CFF */
123 { Script_Devanagari, 0x1cd0, 0x1cff, Script_Devanagari_Numeric, 0},
124 /* Phonetic Extensions: U+1D00–U+1DBF */
125 { Script_Latin, 0x1d00, 0x1dbf, 0, 0},
126 /* Combining Diacritical Marks Supplement: U+1DC0–U+1DFF */
127 { Script_Diacritical,0x1dc0, 0x1dff, 0, 0},
128 /* Latin Extended Additional: U+1E00–U+1EFF */
129 { Script_Latin, 0x1e00, 0x1eff, 0, 0},
130 /* Greek Extended: U+1F00–U+1FFF */
131 { Script_Greek, 0x1f00, 0x1fff, 0, 0},
132 /* General Punctuation: U+2000 –U+206f */
133 { Script_Latin, 0x2000, 0x206f, 0, 0},
134 /* Superscripts and Subscripts : U+2070 –U+209f */
135 /* Currency Symbols : U+20a0 –U+20cf */
136 { Script_Numeric2, 0x2070, 0x2070, 0, 0},
137 { Script_Latin, 0x2071, 0x2073, 0, 0},
138 { Script_Numeric2, 0x2074, 0x2079, 0, 0},
139 { Script_Latin, 0x207a, 0x207f, 0, 0},
140 { Script_Numeric2, 0x2080, 0x2089, 0, 0},
141 { Script_Latin, 0x208a, 0x20cf, 0, 0},
142 /* Letterlike Symbols : U+2100 –U+214f */
143 /* Number Forms : U+2150 –U+218f */
144 /* Arrows : U+2190 –U+21ff */
145 /* Mathematical Operators : U+2200 –U+22ff */
146 /* Miscellaneous Technical : U+2300 –U+23ff */
147 /* Control Pictures : U+2400 –U+243f */
148 /* Optical Character Recognition : U+2440 –U+245f */
149 /* Enclosed Alphanumerics : U+2460 –U+24ff */
150 /* Box Drawing : U+2500 –U+25ff */
151 /* Block Elements : U+2580 –U+259f */
152 /* Geometric Shapes : U+25a0 –U+25ff */
153 /* Miscellaneous Symbols : U+2600 –U+26ff */
154 /* Dingbats : U+2700 –U+27bf */
155 /* Miscellaneous Mathematical Symbols-A : U+27c0 –U+27ef */
156 /* Supplemental Arrows-A : U+27f0 –U+27ff */
157 { Script_Latin, 0x2100, 0x27ff, 0, 0},
158 /* Supplemental Arrows-B : U+2900 –U+297f */
159 /* Miscellaneous Mathematical Symbols-B : U+2980 –U+29ff */
160 /* Supplemental Mathematical Operators : U+2a00 –U+2aff */
161 /* Miscellaneous Symbols and Arrows : U+2b00 –U+2bff */
162 { Script_Latin, 0x2900, 0x2bff, 0, 0},
163 /* Latin Extended-C: U+2C60–U+2C7F */
164 { Script_Latin, 0x2c60, 0x2c7f, 0, 0},
165 /* Georgian: U+2D00–U+2D2F */
166 { Script_Georgian, 0x2d00, 0x2d2f, 0, 0},
167 /* Cyrillic Extended-A: U+2DE0–U+2DFF */
168 { Script_Cyrillic, 0x2de0, 0x2dff, 0, 0},
169 /* CJK Radicals Supplement: U+2E80–U+2EFF */
170 /* Kangxi Radicals: U+2F00–U+2FDF */
171 { Script_CJK_Han, 0x2e80, 0x2fdf, 0, 0},
172 /* Ideographic Description Characters: U+2FF0–U+2FFF */
173 { Script_Ideograph ,0x2ff0, 0x2fff, 0, 0},
174 /* CJK Symbols and Punctuation: U+3000–U+303F */
175 { Script_Ideograph ,0x3000, 0x3004, 0, 0},
176 { Script_CJK_Han ,0x3005, 0x3005, 0, 0},
177 { Script_Ideograph ,0x3006, 0x3006, 0, 0},
178 { Script_CJK_Han ,0x3007, 0x3007, 0, 0},
179 { Script_Ideograph ,0x3008, 0x3020, 0, 0},
180 { Script_CJK_Han ,0x3021, 0x3029, 0, 0},
181 { Script_Ideograph ,0x302a, 0x3030, 0, 0},
182 { Script_Ideograph ,0x3036, 0x3037, 0, 0},
183 { Script_CJK_Han ,0x3038, 0x303b, 0, 0},
184 { Script_Ideograph ,0x303c, 0x303f, 0, 0},
185 /* Kanbun: U+3190–U+319F */
186 { Script_Ideograph ,0x3190, 0x319f, 0, 0},
187 /* CJK Strokes: U+31C0–U+31EF */
188 { Script_Ideograph ,0x31c0, 0x31ef, 0, 0},
189 /* Enclosed CJK Letters and Months: U+3200–U+32FF */
190 { Script_Ideograph ,0x3220, 0x32ff, 0, 0},
191 /* CJK Compatibility: U+3300–U+33FF*/
192 { Script_Ideograph ,0x3300, 0x33ff, 0, 0},
193 /* CJK Unified Ideographs Extension A: U+3400–U+4DBF */
194 { Script_CJK_Han ,0x3400, 0x4dbf, 0, 0},
195 /* CJK Unified Ideographs: U+4E00–U+9FFF */
196 { Script_CJK_Han ,0x4e00, 0x9fff, 0, 0},
197 /* Cyrillic Extended-B: U+A640–U+A69F */
198 { Script_Cyrillic, 0xa640, 0xa69f, 0, 0},
199 /* Modifier Tone Letters: U+A700–U+A71F */
200 /* Latin Extended-D: U+A720–U+A7FF */
201 { Script_Latin, 0xa700, 0xa7ff, 0, 0},
202 /* Phags-pa: U+A840–U+A87F */
203 { Script_Phags_pa, 0xa840, 0xa87f, 0, 0},
204 /* Devanagari Extended: U+A8E0-U+A8FF */
205 { Script_Devanagari, 0xa8e0, 0xa8ff, Script_Devanagari_Numeric, 0},
206 /* Myanmar Extended-A: U+AA60–U+AA7F */
207 { Script_Myanmar, 0xaa60, 0xaa7f, Script_Myanmar_Numeric, 0},
208 /* CJK Compatibility Ideographs: U+F900–U+FAFF */
209 { Script_CJK_Han ,0xf900, 0xfaff, 0, 0},
210 /* Latin Ligatures: U+FB00–U+FB06 */
211 { Script_Latin, 0xfb00, 0xfb06, 0, 0},
212 /* Armenian ligatures U+FB13..U+FB17 */
213 { Script_Armenian, 0xfb13, 0xfb17, 0, 0},
214 /* Alphabetic Presentation Forms: U+FB1D–U+FB4F */
215 { Script_Hebrew, 0xfb1d, 0xfb4f, 0, 0},
216 /* Arabic Presentation Forms-A: U+FB50–U+FDFF*/
217 { Script_Arabic, 0xfb50, 0xfdff, 0, 0},
218 /* Vertical Forms: U+FE10–U+FE1F */
219 /* Combining Half Marks: U+FE20–U+FE2F */
220 /* CJK Compatibility Forms: U+FE30–U+FE4F */
221 /* Small Form Variants: U+FE50–U+FE6F */
222 { Script_Ideograph ,0xfe10, 0xfe6f, 0, 0},
223 /* Arabic Presentation Forms-B: U+FE70–U+FEFF*/
224 { Script_Arabic, 0xfe70, 0xfeff, 0, 0},
225 /* Halfwidth and Fullwidth Forms: U+FF00–FFEF */
226 { Script_Ideograph ,0xff00, 0xff64, Script_Numeric2, 0},
227 { Script_Ideograph ,0xffe0, 0xffef, 0, 0},
228 /* END */
229 { SCRIPT_UNDEFINED, 0, 0, 0}
232 typedef struct _scriptData
234 SCRIPT_ANALYSIS a;
235 SCRIPT_PROPERTIES props;
236 OPENTYPE_TAG scriptTag;
237 WCHAR fallbackFont[LF_FACESIZE];
238 } scriptData;
240 /* the must be in order so that the index matches the Script value */
241 static const scriptData scriptInformation[] = {
242 {{SCRIPT_UNDEFINED, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
243 {LANG_NEUTRAL, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
244 0x00000000,
245 {0}},
246 {{Script_Latin, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
247 {LANG_ENGLISH, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
248 MS_MAKE_TAG('l','a','t','n'),
249 {0}},
250 {{Script_CR, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
251 {LANG_NEUTRAL, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
252 0x00000000,
253 {0}},
254 {{Script_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
255 {LANG_ENGLISH, 1, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
256 0x00000000,
257 {0}},
258 {{Script_Control, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
259 {LANG_ENGLISH, 0, 1, 0, 0, ANSI_CHARSET, 1, 0, 0, 0, 0, 0, 1, 0, 0},
260 0x00000000,
261 {0}},
262 {{Script_Punctuation, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
263 {LANG_NEUTRAL, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
264 0x00000000,
265 {0}},
266 {{Script_Arabic, 1, 1, 0, 0, 0, 0, { 1,0,0,0,0,0,0,0,0,0,0}},
267 {LANG_ARABIC, 0, 1, 0, 0, ARABIC_CHARSET, 0, 0, 0, 0, 0, 0, 1, 1, 0},
268 MS_MAKE_TAG('a','r','a','b'),
269 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
270 {{Script_Arabic_Numeric, 1, 1, 0, 0, 0, 0, { 1,0,0,0,0,0,0,0,0,0,0}},
271 {LANG_ARABIC, 1, 1, 0, 0, ARABIC_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
272 MS_MAKE_TAG('a','r','a','b'),
273 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
274 {{Script_Hebrew, 1, 1, 0, 0, 0, 0, { 1,0,0,0,0,0,0,0,0,0,0}},
275 {LANG_HEBREW, 0, 1, 0, 1, HEBREW_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
276 MS_MAKE_TAG('h','e','b','r'),
277 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
278 {{Script_Syriac, 1, 1, 0, 0, 0, 0, { 1,0,0,0,0,0,0,0,0,0,0}},
279 {LANG_SYRIAC, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 1, 0},
280 MS_MAKE_TAG('s','y','r','c'),
281 {'E','s','t','r','a','n','g','e','l','o',' ','E','d','e','s','s','a',0}},
282 {{Script_Persian, 1, 1, 0, 0, 0, 0, { 1,0,0,0,0,0,0,0,0,0,0}},
283 {LANG_PERSIAN, 1, 1, 0, 0, ARABIC_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
284 MS_MAKE_TAG('s','y','r','c'),
285 {'E','s','t','r','a','n','g','e','l','o',' ','E','d','e','s','s','a',0}},
286 {{Script_Thaana, 1, 1, 0, 0, 0, 0, { 1,0,0,0,0,0,0,0,0,0,0}},
287 {LANG_DIVEHI, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
288 MS_MAKE_TAG('t','h','a','a'),
289 {'M','V',' ','B','o','l','i',0}},
290 {{Script_Greek, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
291 {LANG_GREEK, 0, 0, 0, 0, GREEK_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
292 MS_MAKE_TAG('g','r','e','k'),
293 {0}},
294 {{Script_Cyrillic, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
295 {LANG_RUSSIAN, 0, 0, 0, 0, RUSSIAN_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
296 MS_MAKE_TAG('c','y','r','l'),
297 {0}},
298 {{Script_Armenian, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
299 {LANG_ARMENIAN, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
300 MS_MAKE_TAG('a','r','m','n'),
301 {'S','y','l','f','a','e','n',0}},
302 {{Script_Georgian, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
303 {LANG_GEORGIAN, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
304 MS_MAKE_TAG('g','e','o','r'),
305 {'S','y','l','f','a','e','n',0}},
306 {{Script_Sinhala, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
307 {LANG_SINHALESE, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
308 MS_MAKE_TAG('s','i','n','h'),
309 {'I','s','k','o','o','l','a',' ','P','o','t','a',0}},
310 {{Script_Tibetan, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
311 {LANG_TIBETAN, 0, 1, 1, 1, DEFAULT_CHARSET, 0, 0, 1, 0, 1, 0, 0, 0, 0},
312 MS_MAKE_TAG('t','i','b','t'),
313 {'M','i','c','r','o','s','o','f','t',' ','H','i','m','a','l','a','y','a',0}},
314 {{Script_Tibetan_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
315 {LANG_TIBETAN, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
316 MS_MAKE_TAG('t','i','b','t'),
317 {'M','i','c','r','o','s','o','f','t',' ','H','i','m','a','l','a','y','a',0}},
318 {{Script_Phags_pa, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
319 {LANG_MONGOLIAN, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
320 MS_MAKE_TAG('p','h','a','g'),
321 {'M','i','c','r','o','s','o','f','t',' ','P','h','a','g','s','P','a',0}},
322 {{Script_Thai, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
323 {LANG_THAI, 0, 1, 1, 1, THAI_CHARSET, 0, 0, 1, 0, 1, 0, 0, 0, 1},
324 MS_MAKE_TAG('t','h','a','i'),
325 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
326 {{Script_Thai_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
327 {LANG_THAI, 1, 1, 0, 0, THAI_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
328 MS_MAKE_TAG('t','h','a','i'),
329 {'M','i','c','r','o','s','o','f','t',' ','S','a','n','s',' ','S','e','r','i','f',0}},
330 {{Script_Lao, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
331 {LANG_LAO, 0, 1, 1, 1, DEFAULT_CHARSET, 0, 0, 1, 0, 1, 0, 0, 0, 0},
332 MS_MAKE_TAG('l','a','o',' '),
333 {'D','o','k','C','h','a','m','p','a',0}},
334 {{Script_Lao_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
335 {LANG_LAO, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
336 MS_MAKE_TAG('l','a','o',' '),
337 {'D','o','k','C','h','a','m','p','a',0}},
338 {{Script_Devanagari, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
339 {LANG_HINDI, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
340 MS_MAKE_TAG('d','e','v','a'),
341 {'M','a','n','g','a','l',0}},
342 {{Script_Devanagari_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
343 {LANG_HINDI, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
344 MS_MAKE_TAG('d','e','v','a'),
345 {'M','a','n','g','a','l',0}},
346 {{Script_Bengali, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
347 {LANG_BENGALI, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
348 MS_MAKE_TAG('b','e','n','g'),
349 {'V','r','i','n','d','a',0}},
350 {{Script_Bengali_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
351 {LANG_BENGALI, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
352 MS_MAKE_TAG('b','e','n','g'),
353 {'V','r','i','n','d','a',0}},
354 {{Script_Bengali_Currency, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
355 {LANG_BENGALI, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
356 MS_MAKE_TAG('b','e','n','g'),
357 {'V','r','i','n','d','a',0}},
358 {{Script_Gurmukhi, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
359 {LANG_PUNJABI, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
360 MS_MAKE_TAG('g','u','r','u'),
361 {'R','a','a','v','i',0}},
362 {{Script_Gurmukhi_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
363 {LANG_PUNJABI, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
364 MS_MAKE_TAG('g','u','r','u'),
365 {'R','a','a','v','i',0}},
366 {{Script_Gujarati, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
367 {LANG_GUJARATI, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
368 MS_MAKE_TAG('g','u','j','r'),
369 {'S','h','r','u','t','i',0}},
370 {{Script_Gujarati_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
371 {LANG_GUJARATI, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
372 MS_MAKE_TAG('g','u','j','r'),
373 {'S','h','r','u','t','i',0}},
374 {{Script_Gujarati_Currency, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
375 {LANG_GUJARATI, 0, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
376 MS_MAKE_TAG('g','u','j','r'),
377 {'S','h','r','u','t','i',0}},
378 {{Script_Oriya, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
379 {LANG_ORIYA, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
380 MS_MAKE_TAG('o','r','y','a'),
381 {'K','a','l','i','n','g','a',0}},
382 {{Script_Oriya_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
383 {LANG_ORIYA, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
384 MS_MAKE_TAG('o','r','y','a'),
385 {'K','a','l','i','n','g','a',0}},
386 {{Script_Tamil, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
387 {LANG_TAMIL, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
388 MS_MAKE_TAG('t','a','m','l'),
389 {'L','a','t','h','a',0}},
390 {{Script_Tamil_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
391 {LANG_TAMIL, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
392 MS_MAKE_TAG('t','a','m','l'),
393 {'L','a','t','h','a',0}},
394 {{Script_Telugu, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
395 {LANG_TELUGU, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
396 MS_MAKE_TAG('t','e','l','u'),
397 {'G','a','u','t','a','m','i',0}},
398 {{Script_Telugu_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
399 {LANG_TELUGU, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
400 MS_MAKE_TAG('t','e','l','u'),
401 {'G','a','u','t','a','m','i',0}},
402 {{Script_Kannada, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
403 {LANG_KANNADA, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
404 MS_MAKE_TAG('k','n','d','a'),
405 {'T','u','n','g','a',0}},
406 {{Script_Kannada_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
407 {LANG_KANNADA, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
408 MS_MAKE_TAG('k','n','d','a'),
409 {'T','u','n','g','a',0}},
410 {{Script_Malayalam, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
411 {LANG_MALAYALAM, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
412 MS_MAKE_TAG('m','l','y','m'),
413 {'K','a','r','t','i','k','a',0}},
414 {{Script_Malayalam_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
415 {LANG_MALAYALAM, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
416 MS_MAKE_TAG('m','l','y','m'),
417 {'K','a','r','t','i','k','a',0}},
418 {{Script_Diacritical, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
419 {LANG_ENGLISH, 0, 1, 0, 1, ANSI_CHARSET, 0, 0, 0, 0, 0, 1, 1, 0, 0},
420 0x00000000,
421 {0}},
422 {{Script_Punctuation2, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
423 {LANG_ENGLISH, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
424 MS_MAKE_TAG('l','a','t','n'),
425 {0}},
426 {{Script_Numeric2, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
427 {LANG_ENGLISH, 1, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
428 0x00000000,
429 {0}},
430 {{Script_Myanmar, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
431 {0x55, 0, 1, 1, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
432 MS_MAKE_TAG('m','y','m','r'),
433 {0}},
434 {{Script_Myanmar_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
435 {0x55, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
436 MS_MAKE_TAG('m','y','m','r'),
437 {0}},
438 {{Script_Tai_Le, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
439 {0, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
440 MS_MAKE_TAG('t','a','l','e'),
441 {'M','i','c','r','o','s','o','f','t',' ','T','a','i',' ','L','e'}},
442 {{Script_New_Tai_Lue, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
443 {0, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
444 MS_MAKE_TAG('t','a','l','u'),
445 {'M','i','c','r','o','s','o','f','t',' ','N','e','w',' ','T','a','i',' ','L','u','e'}},
446 {{Script_New_Tai_Lue_Numeric, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
447 {0, 0, 1, 0, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
448 MS_MAKE_TAG('t','a','l','u'),
449 {'M','i','c','r','o','s','o','f','t',' ','N','e','w',' ','T','a','i',' ','L','u','e'}},
450 {{Script_Khmer, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
451 {0x53, 0, 1, 1, 1, DEFAULT_CHARSET, 0, 0, 0, 0, 1, 0, 0, 0, 0},
452 MS_MAKE_TAG('k','h','m','r'),
453 {'D','a','u','n','P','e','n','h'}},
454 {{Script_Khmer, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
455 {0x53, 1, 1, 0, 0, DEFAULT_CHARSET, 0, 0, 0, 0, 0, 0, 0, 0, 0},
456 MS_MAKE_TAG('k','h','m','r'),
457 {'D','a','u','n','P','e','n','h'}},
458 {{Script_CJK_Han, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
459 {LANG_ENGLISH, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
460 MS_MAKE_TAG('h','a','n','i'),
461 {0}},
462 {{Script_Ideograph, 0, 0, 0, 0, 0, 0, { 0,0,0,0,0,0,0,0,0,0,0}},
463 {LANG_ENGLISH, 0, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, 0, 0, 1, 0, 0},
464 MS_MAKE_TAG('h','a','n','i'),
465 {0}},
468 static const SCRIPT_PROPERTIES *script_props[] =
470 &scriptInformation[0].props, &scriptInformation[1].props,
471 &scriptInformation[2].props, &scriptInformation[3].props,
472 &scriptInformation[4].props, &scriptInformation[5].props,
473 &scriptInformation[6].props, &scriptInformation[7].props,
474 &scriptInformation[8].props, &scriptInformation[9].props,
475 &scriptInformation[10].props, &scriptInformation[11].props,
476 &scriptInformation[12].props, &scriptInformation[13].props,
477 &scriptInformation[14].props, &scriptInformation[15].props,
478 &scriptInformation[16].props, &scriptInformation[17].props,
479 &scriptInformation[18].props, &scriptInformation[19].props,
480 &scriptInformation[20].props, &scriptInformation[21].props,
481 &scriptInformation[22].props, &scriptInformation[23].props,
482 &scriptInformation[24].props, &scriptInformation[25].props,
483 &scriptInformation[26].props, &scriptInformation[27].props,
484 &scriptInformation[28].props, &scriptInformation[29].props,
485 &scriptInformation[30].props, &scriptInformation[31].props,
486 &scriptInformation[32].props, &scriptInformation[33].props,
487 &scriptInformation[34].props, &scriptInformation[35].props,
488 &scriptInformation[36].props, &scriptInformation[37].props,
489 &scriptInformation[38].props, &scriptInformation[39].props,
490 &scriptInformation[40].props, &scriptInformation[41].props,
491 &scriptInformation[42].props, &scriptInformation[43].props,
492 &scriptInformation[44].props, &scriptInformation[45].props,
493 &scriptInformation[46].props, &scriptInformation[47].props,
494 &scriptInformation[48].props, &scriptInformation[49].props,
495 &scriptInformation[50].props, &scriptInformation[51].props,
496 &scriptInformation[52].props, &scriptInformation[53].props,
497 &scriptInformation[54].props, &scriptInformation[55].props
500 typedef struct {
501 ScriptCache *sc;
502 int numGlyphs;
503 WORD* glyphs;
504 WORD* pwLogClust;
505 int* piAdvance;
506 SCRIPT_VISATTR* psva;
507 GOFFSET* pGoffset;
508 ABC* abc;
509 int iMaxPosX;
510 HFONT fallbackFont;
511 } StringGlyphs;
513 typedef struct {
514 HDC hdc;
515 DWORD dwFlags;
516 BOOL invalid;
517 int clip_len;
518 int cItems;
519 int cMaxGlyphs;
520 SCRIPT_ITEM* pItem;
521 int numItems;
522 StringGlyphs* glyphs;
523 SCRIPT_LOGATTR* logattrs;
524 SIZE* sz;
525 int* logical2visual;
526 } StringAnalysis;
528 static inline void *heap_alloc(SIZE_T size)
530 return HeapAlloc(GetProcessHeap(), 0, size);
533 static inline void *heap_alloc_zero(SIZE_T size)
535 return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);
538 static inline void *heap_realloc_zero(LPVOID mem, SIZE_T size)
540 return HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, mem, size);
543 static inline BOOL heap_free(LPVOID mem)
545 return HeapFree(GetProcessHeap(), 0, mem);
548 static inline WCHAR get_cache_default_char(SCRIPT_CACHE *psc)
550 return ((ScriptCache *)*psc)->tm.tmDefaultChar;
553 static inline LONG get_cache_height(SCRIPT_CACHE *psc)
555 return ((ScriptCache *)*psc)->tm.tmHeight;
558 static inline BYTE get_cache_pitch_family(SCRIPT_CACHE *psc)
560 return ((ScriptCache *)*psc)->tm.tmPitchAndFamily;
563 static inline WORD get_cache_glyph(SCRIPT_CACHE *psc, WCHAR c)
565 WORD *block = ((ScriptCache *)*psc)->glyphs[c >> GLYPH_BLOCK_SHIFT];
567 if (!block) return 0;
568 return block[c & GLYPH_BLOCK_MASK];
571 static inline WORD set_cache_glyph(SCRIPT_CACHE *psc, WCHAR c, WORD glyph)
573 WORD **block = &((ScriptCache *)*psc)->glyphs[c >> GLYPH_BLOCK_SHIFT];
575 if (!*block && !(*block = heap_alloc_zero(sizeof(WORD) * GLYPH_BLOCK_SIZE))) return 0;
576 return ((*block)[c & GLYPH_BLOCK_MASK] = glyph);
579 static inline BOOL get_cache_glyph_widths(SCRIPT_CACHE *psc, WORD glyph, ABC *abc)
581 static const ABC nil;
582 ABC *block = ((ScriptCache *)*psc)->widths[glyph >> GLYPH_BLOCK_SHIFT];
584 if (!block || !memcmp(&block[glyph & GLYPH_BLOCK_MASK], &nil, sizeof(ABC))) return FALSE;
585 memcpy(abc, &block[glyph & GLYPH_BLOCK_MASK], sizeof(ABC));
586 return TRUE;
589 static inline BOOL set_cache_glyph_widths(SCRIPT_CACHE *psc, WORD glyph, ABC *abc)
591 ABC **block = &((ScriptCache *)*psc)->widths[glyph >> GLYPH_BLOCK_SHIFT];
593 if (!*block && !(*block = heap_alloc_zero(sizeof(ABC) * GLYPH_BLOCK_SIZE))) return FALSE;
594 memcpy(&(*block)[glyph & GLYPH_BLOCK_MASK], abc, sizeof(ABC));
595 return TRUE;
598 static HRESULT init_script_cache(const HDC hdc, SCRIPT_CACHE *psc)
600 ScriptCache *sc;
602 if (!psc) return E_INVALIDARG;
603 if (*psc) return S_OK;
604 if (!hdc) return E_PENDING;
606 if (!(sc = heap_alloc_zero(sizeof(ScriptCache)))) return E_OUTOFMEMORY;
607 if (!GetTextMetricsW(hdc, &sc->tm))
609 heap_free(sc);
610 return E_INVALIDARG;
612 if (!GetObjectW(GetCurrentObject(hdc, OBJ_FONT), sizeof(LOGFONTW), &sc->lf))
614 heap_free(sc);
615 return E_INVALIDARG;
617 sc->sfnt = (GetFontData(hdc, MS_MAKE_TAG('h','e','a','d'), 0, NULL, 0)!=GDI_ERROR);
618 *psc = sc;
619 TRACE("<- %p\n", sc);
620 return S_OK;
623 static WCHAR mirror_char( WCHAR ch )
625 extern const WCHAR wine_mirror_map[];
626 return ch + wine_mirror_map[wine_mirror_map[ch >> 8] + (ch & 0xff)];
629 static WORD get_char_script( WCHAR ch)
631 static const WCHAR latin_punc[] = {'#','$','&','\'',',',';','<','>','?','@','\\','^','_','`','{','|','}','~', 0x00a0, 0};
632 WORD type = 0;
633 int i;
635 if (ch == 0xc || ch == 0x20 || ch == 0x202f)
636 return Script_CR;
638 /* These punctuation are separated out as Latin punctuation */
639 if (strchrW(latin_punc,ch))
640 return Script_Punctuation2;
642 /* These chars are itemized as Punctuation by Windows */
643 if (ch == 0x2212 || ch == 0x2044)
644 return Script_Punctuation;
646 GetStringTypeW(CT_CTYPE1, &ch, 1, &type);
648 if (type == 0)
649 return SCRIPT_UNDEFINED;
651 if (type & C1_CNTRL)
652 return Script_Control;
654 i = 0;
657 if (ch < scriptRanges[i].rangeFirst || scriptRanges[i].script == SCRIPT_UNDEFINED)
658 break;
660 if (ch >= scriptRanges[i].rangeFirst && ch <= scriptRanges[i].rangeLast)
662 if (scriptRanges[i].numericScript && type & C1_DIGIT)
663 return scriptRanges[i].numericScript;
664 if (scriptRanges[i].punctScript && type & C1_PUNCT)
665 return scriptRanges[i].punctScript;
666 return scriptRanges[i].script;
668 i++;
669 } while (1);
671 return SCRIPT_UNDEFINED;
674 /***********************************************************************
675 * DllMain
678 BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpv)
680 switch(fdwReason)
682 case DLL_PROCESS_ATTACH:
683 DisableThreadLibraryCalls(hInstDLL);
684 break;
685 case DLL_PROCESS_DETACH:
686 break;
688 return TRUE;
691 /***********************************************************************
692 * ScriptFreeCache (USP10.@)
694 * Free a script cache.
696 * PARAMS
697 * psc [I/O] Script cache.
699 * RETURNS
700 * Success: S_OK
701 * Failure: Non-zero HRESULT value.
703 HRESULT WINAPI ScriptFreeCache(SCRIPT_CACHE *psc)
705 TRACE("%p\n", psc);
707 if (psc && *psc)
709 unsigned int i;
710 for (i = 0; i < GLYPH_MAX / GLYPH_BLOCK_SIZE; i++)
712 heap_free(((ScriptCache *)*psc)->glyphs[i]);
713 heap_free(((ScriptCache *)*psc)->widths[i]);
715 heap_free(((ScriptCache *)*psc)->GSUB_Table);
716 heap_free(((ScriptCache *)*psc)->GDEF_Table);
717 heap_free(((ScriptCache *)*psc)->features);
718 heap_free(*psc);
719 *psc = NULL;
721 return S_OK;
724 /***********************************************************************
725 * ScriptGetProperties (USP10.@)
727 * Retrieve a list of script properties.
729 * PARAMS
730 * props [I] Pointer to an array of SCRIPT_PROPERTIES pointers.
731 * num [I] Pointer to the number of scripts.
733 * RETURNS
734 * Success: S_OK
735 * Failure: Non-zero HRESULT value.
737 * NOTES
738 * Behaviour matches WinXP.
740 HRESULT WINAPI ScriptGetProperties(const SCRIPT_PROPERTIES ***props, int *num)
742 TRACE("(%p,%p)\n", props, num);
744 if (!props && !num) return E_INVALIDARG;
746 if (num) *num = sizeof(script_props)/sizeof(script_props[0]);
747 if (props) *props = script_props;
749 return S_OK;
752 /***********************************************************************
753 * ScriptGetFontProperties (USP10.@)
755 * Get information on special glyphs.
757 * PARAMS
758 * hdc [I] Device context.
759 * psc [I/O] Opaque pointer to a script cache.
760 * sfp [O] Font properties structure.
762 HRESULT WINAPI ScriptGetFontProperties(HDC hdc, SCRIPT_CACHE *psc, SCRIPT_FONTPROPERTIES *sfp)
764 HRESULT hr;
766 TRACE("%p,%p,%p\n", hdc, psc, sfp);
768 if (!sfp) return E_INVALIDARG;
769 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
771 if (sfp->cBytes != sizeof(SCRIPT_FONTPROPERTIES))
772 return E_INVALIDARG;
774 /* return something sensible? */
775 sfp->wgBlank = 0;
776 sfp->wgDefault = get_cache_default_char(psc);
777 sfp->wgInvalid = 0;
778 sfp->wgKashida = 0xffff;
779 sfp->iKashidaWidth = 0;
781 return S_OK;
784 /***********************************************************************
785 * ScriptRecordDigitSubstitution (USP10.@)
787 * Record digit substitution settings for a given locale.
789 * PARAMS
790 * locale [I] Locale identifier.
791 * sds [I] Structure to record substitution settings.
793 * RETURNS
794 * Success: S_OK
795 * Failure: E_POINTER if sds is NULL, E_INVALIDARG otherwise.
797 * SEE ALSO
798 * http://blogs.msdn.com/michkap/archive/2006/02/22/536877.aspx
800 HRESULT WINAPI ScriptRecordDigitSubstitution(LCID locale, SCRIPT_DIGITSUBSTITUTE *sds)
802 DWORD plgid, sub;
804 TRACE("0x%x, %p\n", locale, sds);
806 /* This implementation appears to be correct for all languages, but it's
807 * not clear if sds->DigitSubstitute is ever set to anything except
808 * CONTEXT or NONE in reality */
810 if (!sds) return E_POINTER;
812 locale = ConvertDefaultLocale(locale);
814 if (!IsValidLocale(locale, LCID_INSTALLED))
815 return E_INVALIDARG;
817 plgid = PRIMARYLANGID(LANGIDFROMLCID(locale));
818 sds->TraditionalDigitLanguage = plgid;
820 if (plgid == LANG_ARABIC || plgid == LANG_FARSI)
821 sds->NationalDigitLanguage = plgid;
822 else
823 sds->NationalDigitLanguage = LANG_ENGLISH;
825 if (!GetLocaleInfoW(locale, LOCALE_IDIGITSUBSTITUTION | LOCALE_RETURN_NUMBER,
826 (LPWSTR)&sub, sizeof(sub)/sizeof(WCHAR))) return E_INVALIDARG;
828 switch (sub)
830 case 0:
831 if (plgid == LANG_ARABIC || plgid == LANG_FARSI)
832 sds->DigitSubstitute = SCRIPT_DIGITSUBSTITUTE_CONTEXT;
833 else
834 sds->DigitSubstitute = SCRIPT_DIGITSUBSTITUTE_NONE;
835 break;
836 case 1:
837 sds->DigitSubstitute = SCRIPT_DIGITSUBSTITUTE_NONE;
838 break;
839 case 2:
840 sds->DigitSubstitute = SCRIPT_DIGITSUBSTITUTE_NATIONAL;
841 break;
842 default:
843 sds->DigitSubstitute = SCRIPT_DIGITSUBSTITUTE_TRADITIONAL;
844 break;
847 sds->dwReserved = 0;
848 return S_OK;
851 /***********************************************************************
852 * ScriptApplyDigitSubstitution (USP10.@)
854 * Apply digit substitution settings.
856 * PARAMS
857 * sds [I] Structure with recorded substitution settings.
858 * sc [I] Script control structure.
859 * ss [I] Script state structure.
861 * RETURNS
862 * Success: S_OK
863 * Failure: E_INVALIDARG if sds is invalid. Otherwise an HRESULT.
865 HRESULT WINAPI ScriptApplyDigitSubstitution(const SCRIPT_DIGITSUBSTITUTE *sds,
866 SCRIPT_CONTROL *sc, SCRIPT_STATE *ss)
868 SCRIPT_DIGITSUBSTITUTE psds;
870 TRACE("%p, %p, %p\n", sds, sc, ss);
872 if (!sc || !ss) return E_POINTER;
873 if (!sds)
875 sds = &psds;
876 if (ScriptRecordDigitSubstitution(LOCALE_USER_DEFAULT, &psds) != S_OK)
877 return E_INVALIDARG;
880 sc->uDefaultLanguage = LANG_ENGLISH;
881 sc->fContextDigits = 0;
882 ss->fDigitSubstitute = 0;
884 switch (sds->DigitSubstitute) {
885 case SCRIPT_DIGITSUBSTITUTE_CONTEXT:
886 case SCRIPT_DIGITSUBSTITUTE_NATIONAL:
887 case SCRIPT_DIGITSUBSTITUTE_NONE:
888 case SCRIPT_DIGITSUBSTITUTE_TRADITIONAL:
889 return S_OK;
890 default:
891 return E_INVALIDARG;
895 static inline BOOL is_indic(WORD script)
897 return (script >= Script_Devanagari && script <= Script_Malayalam_Numeric);
900 static inline WORD base_indic(WORD script)
902 switch (script)
904 case Script_Devanagari:
905 case Script_Devanagari_Numeric: return Script_Devanagari;
906 case Script_Bengali:
907 case Script_Bengali_Numeric:
908 case Script_Bengali_Currency: return Script_Bengali;
909 case Script_Gurmukhi:
910 case Script_Gurmukhi_Numeric: return Script_Gurmukhi;
911 case Script_Gujarati:
912 case Script_Gujarati_Numeric:
913 case Script_Gujarati_Currency: return Script_Gujarati;
914 case Script_Oriya:
915 case Script_Oriya_Numeric: return Script_Oriya;
916 case Script_Tamil:
917 case Script_Tamil_Numeric: return Script_Tamil;
918 case Script_Telugu:
919 case Script_Telugu_Numeric: return Script_Telugu;
920 case Script_Kannada:
921 case Script_Kannada_Numeric: return Script_Kannada;
922 case Script_Malayalam:
923 case Script_Malayalam_Numeric: return Script_Malayalam;
924 default:
925 return -1;
929 /***********************************************************************
930 * ScriptItemizeOpenType (USP10.@)
932 * Split a Unicode string into shapeable parts.
934 * PARAMS
935 * pwcInChars [I] String to split.
936 * cInChars [I] Number of characters in pwcInChars.
937 * cMaxItems [I] Maximum number of items to return.
938 * psControl [I] Pointer to a SCRIPT_CONTROL structure.
939 * psState [I] Pointer to a SCRIPT_STATE structure.
940 * pItems [O] Buffer to receive SCRIPT_ITEM structures.
941 * pScriptTags [O] Buffer to receive OPENTYPE_TAGs.
942 * pcItems [O] Number of script items returned.
944 * RETURNS
945 * Success: S_OK
946 * Failure: Non-zero HRESULT value.
948 HRESULT WINAPI ScriptItemizeOpenType(const WCHAR *pwcInChars, int cInChars, int cMaxItems,
949 const SCRIPT_CONTROL *psControl, const SCRIPT_STATE *psState,
950 SCRIPT_ITEM *pItems, OPENTYPE_TAG *pScriptTags, int *pcItems)
953 #define Numeric_space 0x0020
954 #define ZWNJ 0x200C
955 #define ZWJ 0x200D
957 int cnt = 0, index = 0, str = 0;
958 int New_Script = -1;
959 int i;
960 WORD *levels = NULL;
961 WORD *strength = NULL;
962 WORD *scripts = NULL;
963 WORD baselevel = 0;
964 BOOL new_run;
965 WORD last_indic = -1;
966 WORD layoutRTL = 0;
967 BOOL forceLevels = FALSE;
969 TRACE("%s,%d,%d,%p,%p,%p,%p\n", debugstr_wn(pwcInChars, cInChars), cInChars, cMaxItems,
970 psControl, psState, pItems, pcItems);
972 if (!pwcInChars || !cInChars || !pItems || cMaxItems < 2)
973 return E_INVALIDARG;
975 scripts = heap_alloc(cInChars * sizeof(WORD));
976 if (!scripts)
977 return E_OUTOFMEMORY;
979 for (i = 0; i < cInChars; i++)
981 scripts[i] = get_char_script(pwcInChars[i]);
982 /* Devanagari danda (U+0964) and double danda (U+0965) are used for
983 all Indic scripts */
984 if ((pwcInChars[i] == 0x964 || pwcInChars[i] ==0x965) && last_indic > 0)
985 scripts[i] = last_indic;
986 else if (is_indic(scripts[i]))
987 last_indic = base_indic(scripts[i]);
989 /* Some unicode points (Zero Width Space U+200B -
990 Right-to-Left Mark U+200F) will force us into bidi mode */
991 if (!forceLevels && pwcInChars[i] >= 0x200B && pwcInChars[i] <= 0x200F)
992 forceLevels = TRUE;
994 /* Diacritical marks merge with other scripts */
995 if (scripts[i] == Script_Diacritical && i > 0)
996 scripts[i] = scripts[i-1];
999 for (i = 0; i < cInChars; i++)
1001 /* Joiners get merged preferencially right */
1002 if (i > 0 && (pwcInChars[i] == ZWJ || pwcInChars[i] == ZWNJ))
1004 int j;
1005 if (i+1 == cInChars)
1006 scripts[i] = scripts[i-1];
1007 else
1009 for (j = i+1; j < cInChars; j++)
1011 if (pwcInChars[j] != ZWJ && pwcInChars[j] != ZWNJ && pwcInChars[j] != Numeric_space)
1013 scripts[i] = scripts[j];
1014 break;
1021 if (psState && psControl)
1023 levels = heap_alloc_zero(cInChars * sizeof(WORD));
1024 if (!levels)
1026 heap_free(scripts);
1027 return E_OUTOFMEMORY;
1030 BIDI_DetermineLevels(pwcInChars, cInChars, psState, psControl, levels);
1031 baselevel = levels[0];
1032 for (i = 0; i < cInChars; i++)
1033 if (levels[i]!=levels[0])
1034 break;
1035 if (i >= cInChars && !odd(baselevel) && !odd(psState->uBidiLevel) && !forceLevels)
1037 heap_free(levels);
1038 levels = NULL;
1040 else
1042 BOOL inNumber = FALSE;
1043 static WCHAR math_punc[] = {'#','$','%','+',',','-','.','/',':',0x2212, 0x2044, 0x00a0,0};
1045 strength = heap_alloc_zero(cInChars * sizeof(WORD));
1046 if (!strength)
1048 heap_free(scripts);
1049 heap_free(levels);
1050 return E_OUTOFMEMORY;
1052 BIDI_GetStrengths(pwcInChars, cInChars, psControl, strength);
1054 /* We currently mis-level leading Diacriticals */
1055 if (scripts[0] == Script_Diacritical)
1056 for (i = 0; i < cInChars && scripts[0] == Script_Diacritical; i++)
1058 levels[i] = odd(levels[i])?levels[i]+1:levels[i];
1059 strength[i] = BIDI_STRONG;
1062 for (i = 0; i < cInChars; i++)
1064 /* Script_Numeric and select puncuation at level 0 get bumped to level 2 */
1065 if ((levels[i] == 0 || (odd(psState->uBidiLevel) && levels[i] == psState->uBidiLevel+1)) && inNumber && strchrW(math_punc,pwcInChars[i]))
1067 scripts[i] = Script_Numeric;
1068 levels[i] = 2;
1070 else if ((levels[i] == 0 || (odd(psState->uBidiLevel) && levels[i] == psState->uBidiLevel+1)) && scripts[i] == Script_Numeric)
1072 levels[i] = 2;
1073 inNumber = TRUE;
1075 else
1076 inNumber = FALSE;
1078 /* Joiners get merged preferencially right */
1079 if (i > 0 && (pwcInChars[i] == ZWJ || pwcInChars[i] == ZWNJ))
1081 int j;
1082 if (i+1 == cInChars && levels[i-1] == levels[i])
1083 strength[i] = strength[i-1];
1084 else
1085 for (j = i+1; j < cInChars && levels[i] == levels[j]; j++)
1086 if (pwcInChars[j] != ZWJ && pwcInChars[j] != ZWNJ && pwcInChars[j] != Numeric_space)
1088 strength[i] = strength[j];
1089 break;
1093 if (psControl->fMergeNeutralItems)
1095 /* Merge the neutrals */
1096 for (i = 0; i < cInChars; i++)
1098 if (strength[i] == BIDI_NEUTRAL || strength[i] == BIDI_WEAK)
1100 int j;
1101 for (j = i; j > 0; j--)
1103 if (levels[i] != levels[j])
1104 break;
1105 if ((strength[j] == BIDI_STRONG) || (strength[i] == BIDI_NEUTRAL && strength[j] == BIDI_WEAK))
1107 scripts[i] = scripts[j];
1108 strength[i] = strength[j];
1109 break;
1113 /* Try going the other way */
1114 if (strength[i] == BIDI_NEUTRAL || strength[i] == BIDI_WEAK)
1116 int j;
1117 for (j = i; j < cInChars; j++)
1119 if (levels[i] != levels[j])
1120 break;
1121 if ((strength[j] == BIDI_STRONG) || (strength[i] == BIDI_NEUTRAL && strength[j] == BIDI_WEAK))
1123 scripts[i] = scripts[j];
1124 strength[i] = strength[j];
1125 break;
1134 while ((!levels || (levels && levels[cnt+1] == levels[0])) && (pwcInChars[cnt] == Numeric_space) && cnt < cInChars)
1135 cnt++;
1137 if (cnt == cInChars) /* All Spaces */
1139 cnt = 0;
1140 New_Script = scripts[cnt];
1143 pItems[index].iCharPos = 0;
1144 pItems[index].a = scriptInformation[scripts[cnt]].a;
1145 pScriptTags[index] = scriptInformation[scripts[cnt]].scriptTag;
1147 if (strength && strength[cnt] == BIDI_STRONG)
1148 str = strength[cnt];
1149 else if (strength)
1150 str = strength[0];
1152 cnt = 0;
1154 if (levels)
1156 if (strength[cnt] == BIDI_STRONG)
1157 layoutRTL = (odd(levels[cnt]))?1:0;
1158 else
1159 layoutRTL = (psState->uBidiLevel || odd(levels[cnt]))?1:0;
1160 pItems[index].a.fRTL = odd(levels[cnt]);
1161 pItems[index].a.fLayoutRTL = layoutRTL;
1162 pItems[index].a.s.uBidiLevel = levels[cnt];
1164 else if (!pItems[index].a.s.uBidiLevel)
1166 layoutRTL = (odd(baselevel))?1:0;
1167 pItems[index].a.s.uBidiLevel = baselevel;
1168 pItems[index].a.fLayoutRTL = odd(baselevel);
1169 pItems[index].a.fRTL = odd(baselevel);
1172 TRACE("New_Level=%i New_Strength=%i New_Script=%d, eScript=%d index=%d cnt=%d iCharPos=%d\n",
1173 levels?levels[cnt]:-1, str, New_Script, pItems[index].a.eScript, index, cnt,
1174 pItems[index].iCharPos);
1176 for (cnt=1; cnt < cInChars; cnt++)
1178 if(pwcInChars[cnt] != Numeric_space)
1179 New_Script = scripts[cnt];
1180 else if (levels)
1182 int j = 1;
1183 while (cnt + j < cInChars - 1 && pwcInChars[cnt+j] == Numeric_space && levels[cnt] == levels[cnt+j])
1184 j++;
1185 if (cnt + j < cInChars && levels[cnt] == levels[cnt+j])
1186 New_Script = scripts[cnt+j];
1187 else
1188 New_Script = scripts[cnt];
1191 new_run = FALSE;
1192 /* merge space strengths*/
1193 if (strength && strength[cnt] == BIDI_STRONG && str != BIDI_STRONG && New_Script == pItems[index].a.eScript)
1194 str = BIDI_STRONG;
1196 if (strength && strength[cnt] == BIDI_NEUTRAL && str == BIDI_STRONG && pwcInChars[cnt] != Numeric_space && New_Script == pItems[index].a.eScript)
1197 str = BIDI_NEUTRAL;
1199 /* changes in level */
1200 if (levels && (levels[cnt] != pItems[index].a.s.uBidiLevel))
1202 TRACE("Level break(%i/%i)\n",pItems[index].a.s.uBidiLevel,levels[cnt]);
1203 new_run = TRUE;
1205 /* changes in strength */
1206 else if (strength && pwcInChars[cnt] != Numeric_space && str != strength[cnt])
1208 TRACE("Strength break (%i/%i)\n",str,strength[cnt]);
1209 new_run = TRUE;
1211 /* changes in script */
1212 else if (((pwcInChars[cnt] != Numeric_space) && (New_Script != -1) && (New_Script != pItems[index].a.eScript)) || (New_Script == Script_Control))
1214 TRACE("Script break(%i/%i)\n",pItems[index].a.eScript,New_Script);
1215 new_run = TRUE;
1218 if (!new_run && strength && str == BIDI_STRONG)
1220 layoutRTL = odd(levels[cnt])?1:0;
1221 pItems[index].a.fLayoutRTL = layoutRTL;
1224 if (new_run)
1226 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);
1228 index++;
1229 if (index+1 > cMaxItems)
1230 return E_OUTOFMEMORY;
1232 if (strength)
1233 str = strength[cnt];
1235 pItems[index].iCharPos = cnt;
1236 memset(&pItems[index].a, 0, sizeof(SCRIPT_ANALYSIS));
1238 pItems[index].a = scriptInformation[New_Script].a;
1239 pScriptTags[index] = scriptInformation[New_Script].scriptTag;
1240 if (levels)
1242 if (levels[cnt] == 0)
1243 layoutRTL = 0;
1244 else
1245 layoutRTL = (layoutRTL || odd(levels[cnt]))?1:0;
1246 pItems[index].a.fRTL = odd(levels[cnt]);
1247 pItems[index].a.fLayoutRTL = layoutRTL;
1248 pItems[index].a.s.uBidiLevel = levels[cnt];
1250 else if (!pItems[index].a.s.uBidiLevel)
1252 pItems[index].a.s.uBidiLevel = baselevel;
1253 pItems[index].a.fLayoutRTL = layoutRTL;
1254 pItems[index].a.fRTL = odd(baselevel);
1257 TRACE("index=%d cnt=%d iCharPos=%d\n", index, cnt, pItems[index].iCharPos);
1261 /* While not strictly necessary according to the spec, make sure the n+1
1262 * item is set up to prevent random behaviour if the caller erroneously
1263 * checks the n+1 structure */
1264 index++;
1265 memset(&pItems[index].a, 0, sizeof(SCRIPT_ANALYSIS));
1267 TRACE("index=%d cnt=%d iCharPos=%d\n", index, cnt, pItems[index].iCharPos);
1269 /* Set one SCRIPT_STATE item being returned */
1270 if (index + 1 > cMaxItems) return E_OUTOFMEMORY;
1271 if (pcItems) *pcItems = index;
1273 /* Set SCRIPT_ITEM */
1274 pItems[index].iCharPos = cnt; /* the last item contains the ptr to the lastchar */
1275 heap_free(levels);
1276 heap_free(strength);
1277 heap_free(scripts);
1278 return S_OK;
1281 /***********************************************************************
1282 * ScriptItemize (USP10.@)
1284 * Split a Unicode string into shapeable parts.
1286 * PARAMS
1287 * pwcInChars [I] String to split.
1288 * cInChars [I] Number of characters in pwcInChars.
1289 * cMaxItems [I] Maximum number of items to return.
1290 * psControl [I] Pointer to a SCRIPT_CONTROL structure.
1291 * psState [I] Pointer to a SCRIPT_STATE structure.
1292 * pItems [O] Buffer to receive SCRIPT_ITEM structures.
1293 * pcItems [O] Number of script items returned.
1295 * RETURNS
1296 * Success: S_OK
1297 * Failure: Non-zero HRESULT value.
1299 HRESULT WINAPI ScriptItemize(const WCHAR *pwcInChars, int cInChars, int cMaxItems,
1300 const SCRIPT_CONTROL *psControl, const SCRIPT_STATE *psState,
1301 SCRIPT_ITEM *pItems, int *pcItems)
1303 OPENTYPE_TAG *discarded_tags;
1304 HRESULT res;
1306 discarded_tags = heap_alloc(cMaxItems * sizeof(OPENTYPE_TAG));
1307 if (!discarded_tags)
1308 return E_OUTOFMEMORY;
1309 res = ScriptItemizeOpenType(pwcInChars, cInChars, cMaxItems, psControl, psState, pItems, discarded_tags, pcItems);
1310 heap_free(discarded_tags);
1311 return res;
1314 static inline int getGivenTabWidth(ScriptCache *psc, SCRIPT_TABDEF *pTabdef, int charPos, int current_x)
1316 int defWidth;
1317 int cTabStops=0;
1318 INT *lpTabPos = NULL;
1319 INT nTabOrg = 0;
1320 INT x = 0;
1322 if (pTabdef)
1323 lpTabPos = pTabdef->pTabStops;
1325 if (pTabdef && pTabdef->iTabOrigin)
1327 if (pTabdef->iScale)
1328 nTabOrg = (pTabdef->iTabOrigin * pTabdef->iScale)/4;
1329 else
1330 nTabOrg = pTabdef->iTabOrigin * psc->tm.tmAveCharWidth;
1333 if (pTabdef)
1334 cTabStops = pTabdef->cTabStops;
1336 if (cTabStops == 1)
1338 if (pTabdef->iScale)
1339 defWidth = ((pTabdef->pTabStops[0])*pTabdef->iScale) / 4;
1340 else
1341 defWidth = (pTabdef->pTabStops[0])*psc->tm.tmAveCharWidth;
1342 cTabStops = 0;
1344 else
1345 defWidth = 8 * psc->tm.tmAveCharWidth;
1347 for (; cTabStops>0 ; lpTabPos++, cTabStops--)
1349 int position = *lpTabPos;
1350 if (position < 0)
1351 position = -1 * position;
1352 if (pTabdef->iScale)
1353 position = (position * pTabdef->iScale) / 4;
1354 else
1355 position = position * psc->tm.tmAveCharWidth;
1357 if( nTabOrg + position > current_x)
1359 if( *lpTabPos >= 0)
1361 /* a left aligned tab */
1362 x = (nTabOrg + *lpTabPos) - current_x;
1363 break;
1365 else
1367 FIXME("Negative tabstop\n");
1368 break;
1372 if ((!cTabStops) && (defWidth > 0))
1373 x =((((current_x - nTabOrg) / defWidth)+1) * defWidth) - current_x;
1374 else if ((!cTabStops) && (defWidth < 0))
1375 FIXME("TODO: Negative defWidth\n");
1377 return x;
1380 /***********************************************************************
1381 * Helper function for ScriptStringAnalyse
1383 static BOOL requires_fallback(HDC hdc, SCRIPT_CACHE *psc, SCRIPT_ANALYSIS *psa,
1384 const WCHAR *pwcInChars, int cChars )
1386 /* FIXME: When to properly fallback is still a bit of a mystery */
1387 WORD *glyphs;
1389 if (psa->fNoGlyphIndex)
1390 return FALSE;
1392 if (init_script_cache(hdc, psc) != S_OK)
1393 return FALSE;
1395 if (SHAPE_CheckFontForRequiredFeatures(hdc, (ScriptCache *)*psc, psa) != S_OK)
1396 return TRUE;
1398 glyphs = heap_alloc(sizeof(WORD) * cChars);
1399 if (!glyphs)
1400 return FALSE;
1401 if (ScriptGetCMap(hdc, psc, pwcInChars, cChars, 0, glyphs) != S_OK)
1403 heap_free(glyphs);
1404 return TRUE;
1406 heap_free(glyphs);
1408 return FALSE;
1411 static void find_fallback_font(DWORD scriptid, LPWSTR FaceName)
1413 HKEY hkey;
1415 if (!RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Uniscribe\\Fallback", &hkey))
1417 static const WCHAR szFmt[] = {'%','x',0};
1418 WCHAR value[10];
1419 DWORD count = LF_FACESIZE * sizeof(WCHAR);
1420 DWORD type;
1422 sprintfW(value, szFmt, scriptInformation[scriptid].scriptTag);
1423 if (RegQueryValueExW(hkey, value, 0, &type, (LPBYTE)FaceName, &count))
1424 lstrcpyW(FaceName,scriptInformation[scriptid].fallbackFont);
1425 RegCloseKey(hkey);
1427 else
1428 lstrcpyW(FaceName,scriptInformation[scriptid].fallbackFont);
1431 /***********************************************************************
1432 * ScriptStringAnalyse (USP10.@)
1435 HRESULT WINAPI ScriptStringAnalyse(HDC hdc, const void *pString, int cString,
1436 int cGlyphs, int iCharset, DWORD dwFlags,
1437 int iReqWidth, SCRIPT_CONTROL *psControl,
1438 SCRIPT_STATE *psState, const int *piDx,
1439 SCRIPT_TABDEF *pTabdef, const BYTE *pbInClass,
1440 SCRIPT_STRING_ANALYSIS *pssa)
1442 HRESULT hr = E_OUTOFMEMORY;
1443 StringAnalysis *analysis = NULL;
1444 SCRIPT_CONTROL sControl;
1445 SCRIPT_STATE sState;
1446 int i, num_items = 255;
1447 BYTE *BidiLevel;
1448 WCHAR *iString = NULL;
1450 TRACE("(%p,%p,%d,%d,%d,0x%x,%d,%p,%p,%p,%p,%p,%p)\n",
1451 hdc, pString, cString, cGlyphs, iCharset, dwFlags, iReqWidth,
1452 psControl, psState, piDx, pTabdef, pbInClass, pssa);
1454 if (iCharset != -1)
1456 FIXME("Only Unicode strings are supported\n");
1457 return E_INVALIDARG;
1459 if (cString < 1 || !pString) return E_INVALIDARG;
1460 if ((dwFlags & SSA_GLYPHS) && !hdc) return E_PENDING;
1462 if (!(analysis = heap_alloc_zero(sizeof(StringAnalysis)))) return E_OUTOFMEMORY;
1463 if (!(analysis->pItem = heap_alloc_zero(num_items * sizeof(SCRIPT_ITEM) + 1))) goto error;
1465 /* FIXME: handle clipping */
1466 analysis->clip_len = cString;
1467 analysis->hdc = hdc;
1468 analysis->dwFlags = dwFlags;
1470 if (psState)
1471 sState = *psState;
1472 else
1473 memset(&sState, 0, sizeof(SCRIPT_STATE));
1475 if (psControl)
1476 sControl = *psControl;
1477 else
1478 memset(&sControl, 0, sizeof(SCRIPT_CONTROL));
1480 if (dwFlags & SSA_PASSWORD)
1482 iString = heap_alloc(sizeof(WCHAR)*cString);
1483 if (!iString)
1485 hr = E_OUTOFMEMORY;
1486 goto error;
1488 for (i = 0; i < cString; i++)
1489 iString[i] = *((const WCHAR *)pString);
1490 pString = iString;
1493 hr = ScriptItemize(pString, cString, num_items, &sControl, &sState, analysis->pItem,
1494 &analysis->numItems);
1496 while (hr == E_OUTOFMEMORY)
1498 SCRIPT_ITEM *tmp;
1500 num_items *= 2;
1501 if (!(tmp = heap_realloc_zero(analysis->pItem, num_items * sizeof(SCRIPT_ITEM) + 1)))
1502 goto error;
1504 analysis->pItem = tmp;
1505 hr = ScriptItemize(pString, cString, num_items, psControl, psState, analysis->pItem,
1506 &analysis->numItems);
1508 if (hr != S_OK) goto error;
1510 /* set back to out of memory for default goto error behaviour */
1511 hr = E_OUTOFMEMORY;
1513 if (dwFlags & SSA_BREAK)
1515 if ((analysis->logattrs = heap_alloc(sizeof(SCRIPT_LOGATTR) * cString)))
1517 for (i = 0; i < analysis->numItems; i++)
1518 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]);
1520 else
1521 goto error;
1524 if (!(analysis->logical2visual = heap_alloc_zero(sizeof(int) * analysis->numItems)))
1525 goto error;
1526 if (!(BidiLevel = heap_alloc_zero(analysis->numItems)))
1527 goto error;
1529 if (dwFlags & SSA_GLYPHS)
1531 int tab_x = 0;
1532 if (!(analysis->glyphs = heap_alloc_zero(sizeof(StringGlyphs) * analysis->numItems)))
1534 heap_free(BidiLevel);
1535 goto error;
1538 for (i = 0; i < analysis->numItems; i++)
1540 SCRIPT_CACHE *sc = (SCRIPT_CACHE*)&analysis->glyphs[i].sc;
1541 int cChar = analysis->pItem[i+1].iCharPos - analysis->pItem[i].iCharPos;
1542 int numGlyphs = 1.5 * cChar + 16;
1543 WORD *glyphs = heap_alloc_zero(sizeof(WORD) * numGlyphs);
1544 WORD *pwLogClust = heap_alloc_zero(sizeof(WORD) * cChar);
1545 int *piAdvance = heap_alloc_zero(sizeof(int) * numGlyphs);
1546 SCRIPT_VISATTR *psva = heap_alloc_zero(sizeof(SCRIPT_VISATTR) * numGlyphs);
1547 GOFFSET *pGoffset = heap_alloc_zero(sizeof(GOFFSET) * numGlyphs);
1548 ABC *abc = heap_alloc_zero(sizeof(ABC));
1549 int numGlyphsReturned;
1550 HFONT originalFont = 0x0;
1552 /* FIXME: non unicode strings */
1553 const WCHAR* pStr = (const WCHAR*)pString;
1554 analysis->glyphs[i].fallbackFont = NULL;
1556 if (!glyphs || !pwLogClust || !piAdvance || !psva || !pGoffset || !abc)
1558 heap_free (BidiLevel);
1559 heap_free (glyphs);
1560 heap_free (pwLogClust);
1561 heap_free (piAdvance);
1562 heap_free (psva);
1563 heap_free (pGoffset);
1564 heap_free (abc);
1565 hr = E_OUTOFMEMORY;
1566 goto error;
1569 if ((dwFlags & SSA_FALLBACK) && requires_fallback(hdc, sc, &analysis->pItem[i].a, &pStr[analysis->pItem[i].iCharPos], cChar))
1571 LOGFONTW lf;
1572 GetObjectW(GetCurrentObject(hdc, OBJ_FONT), sizeof(lf), & lf);
1573 lf.lfCharSet = scriptInformation[analysis->pItem[i].a.eScript].props.bCharSet;
1574 find_fallback_font(analysis->pItem[i].a.eScript, lf.lfFaceName);
1575 analysis->glyphs[i].fallbackFont = CreateFontIndirectW(&lf);
1576 if (analysis->glyphs[i].fallbackFont)
1578 ScriptFreeCache(sc);
1579 originalFont = SelectObject(hdc, analysis->glyphs[i].fallbackFont);
1583 hr = ScriptShape(hdc, sc, &pStr[analysis->pItem[i].iCharPos],
1584 cChar, numGlyphs, &analysis->pItem[i].a,
1585 glyphs, pwLogClust, psva, &numGlyphsReturned);
1586 hr = ScriptPlace(hdc, sc, glyphs, numGlyphsReturned, psva, &analysis->pItem[i].a,
1587 piAdvance, pGoffset, abc);
1588 if (originalFont)
1589 SelectObject(hdc,originalFont);
1591 if (dwFlags & SSA_TAB)
1593 int tabi = 0;
1594 for (tabi = 0; tabi < cChar; tabi++)
1596 if (pStr[analysis->pItem[i].iCharPos+tabi] == 0x0009)
1597 piAdvance[tabi] = getGivenTabWidth(analysis->glyphs[i].sc, pTabdef, analysis->pItem[i].iCharPos+tabi, tab_x);
1598 tab_x+=piAdvance[tabi];
1602 analysis->glyphs[i].numGlyphs = numGlyphsReturned;
1603 analysis->glyphs[i].glyphs = glyphs;
1604 analysis->glyphs[i].pwLogClust = pwLogClust;
1605 analysis->glyphs[i].piAdvance = piAdvance;
1606 analysis->glyphs[i].psva = psva;
1607 analysis->glyphs[i].pGoffset = pGoffset;
1608 analysis->glyphs[i].abc = abc;
1609 analysis->glyphs[i].iMaxPosX= -1;
1611 BidiLevel[i] = analysis->pItem[i].a.s.uBidiLevel;
1614 else
1616 for (i = 0; i < analysis->numItems; i++)
1617 BidiLevel[i] = analysis->pItem[i].a.s.uBidiLevel;
1620 ScriptLayout(analysis->numItems, BidiLevel, NULL, analysis->logical2visual);
1621 heap_free(BidiLevel);
1623 *pssa = analysis;
1624 heap_free(iString);
1625 return S_OK;
1627 error:
1628 heap_free(iString);
1629 heap_free(analysis->glyphs);
1630 heap_free(analysis->logattrs);
1631 heap_free(analysis->pItem);
1632 heap_free(analysis->logical2visual);
1633 heap_free(analysis);
1634 return hr;
1637 static inline BOOL does_glyph_start_cluster(const SCRIPT_VISATTR *pva, const WORD *pwLogClust, int cChars, int glyph, int direction)
1639 int i;
1641 if (pva[glyph].fClusterStart)
1642 return TRUE;
1643 for (i = 0; i < cChars; i++)
1644 if (pwLogClust[i] == glyph) break;
1645 if (i != cChars)
1646 return TRUE;
1648 return FALSE;
1652 static HRESULT SS_ItemOut( SCRIPT_STRING_ANALYSIS ssa,
1653 int iX,
1654 int iY,
1655 int iItem,
1656 int cStart,
1657 int cEnd,
1658 UINT uOptions,
1659 const RECT *prc,
1660 BOOL fSelected,
1661 BOOL fDisabled)
1663 StringAnalysis *analysis;
1664 int off_x = 0;
1665 HRESULT hr;
1666 COLORREF BkColor = 0x0;
1667 COLORREF TextColor = 0x0;
1668 INT BkMode = 0;
1669 INT runStart, runEnd;
1670 INT iGlyph, cGlyphs;
1671 HFONT oldFont = 0x0;
1673 TRACE("(%p,%d,%d,%d,%d,%d, 0x%1x, %d, %d)\n",
1674 ssa, iX, iY, iItem, cStart, cEnd, uOptions, fSelected, fDisabled);
1676 if (!(analysis = ssa)) return E_INVALIDARG;
1678 if ((cStart >= 0 && analysis->pItem[iItem+1].iCharPos <= cStart) ||
1679 (cEnd >= 0 && analysis->pItem[iItem].iCharPos >= cEnd))
1680 return S_OK;
1682 if (fSelected)
1684 BkMode = GetBkMode(analysis->hdc);
1685 SetBkMode( analysis->hdc, OPAQUE);
1686 BkColor = GetBkColor(analysis->hdc);
1687 SetBkColor(analysis->hdc, GetSysColor(COLOR_HIGHLIGHT));
1688 if (!fDisabled)
1690 TextColor = GetTextColor(analysis->hdc);
1691 SetTextColor(analysis->hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1694 if (analysis->glyphs[iItem].fallbackFont)
1695 oldFont = SelectObject(analysis->hdc, analysis->glyphs[iItem].fallbackFont);
1697 if (cStart >= 0 && analysis->pItem[iItem+1].iCharPos > cStart && analysis->pItem[iItem].iCharPos <= cStart)
1698 runStart = cStart - analysis->pItem[iItem].iCharPos;
1699 else
1700 runStart = 0;
1701 if (cEnd >= 0 && analysis->pItem[iItem+1].iCharPos > cEnd && analysis->pItem[iItem].iCharPos <= cEnd)
1702 runEnd = (cEnd-1) - analysis->pItem[iItem].iCharPos;
1703 else
1704 runEnd = (analysis->pItem[iItem+1].iCharPos - analysis->pItem[iItem].iCharPos) - 1;
1706 if (analysis->pItem[iItem].a.fRTL)
1708 if (cEnd >= 0 && cEnd < analysis->pItem[iItem+1].iCharPos)
1709 ScriptStringCPtoX(ssa, cEnd, FALSE, &off_x);
1710 else
1711 ScriptStringCPtoX(ssa, analysis->pItem[iItem+1].iCharPos-1, TRUE, &off_x);
1713 else
1715 if (cStart >=0 && runStart)
1716 ScriptStringCPtoX(ssa, cStart, FALSE, &off_x);
1717 else
1718 ScriptStringCPtoX(ssa, analysis->pItem[iItem].iCharPos, FALSE, &off_x);
1721 if (analysis->pItem[iItem].a.fRTL)
1722 iGlyph = analysis->glyphs[iItem].pwLogClust[runEnd];
1723 else
1724 iGlyph = analysis->glyphs[iItem].pwLogClust[runStart];
1726 if (analysis->pItem[iItem].a.fRTL)
1727 cGlyphs = analysis->glyphs[iItem].pwLogClust[runStart] - iGlyph;
1728 else
1729 cGlyphs = analysis->glyphs[iItem].pwLogClust[runEnd] - iGlyph;
1731 cGlyphs++;
1733 if (cEnd < 0 || scriptInformation[analysis->pItem[iItem].a.eScript].props.fNeedsCaretInfo)
1735 INT direction;
1736 INT clust_glyph;
1738 clust_glyph = iGlyph + cGlyphs;
1739 if (analysis->pItem[iItem].a.fRTL)
1740 direction = -1;
1741 else
1742 direction = 1;
1744 while(clust_glyph < analysis->glyphs[iItem].numGlyphs &&
1745 !does_glyph_start_cluster(analysis->glyphs[iItem].psva, analysis->glyphs[iItem].pwLogClust, (analysis->pItem[iItem+1].iCharPos - analysis->pItem[iItem].iCharPos), clust_glyph, direction))
1747 cGlyphs++;
1748 clust_glyph++;
1752 hr = ScriptTextOut(analysis->hdc,
1753 (SCRIPT_CACHE *)&analysis->glyphs[iItem].sc, iX + off_x,
1754 iY, uOptions, prc, &analysis->pItem[iItem].a, NULL, 0,
1755 &analysis->glyphs[iItem].glyphs[iGlyph], cGlyphs,
1756 &analysis->glyphs[iItem].piAdvance[iGlyph], NULL,
1757 &analysis->glyphs[iItem].pGoffset[iGlyph]);
1759 TRACE("ScriptTextOut hr=%08x\n", hr);
1761 if (fSelected)
1763 SetBkColor(analysis->hdc, BkColor);
1764 SetBkMode( analysis->hdc, BkMode);
1765 if (!fDisabled)
1766 SetTextColor(analysis->hdc, TextColor);
1768 if (analysis->glyphs[iItem].fallbackFont)
1769 SelectObject(analysis->hdc, oldFont);
1771 return hr;
1774 /***********************************************************************
1775 * ScriptStringOut (USP10.@)
1777 * This function takes the output of ScriptStringAnalyse and joins the segments
1778 * of glyphs and passes the resulting string to ScriptTextOut. ScriptStringOut
1779 * only processes glyphs.
1781 * Parameters:
1782 * ssa [I] buffer to hold the analysed string components
1783 * iX [I] X axis displacement for output
1784 * iY [I] Y axis displacement for output
1785 * uOptions [I] flags controling output processing
1786 * prc [I] rectangle coordinates
1787 * iMinSel [I] starting pos for substringing output string
1788 * iMaxSel [I] ending pos for substringing output string
1789 * fDisabled [I] controls text highlighting
1791 * RETURNS
1792 * Success: S_OK
1793 * Failure: is the value returned by ScriptTextOut
1795 HRESULT WINAPI ScriptStringOut(SCRIPT_STRING_ANALYSIS ssa,
1796 int iX,
1797 int iY,
1798 UINT uOptions,
1799 const RECT *prc,
1800 int iMinSel,
1801 int iMaxSel,
1802 BOOL fDisabled)
1804 StringAnalysis *analysis;
1805 int item;
1806 HRESULT hr;
1808 TRACE("(%p,%d,%d,0x%1x,%p,%d,%d,%d)\n",
1809 ssa, iX, iY, uOptions, prc, iMinSel, iMaxSel, fDisabled);
1811 if (!(analysis = ssa)) return E_INVALIDARG;
1812 if (!(analysis->dwFlags & SSA_GLYPHS)) return E_INVALIDARG;
1814 for (item = 0; item < analysis->numItems; item++)
1816 hr = SS_ItemOut( ssa, iX, iY, analysis->logical2visual[item], -1, -1, uOptions, prc, FALSE, fDisabled);
1817 if (FAILED(hr))
1818 return hr;
1821 if (iMinSel < iMaxSel && (iMinSel > 0 || iMaxSel > 0))
1823 if (iMaxSel > 0 && iMinSel < 0)
1824 iMinSel = 0;
1825 for (item = 0; item < analysis->numItems; item++)
1827 hr = SS_ItemOut( ssa, iX, iY, analysis->logical2visual[item], iMinSel, iMaxSel, uOptions, prc, TRUE, fDisabled);
1828 if (FAILED(hr))
1829 return hr;
1833 return S_OK;
1836 /***********************************************************************
1837 * ScriptStringCPtoX (USP10.@)
1840 HRESULT WINAPI ScriptStringCPtoX(SCRIPT_STRING_ANALYSIS ssa, int icp, BOOL fTrailing, int* pX)
1842 int item;
1843 int runningX = 0;
1844 StringAnalysis* analysis = ssa;
1846 TRACE("(%p), %d, %d, (%p)\n", ssa, icp, fTrailing, pX);
1848 if (!ssa || !pX) return S_FALSE;
1849 if (!(analysis->dwFlags & SSA_GLYPHS)) return S_FALSE;
1851 /* icp out of range */
1852 if(icp < 0)
1854 analysis->invalid = TRUE;
1855 return E_INVALIDARG;
1858 for(item=0; item<analysis->numItems; item++)
1860 int CP, i;
1861 int offset;
1863 i = analysis->logical2visual[item];
1864 CP = analysis->pItem[i+1].iCharPos - analysis->pItem[i].iCharPos;
1865 /* initialize max extents for uninitialized runs */
1866 if (analysis->glyphs[i].iMaxPosX == -1)
1868 if (analysis->pItem[i].a.fRTL)
1869 ScriptCPtoX(0, FALSE, CP, analysis->glyphs[i].numGlyphs, analysis->glyphs[i].pwLogClust,
1870 analysis->glyphs[i].psva, analysis->glyphs[i].piAdvance,
1871 &analysis->pItem[i].a, &analysis->glyphs[i].iMaxPosX);
1872 else
1873 ScriptCPtoX(CP, TRUE, CP, analysis->glyphs[i].numGlyphs, analysis->glyphs[i].pwLogClust,
1874 analysis->glyphs[i].psva, analysis->glyphs[i].piAdvance,
1875 &analysis->pItem[i].a, &analysis->glyphs[i].iMaxPosX);
1878 if (icp >= analysis->pItem[i+1].iCharPos || icp < analysis->pItem[i].iCharPos)
1880 runningX += analysis->glyphs[i].iMaxPosX;
1881 continue;
1884 icp -= analysis->pItem[i].iCharPos;
1885 ScriptCPtoX(icp, fTrailing, CP, analysis->glyphs[i].numGlyphs, analysis->glyphs[i].pwLogClust,
1886 analysis->glyphs[i].psva, analysis->glyphs[i].piAdvance,
1887 &analysis->pItem[i].a, &offset);
1888 runningX += offset;
1890 *pX = runningX;
1891 return S_OK;
1894 /* icp out of range */
1895 analysis->invalid = TRUE;
1896 return E_INVALIDARG;
1899 /***********************************************************************
1900 * ScriptStringXtoCP (USP10.@)
1903 HRESULT WINAPI ScriptStringXtoCP(SCRIPT_STRING_ANALYSIS ssa, int iX, int* piCh, int* piTrailing)
1905 StringAnalysis* analysis = ssa;
1906 int item;
1908 TRACE("(%p), %d, (%p), (%p)\n", ssa, iX, piCh, piTrailing);
1910 if (!ssa || !piCh || !piTrailing) return S_FALSE;
1911 if (!(analysis->dwFlags & SSA_GLYPHS)) return S_FALSE;
1913 /* out of range */
1914 if(iX < 0)
1916 if (analysis->pItem[0].a.fRTL)
1918 *piCh = 1;
1919 *piTrailing = FALSE;
1921 else
1923 *piCh = -1;
1924 *piTrailing = TRUE;
1926 return S_OK;
1929 for(item=0; item<analysis->numItems; item++)
1931 int i;
1932 int CP;
1934 for (i = 0; i < analysis->numItems && analysis->logical2visual[i] != item; i++)
1935 /* nothing */;
1937 CP = analysis->pItem[i+1].iCharPos - analysis->pItem[i].iCharPos;
1938 /* initialize max extents for uninitialized runs */
1939 if (analysis->glyphs[i].iMaxPosX == -1)
1941 if (analysis->pItem[i].a.fRTL)
1942 ScriptCPtoX(0, FALSE, CP, analysis->glyphs[i].numGlyphs, analysis->glyphs[i].pwLogClust,
1943 analysis->glyphs[i].psva, analysis->glyphs[i].piAdvance,
1944 &analysis->pItem[i].a, &analysis->glyphs[i].iMaxPosX);
1945 else
1946 ScriptCPtoX(CP, TRUE, CP, analysis->glyphs[i].numGlyphs, analysis->glyphs[i].pwLogClust,
1947 analysis->glyphs[i].psva, analysis->glyphs[i].piAdvance,
1948 &analysis->pItem[i].a, &analysis->glyphs[i].iMaxPosX);
1951 if (iX > analysis->glyphs[i].iMaxPosX)
1953 iX -= analysis->glyphs[i].iMaxPosX;
1954 continue;
1957 ScriptXtoCP(iX, CP, analysis->glyphs[i].numGlyphs, analysis->glyphs[i].pwLogClust,
1958 analysis->glyphs[i].psva, analysis->glyphs[i].piAdvance,
1959 &analysis->pItem[i].a, piCh, piTrailing);
1960 *piCh += analysis->pItem[i].iCharPos;
1962 return S_OK;
1965 /* out of range */
1966 *piCh = analysis->pItem[analysis->numItems].iCharPos;
1967 *piTrailing = FALSE;
1969 return S_OK;
1973 /***********************************************************************
1974 * ScriptStringFree (USP10.@)
1976 * Free a string analysis.
1978 * PARAMS
1979 * pssa [I] string analysis.
1981 * RETURNS
1982 * Success: S_OK
1983 * Failure: Non-zero HRESULT value.
1985 HRESULT WINAPI ScriptStringFree(SCRIPT_STRING_ANALYSIS *pssa)
1987 StringAnalysis* analysis;
1988 BOOL invalid;
1989 int i;
1991 TRACE("(%p)\n", pssa);
1993 if (!pssa || !(analysis = *pssa)) return E_INVALIDARG;
1995 invalid = analysis->invalid;
1997 if (analysis->glyphs)
1999 for (i = 0; i < analysis->numItems; i++)
2001 heap_free(analysis->glyphs[i].glyphs);
2002 heap_free(analysis->glyphs[i].pwLogClust);
2003 heap_free(analysis->glyphs[i].piAdvance);
2004 heap_free(analysis->glyphs[i].psva);
2005 heap_free(analysis->glyphs[i].pGoffset);
2006 heap_free(analysis->glyphs[i].abc);
2007 if (analysis->glyphs[i].fallbackFont)
2008 DeleteObject(analysis->glyphs[i].fallbackFont);
2009 ScriptFreeCache((SCRIPT_CACHE *)&analysis->glyphs[i].sc);
2010 heap_free(analysis->glyphs[i].sc);
2012 heap_free(analysis->glyphs);
2015 heap_free(analysis->pItem);
2016 heap_free(analysis->logattrs);
2017 heap_free(analysis->sz);
2018 heap_free(analysis->logical2visual);
2019 heap_free(analysis);
2021 if (invalid) return E_INVALIDARG;
2022 return S_OK;
2025 static inline int get_cluster_size(const WORD *pwLogClust, int cChars, int item,
2026 int direction, int* iCluster, int *check_out)
2028 int clust_size = 1;
2029 int check;
2030 WORD clust = pwLogClust[item];
2032 for (check = item+direction; check < cChars && check >= 0; check+=direction)
2034 if (pwLogClust[check] == clust)
2036 clust_size ++;
2037 if (iCluster && *iCluster == -1)
2038 *iCluster = item;
2040 else break;
2043 if (check_out)
2044 *check_out = check;
2046 return clust_size;
2049 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)
2051 int advance;
2052 int log_clust_max = 0;
2053 int i;
2055 advance = piAdvance[glyph];
2057 for (i = 0; i < cChars; i++)
2059 if (pwLogClust[i] > log_clust_max)
2060 log_clust_max = pwLogClust[i];
2063 if (glyph > log_clust_max)
2064 return advance;
2066 for (glyph+=direction; glyph < cGlyphs && glyph >= 0; glyph +=direction)
2069 if (does_glyph_start_cluster(pva, pwLogClust, cChars, glyph, direction))
2070 break;
2071 if (glyph > log_clust_max)
2072 break;
2073 advance += piAdvance[glyph];
2076 return advance;
2079 /***********************************************************************
2080 * ScriptCPtoX (USP10.@)
2083 HRESULT WINAPI ScriptCPtoX(int iCP,
2084 BOOL fTrailing,
2085 int cChars,
2086 int cGlyphs,
2087 const WORD *pwLogClust,
2088 const SCRIPT_VISATTR *psva,
2089 const int *piAdvance,
2090 const SCRIPT_ANALYSIS *psa,
2091 int *piX)
2093 int item;
2094 float iPosX;
2095 int iSpecial = -1;
2096 int iCluster = -1;
2097 int clust_size = 1;
2098 float special_size = 0.0;
2099 int iMaxPos = 0;
2100 int advance = 0;
2101 BOOL rtl = FALSE;
2103 TRACE("(%d,%d,%d,%d,%p,%p,%p,%p,%p)\n",
2104 iCP, fTrailing, cChars, cGlyphs, pwLogClust, psva, piAdvance,
2105 psa, piX);
2107 if (psa->fRTL && ! psa->fLogicalOrder)
2108 rtl = TRUE;
2110 if (fTrailing)
2111 iCP++;
2113 if (rtl)
2115 int max_clust = pwLogClust[0];
2117 for (item=0; item < cGlyphs; item++)
2118 if (pwLogClust[item] > max_clust)
2120 ERR("We do not handle non reversed clusters properly\n");
2121 break;
2124 iMaxPos = 0;
2125 for (item = max_clust; item >=0; item --)
2126 iMaxPos += piAdvance[item];
2129 iPosX = 0.0;
2130 for (item=0; item < iCP && item < cChars; item++)
2132 if (iSpecial == -1 && (iCluster == -1 || (iCluster != -1 && iCluster+clust_size <= item)))
2134 int check;
2135 int clust = pwLogClust[item];
2137 iCluster = -1;
2138 clust_size = get_cluster_size(pwLogClust, cChars, item, 1, &iCluster,
2139 &check);
2141 advance = get_glyph_cluster_advance(piAdvance, psva, pwLogClust, cGlyphs, cChars, clust, 1);
2143 if (check >= cChars && !iMaxPos)
2145 for (check = clust; check < cChars; check++)
2146 special_size += get_glyph_cluster_advance(piAdvance, psva, pwLogClust, cGlyphs, cChars, check, 1);
2147 iSpecial = item;
2148 special_size /= (cChars - item);
2149 iPosX += special_size;
2151 else
2153 if (scriptInformation[psa->eScript].props.fNeedsCaretInfo)
2155 clust_size --;
2156 if (clust_size == 0)
2157 iPosX += advance;
2159 else
2160 iPosX += advance / (float)clust_size;
2163 else if (iSpecial != -1)
2164 iPosX += special_size;
2165 else /* (iCluster != -1) */
2167 int adv = get_glyph_cluster_advance(piAdvance, psva, pwLogClust, cGlyphs, cChars, pwLogClust[iCluster], 1);
2168 if (scriptInformation[psa->eScript].props.fNeedsCaretInfo)
2170 clust_size --;
2171 if (clust_size == 0)
2172 iPosX += adv;
2174 else
2175 iPosX += adv / (float)clust_size;
2179 if (iMaxPos > 0)
2181 iPosX = iMaxPos - iPosX;
2182 if (iPosX < 0)
2183 iPosX = 0;
2186 *piX = iPosX;
2187 TRACE("*piX=%d\n", *piX);
2188 return S_OK;
2191 /***********************************************************************
2192 * ScriptXtoCP (USP10.@)
2195 HRESULT WINAPI ScriptXtoCP(int iX,
2196 int cChars,
2197 int cGlyphs,
2198 const WORD *pwLogClust,
2199 const SCRIPT_VISATTR *psva,
2200 const int *piAdvance,
2201 const SCRIPT_ANALYSIS *psa,
2202 int *piCP,
2203 int *piTrailing)
2205 int item;
2206 float iPosX;
2207 float iLastPosX;
2208 int iSpecial = -1;
2209 int iCluster = -1;
2210 int clust_size = 1;
2211 int cjump = 0;
2212 int advance;
2213 float special_size = 0.0;
2214 int direction = 1;
2216 TRACE("(%d,%d,%d,%p,%p,%p,%p,%p,%p)\n",
2217 iX, cChars, cGlyphs, pwLogClust, psva, piAdvance,
2218 psa, piCP, piTrailing);
2220 if (psa->fRTL && ! psa->fLogicalOrder)
2221 direction = -1;
2223 if (direction<0)
2225 int max_clust = pwLogClust[0];
2227 if (iX < 0)
2229 *piCP = cChars;
2230 *piTrailing = 0;
2231 return S_OK;
2234 for (item=0; item < cChars; item++)
2235 if (pwLogClust[item] > max_clust)
2237 ERR("We do not handle non reversed clusters properly\n");
2238 break;
2242 if (iX < 0)
2244 *piCP = -1;
2245 *piTrailing = 1;
2246 return S_OK;
2249 iPosX = iLastPosX = 0;
2250 if (direction > 0)
2251 item = 0;
2252 else
2253 item = cChars - 1;
2254 for (; iPosX <= iX && item < cChars && item >= 0; item+=direction)
2256 iLastPosX = iPosX;
2257 if (iSpecial == -1 &&
2258 (iCluster == -1 ||
2259 (iCluster != -1 &&
2260 ((direction > 0 && iCluster+clust_size <= item) ||
2261 (direction < 0 && iCluster-clust_size >= item))
2266 int check;
2267 int clust = pwLogClust[item];
2269 iCluster = -1;
2270 cjump = 0;
2271 clust_size = get_cluster_size(pwLogClust, cChars, item, direction,
2272 &iCluster, &check);
2273 advance = get_glyph_cluster_advance(piAdvance, psva, pwLogClust, cGlyphs, cChars, clust, direction);
2275 if (check >= cChars && direction > 0)
2277 for (check = clust; check < cChars; check++)
2278 special_size += get_glyph_cluster_advance(piAdvance, psva, pwLogClust, cGlyphs, cChars, check, direction);
2279 iSpecial = item;
2280 special_size /= (cChars - item);
2281 iPosX += special_size;
2283 else
2285 if (scriptInformation[psa->eScript].props.fNeedsCaretInfo)
2287 if (!cjump)
2288 iPosX += advance;
2289 cjump++;
2291 else
2292 iPosX += advance / (float)clust_size;
2295 else if (iSpecial != -1)
2296 iPosX += special_size;
2297 else /* (iCluster != -1) */
2299 int adv = get_glyph_cluster_advance(piAdvance, psva, pwLogClust, cGlyphs, cChars, pwLogClust[iCluster], direction);
2300 if (scriptInformation[psa->eScript].props.fNeedsCaretInfo)
2302 if (!cjump)
2303 iPosX += adv;
2304 cjump++;
2306 else
2307 iPosX += adv / (float)clust_size;
2311 if (direction > 0)
2313 if (iPosX > iX)
2314 item--;
2315 if (item < cChars && ((iPosX - iLastPosX) / 2.0) + iX >= iPosX)
2317 if (scriptInformation[psa->eScript].props.fNeedsCaretInfo && clust_size > 1)
2318 item+=(clust_size-1);
2319 *piTrailing = 1;
2321 else
2322 *piTrailing = 0;
2324 else
2326 if (iX == iLastPosX)
2327 item++;
2328 if (iX >= iLastPosX && iX <= iPosX)
2329 item++;
2331 if (iLastPosX == iX)
2332 *piTrailing = 0;
2333 else if (item < 0 || ((iLastPosX - iPosX) / 2.0) + iX <= iLastPosX)
2335 if (scriptInformation[psa->eScript].props.fNeedsCaretInfo && clust_size > 1)
2336 item-=(clust_size-1);
2337 *piTrailing = 1;
2339 else
2340 *piTrailing = 0;
2343 *piCP = item;
2345 TRACE("*piCP=%d\n", *piCP);
2346 TRACE("*piTrailing=%d\n", *piTrailing);
2347 return S_OK;
2350 /***********************************************************************
2351 * ScriptBreak (USP10.@)
2353 * Retrieve line break information.
2355 * PARAMS
2356 * chars [I] Array of characters.
2357 * sa [I] String analysis.
2358 * la [I] Array of logical attribute structures.
2360 * RETURNS
2361 * Success: S_OK
2362 * Failure: S_FALSE
2364 HRESULT WINAPI ScriptBreak(const WCHAR *chars, int count, const SCRIPT_ANALYSIS *sa, SCRIPT_LOGATTR *la)
2366 TRACE("(%s, %d, %p, %p)\n", debugstr_wn(chars, count), count, sa, la);
2368 if (!la) return S_FALSE;
2370 BREAK_line(chars, count, sa, la);
2372 return S_OK;
2375 /***********************************************************************
2376 * ScriptIsComplex (USP10.@)
2378 * Determine if a string is complex.
2380 * PARAMS
2381 * chars [I] Array of characters to test.
2382 * len [I] Length in characters.
2383 * flag [I] Flag.
2385 * RETURNS
2386 * Success: S_OK
2387 * Failure: S_FALSE
2390 HRESULT WINAPI ScriptIsComplex(const WCHAR *chars, int len, DWORD flag)
2392 int i;
2394 TRACE("(%s,%d,0x%x)\n", debugstr_wn(chars, len), len, flag);
2396 for (i = 0; i < len; i++)
2398 int script;
2400 if ((flag & SIC_ASCIIDIGIT) && chars[i] >= 0x30 && chars[i] <= 0x39)
2401 return S_OK;
2403 script = get_char_script(chars[i]);
2404 if ((scriptInformation[script].props.fComplex && (flag & SIC_COMPLEX))||
2405 (!scriptInformation[script].props.fComplex && (flag & SIC_NEUTRAL)))
2406 return S_OK;
2408 return S_FALSE;
2411 /***********************************************************************
2412 * ScriptShapeOpenType (USP10.@)
2414 * Produce glyphs and visual attributes for a run.
2416 * PARAMS
2417 * hdc [I] Device context.
2418 * psc [I/O] Opaque pointer to a script cache.
2419 * psa [I/O] Script analysis.
2420 * tagScript [I] The OpenType tag for the Script
2421 * tagLangSys [I] The OpenType tag for the Language
2422 * rcRangeChars[I] Array of Character counts in each range
2423 * rpRangeProperties [I] Array of TEXTRANGE_PROPERTIES structures
2424 * cRanges [I] Count of ranges
2425 * pwcChars [I] Array of characters specifying the run.
2426 * cChars [I] Number of characters in pwcChars.
2427 * cMaxGlyphs [I] Length of pwOutGlyphs.
2428 * pwLogClust [O] Array of logical cluster info.
2429 * pCharProps [O] Array of character property values
2430 * pwOutGlyphs [O] Array of glyphs.
2431 * pOutGlyphProps [O] Array of attributes for the retrieved glyphs
2432 * pcGlyphs [O] Number of glyphs returned.
2434 * RETURNS
2435 * Success: S_OK
2436 * Failure: Non-zero HRESULT value.
2438 HRESULT WINAPI ScriptShapeOpenType( HDC hdc, SCRIPT_CACHE *psc,
2439 SCRIPT_ANALYSIS *psa, OPENTYPE_TAG tagScript,
2440 OPENTYPE_TAG tagLangSys, int *rcRangeChars,
2441 TEXTRANGE_PROPERTIES **rpRangeProperties,
2442 int cRanges, const WCHAR *pwcChars, int cChars,
2443 int cMaxGlyphs, WORD *pwLogClust,
2444 SCRIPT_CHARPROP *pCharProps, WORD *pwOutGlyphs,
2445 SCRIPT_GLYPHPROP *pOutGlyphProps, int *pcGlyphs)
2447 HRESULT hr;
2448 unsigned int i;
2449 BOOL rtl;
2451 TRACE("(%p, %p, %p, %s, %s, %p, %p, %d, %s, %d, %d, %p, %p, %p, %p, %p )\n",
2452 hdc, psc, psa,
2453 debugstr_an((char*)&tagScript,4), debugstr_an((char*)&tagLangSys,4),
2454 rcRangeChars, rpRangeProperties, cRanges, debugstr_wn(pwcChars, cChars),
2455 cChars, cMaxGlyphs, pwLogClust, pCharProps, pwOutGlyphs, pOutGlyphProps, pcGlyphs);
2457 if (psa) TRACE("psa values: %d, %d, %d, %d, %d, %d, %d\n", psa->eScript, psa->fRTL, psa->fLayoutRTL,
2458 psa->fLinkBefore, psa->fLinkAfter, psa->fLogicalOrder, psa->fNoGlyphIndex);
2460 if (!pOutGlyphProps || !pcGlyphs || !pCharProps) return E_INVALIDARG;
2461 if (cChars > cMaxGlyphs) return E_OUTOFMEMORY;
2463 if (cRanges)
2464 FIXME("Ranges not supported yet\n");
2466 rtl = (psa && !psa->fLogicalOrder && psa->fRTL);
2468 *pcGlyphs = cChars;
2469 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
2470 if (!pwLogClust) return E_FAIL;
2472 ((ScriptCache *)*psc)->userScript = tagScript;
2473 ((ScriptCache *)*psc)->userLang = tagLangSys;
2475 /* set fNoGlyphIndex non truetype/opentype fonts */
2476 if (!psa->fNoGlyphIndex && !((ScriptCache *)*psc)->sfnt)
2477 psa->fNoGlyphIndex = TRUE;
2479 /* Initialize a SCRIPT_VISATTR and LogClust for each char in this run */
2480 for (i = 0; i < cChars; i++)
2482 int idx = i;
2483 if (rtl) idx = cChars - 1 - i;
2484 /* FIXME: set to better values */
2485 pOutGlyphProps[i].sva.uJustification = (pwcChars[idx] == ' ') ? SCRIPT_JUSTIFY_BLANK : SCRIPT_JUSTIFY_CHARACTER;
2486 pOutGlyphProps[i].sva.fClusterStart = 1;
2487 pOutGlyphProps[i].sva.fDiacritic = 0;
2488 pOutGlyphProps[i].sva.fZeroWidth = 0;
2489 pOutGlyphProps[i].sva.fReserved = 0;
2490 pOutGlyphProps[i].sva.fShapeReserved = 0;
2492 /* FIXME: have the shaping engine set this */
2493 pCharProps[i].fCanGlyphAlone = 0;
2495 pwLogClust[i] = idx;
2498 if (psa && !psa->fNoGlyphIndex)
2500 WCHAR *rChars;
2501 if ((hr = SHAPE_CheckFontForRequiredFeatures(hdc, (ScriptCache *)*psc, psa)) != S_OK) return hr;
2503 rChars = heap_alloc(sizeof(WCHAR) * cChars);
2504 if (!rChars) return E_OUTOFMEMORY;
2505 for (i = 0; i < cChars; i++)
2507 int idx = i;
2508 WCHAR chInput;
2509 if (rtl) idx = cChars - 1 - i;
2510 if (psa->fRTL)
2511 chInput = mirror_char(pwcChars[idx]);
2512 else
2513 chInput = pwcChars[idx];
2514 /* special case for tabs */
2515 if (chInput == 0x0009)
2516 chInput = 0x0020;
2517 if (!(pwOutGlyphs[i] = get_cache_glyph(psc, chInput)))
2519 WORD glyph;
2520 if (!hdc)
2522 heap_free(rChars);
2523 return E_PENDING;
2525 if (GetGlyphIndicesW(hdc, &chInput, 1, &glyph, 0) == GDI_ERROR)
2527 heap_free(rChars);
2528 return S_FALSE;
2530 pwOutGlyphs[i] = set_cache_glyph(psc, chInput, glyph);
2532 rChars[i] = chInput;
2535 SHAPE_ContextualShaping(hdc, (ScriptCache *)*psc, psa, rChars, cChars, pwOutGlyphs, pcGlyphs, cMaxGlyphs, pwLogClust);
2536 SHAPE_ApplyDefaultOpentypeFeatures(hdc, (ScriptCache *)*psc, psa, pwOutGlyphs, pcGlyphs, cMaxGlyphs, cChars, pwLogClust);
2537 SHAPE_CharGlyphProp(hdc, (ScriptCache *)*psc, psa, pwcChars, cChars, pwOutGlyphs, *pcGlyphs, pwLogClust, pCharProps, pOutGlyphProps);
2538 heap_free(rChars);
2540 else
2542 TRACE("no glyph translation\n");
2543 for (i = 0; i < cChars; i++)
2545 int idx = i;
2546 /* No mirroring done here */
2547 if (rtl) idx = cChars - 1 - i;
2548 pwOutGlyphs[i] = pwcChars[idx];
2552 return S_OK;
2556 /***********************************************************************
2557 * ScriptShape (USP10.@)
2559 * Produce glyphs and visual attributes for a run.
2561 * PARAMS
2562 * hdc [I] Device context.
2563 * psc [I/O] Opaque pointer to a script cache.
2564 * pwcChars [I] Array of characters specifying the run.
2565 * cChars [I] Number of characters in pwcChars.
2566 * cMaxGlyphs [I] Length of pwOutGlyphs.
2567 * psa [I/O] Script analysis.
2568 * pwOutGlyphs [O] Array of glyphs.
2569 * pwLogClust [O] Array of logical cluster info.
2570 * psva [O] Array of visual attributes.
2571 * pcGlyphs [O] Number of glyphs returned.
2573 * RETURNS
2574 * Success: S_OK
2575 * Failure: Non-zero HRESULT value.
2577 HRESULT WINAPI ScriptShape(HDC hdc, SCRIPT_CACHE *psc, const WCHAR *pwcChars,
2578 int cChars, int cMaxGlyphs,
2579 SCRIPT_ANALYSIS *psa, WORD *pwOutGlyphs, WORD *pwLogClust,
2580 SCRIPT_VISATTR *psva, int *pcGlyphs)
2582 HRESULT hr;
2583 int i;
2584 SCRIPT_CHARPROP *charProps;
2585 SCRIPT_GLYPHPROP *glyphProps;
2587 if (!psva || !pcGlyphs) return E_INVALIDARG;
2588 if (cChars > cMaxGlyphs) return E_OUTOFMEMORY;
2590 charProps = heap_alloc_zero(sizeof(SCRIPT_CHARPROP)*cChars);
2591 if (!charProps) return E_OUTOFMEMORY;
2592 glyphProps = heap_alloc_zero(sizeof(SCRIPT_GLYPHPROP)*cMaxGlyphs);
2593 if (!glyphProps)
2595 heap_free(charProps);
2596 return E_OUTOFMEMORY;
2599 hr = ScriptShapeOpenType(hdc, psc, psa, scriptInformation[psa->eScript].scriptTag, 0, NULL, NULL, 0, pwcChars, cChars, cMaxGlyphs, pwLogClust, charProps, pwOutGlyphs, glyphProps, pcGlyphs);
2601 if (SUCCEEDED(hr))
2603 for (i = 0; i < *pcGlyphs; i++)
2604 psva[i] = glyphProps[i].sva;
2607 heap_free(charProps);
2608 heap_free(glyphProps);
2610 return hr;
2613 /***********************************************************************
2614 * ScriptPlaceOpenType (USP10.@)
2616 * Produce advance widths for a run.
2618 * PARAMS
2619 * hdc [I] Device context.
2620 * psc [I/O] Opaque pointer to a script cache.
2621 * psa [I/O] String analysis.
2622 * tagScript [I] The OpenType tag for the Script
2623 * tagLangSys [I] The OpenType tag for the Language
2624 * rcRangeChars[I] Array of Character counts in each range
2625 * rpRangeProperties [I] Array of TEXTRANGE_PROPERTIES structures
2626 * cRanges [I] Count of ranges
2627 * pwcChars [I] Array of characters specifying the run.
2628 * pwLogClust [I] Array of logical cluster info
2629 * pCharProps [I] Array of character property values
2630 * cChars [I] Number of characters in pwcChars.
2631 * pwGlyphs [I] Array of glyphs.
2632 * pGlyphProps [I] Array of attributes for the retrieved glyphs
2633 * cGlyphs [I] Count of Glyphs
2634 * piAdvance [O] Array of advance widths.
2635 * pGoffset [O] Glyph offsets.
2636 * pABC [O] Combined ABC width.
2638 * RETURNS
2639 * Success: S_OK
2640 * Failure: Non-zero HRESULT value.
2643 HRESULT WINAPI ScriptPlaceOpenType( HDC hdc, SCRIPT_CACHE *psc, SCRIPT_ANALYSIS *psa,
2644 OPENTYPE_TAG tagScript, OPENTYPE_TAG tagLangSys,
2645 int *rcRangeChars, TEXTRANGE_PROPERTIES **rpRangeProperties,
2646 int cRanges, const WCHAR *pwcChars, WORD *pwLogClust,
2647 SCRIPT_CHARPROP *pCharProps, int cChars,
2648 const WORD *pwGlyphs, const SCRIPT_GLYPHPROP *pGlyphProps,
2649 int cGlyphs, int *piAdvance,
2650 GOFFSET *pGoffset, ABC *pABC
2653 HRESULT hr;
2654 int i;
2656 TRACE("(%p, %p, %p, %s, %s, %p, %p, %d, %s, %p, %p, %d, %p, %p, %d, %p %p %p)\n",
2657 hdc, psc, psa,
2658 debugstr_an((char*)&tagScript,4), debugstr_an((char*)&tagLangSys,4),
2659 rcRangeChars, rpRangeProperties, cRanges, debugstr_wn(pwcChars, cChars),
2660 pwLogClust, pCharProps, cChars, pwGlyphs, pGlyphProps, cGlyphs, piAdvance,
2661 pGoffset, pABC);
2663 if (!pGlyphProps) return E_INVALIDARG;
2664 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
2665 if (!pGoffset) return E_FAIL;
2667 if (cRanges)
2668 FIXME("Ranges not supported yet\n");
2670 ((ScriptCache *)*psc)->userScript = tagScript;
2671 ((ScriptCache *)*psc)->userLang = tagLangSys;
2673 if (pABC) memset(pABC, 0, sizeof(ABC));
2674 for (i = 0; i < cGlyphs; i++)
2676 ABC abc;
2677 if (!get_cache_glyph_widths(psc, pwGlyphs[i], &abc))
2679 if (!hdc) return E_PENDING;
2680 if ((get_cache_pitch_family(psc) & TMPF_TRUETYPE) && !psa->fNoGlyphIndex)
2682 if (!GetCharABCWidthsI(hdc, 0, 1, (WORD *)&pwGlyphs[i], &abc)) return S_FALSE;
2684 else
2686 INT width;
2687 if (!GetCharWidth32W(hdc, pwGlyphs[i], pwGlyphs[i], &width)) return S_FALSE;
2688 abc.abcB = width;
2689 abc.abcA = abc.abcC = 0;
2691 set_cache_glyph_widths(psc, pwGlyphs[i], &abc);
2693 if (pABC)
2695 pABC->abcA += abc.abcA;
2696 pABC->abcB += abc.abcB;
2697 pABC->abcC += abc.abcC;
2699 /* FIXME: set to more reasonable values */
2700 pGoffset[i].du = pGoffset[i].dv = 0;
2701 if (piAdvance) piAdvance[i] = abc.abcA + abc.abcB + abc.abcC;
2704 if (pABC) TRACE("Total for run: abcA=%d, abcB=%d, abcC=%d\n", pABC->abcA, pABC->abcB, pABC->abcC);
2705 return S_OK;
2708 /***********************************************************************
2709 * ScriptPlace (USP10.@)
2711 * Produce advance widths for a run.
2713 * PARAMS
2714 * hdc [I] Device context.
2715 * psc [I/O] Opaque pointer to a script cache.
2716 * pwGlyphs [I] Array of glyphs.
2717 * cGlyphs [I] Number of glyphs in pwGlyphs.
2718 * psva [I] Array of visual attributes.
2719 * psa [I/O] String analysis.
2720 * piAdvance [O] Array of advance widths.
2721 * pGoffset [O] Glyph offsets.
2722 * pABC [O] Combined ABC width.
2724 * RETURNS
2725 * Success: S_OK
2726 * Failure: Non-zero HRESULT value.
2728 HRESULT WINAPI ScriptPlace(HDC hdc, SCRIPT_CACHE *psc, const WORD *pwGlyphs,
2729 int cGlyphs, const SCRIPT_VISATTR *psva,
2730 SCRIPT_ANALYSIS *psa, int *piAdvance, GOFFSET *pGoffset, ABC *pABC )
2732 HRESULT hr;
2733 SCRIPT_GLYPHPROP *glyphProps;
2734 int i;
2736 TRACE("(%p, %p, %p, %d, %p, %p, %p, %p, %p)\n", hdc, psc, pwGlyphs, cGlyphs, psva, psa,
2737 piAdvance, pGoffset, pABC);
2739 if (!psva) return E_INVALIDARG;
2740 if (!pGoffset) return E_FAIL;
2742 glyphProps = heap_alloc(sizeof(SCRIPT_GLYPHPROP)*cGlyphs);
2743 if (!glyphProps) return E_OUTOFMEMORY;
2745 for (i = 0; i < cGlyphs; i++)
2746 glyphProps[i].sva = psva[i];
2748 hr = ScriptPlaceOpenType(hdc, psc, psa, scriptInformation[psa->eScript].scriptTag, 0, NULL, NULL, 0, NULL, NULL, NULL, 0, pwGlyphs, glyphProps, cGlyphs, piAdvance, pGoffset, pABC);
2750 heap_free(glyphProps);
2752 return hr;
2755 /***********************************************************************
2756 * ScriptGetCMap (USP10.@)
2758 * Retrieve glyph indices.
2760 * PARAMS
2761 * hdc [I] Device context.
2762 * psc [I/O] Opaque pointer to a script cache.
2763 * pwcInChars [I] Array of Unicode characters.
2764 * cChars [I] Number of characters in pwcInChars.
2765 * dwFlags [I] Flags.
2766 * pwOutGlyphs [O] Buffer to receive the array of glyph indices.
2768 * RETURNS
2769 * Success: S_OK
2770 * Failure: Non-zero HRESULT value.
2772 HRESULT WINAPI ScriptGetCMap(HDC hdc, SCRIPT_CACHE *psc, const WCHAR *pwcInChars,
2773 int cChars, DWORD dwFlags, WORD *pwOutGlyphs)
2775 HRESULT hr;
2776 int i;
2778 TRACE("(%p,%p,%s,%d,0x%x,%p)\n", hdc, psc, debugstr_wn(pwcInChars, cChars),
2779 cChars, dwFlags, pwOutGlyphs);
2781 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
2783 hr = S_OK;
2785 if ((get_cache_pitch_family(psc) & TMPF_TRUETYPE))
2787 for (i = 0; i < cChars; i++)
2789 WCHAR inChar;
2790 if (dwFlags == SGCM_RTL)
2791 inChar = mirror_char(pwcInChars[i]);
2792 else
2793 inChar = pwcInChars[i];
2794 if (!(pwOutGlyphs[i] = get_cache_glyph(psc, inChar)))
2796 WORD glyph;
2797 if (!hdc) return E_PENDING;
2798 if (GetGlyphIndicesW(hdc, &inChar, 1, &glyph, GGI_MARK_NONEXISTING_GLYPHS) == GDI_ERROR) return S_FALSE;
2799 if (glyph == 0xffff)
2801 hr = S_FALSE;
2802 glyph = 0x0;
2804 pwOutGlyphs[i] = set_cache_glyph(psc, inChar, glyph);
2808 else
2810 TRACE("no glyph translation\n");
2811 for (i = 0; i < cChars; i++)
2813 WCHAR inChar;
2814 if (dwFlags == SGCM_RTL)
2815 inChar = mirror_char(pwcInChars[i]);
2816 else
2817 inChar = pwcInChars[i];
2818 pwOutGlyphs[i] = inChar;
2821 return hr;
2824 /***********************************************************************
2825 * ScriptTextOut (USP10.@)
2828 HRESULT WINAPI ScriptTextOut(const HDC hdc, SCRIPT_CACHE *psc, int x, int y, UINT fuOptions,
2829 const RECT *lprc, const SCRIPT_ANALYSIS *psa, const WCHAR *pwcReserved,
2830 int iReserved, const WORD *pwGlyphs, int cGlyphs, const int *piAdvance,
2831 const int *piJustify, const GOFFSET *pGoffset)
2833 HRESULT hr = S_OK;
2835 TRACE("(%p, %p, %d, %d, %04x, %p, %p, %p, %d, %p, %d, %p, %p, %p)\n",
2836 hdc, psc, x, y, fuOptions, lprc, psa, pwcReserved, iReserved, pwGlyphs, cGlyphs,
2837 piAdvance, piJustify, pGoffset);
2839 if (!hdc || !psc) return E_INVALIDARG;
2840 if (!piAdvance || !psa || !pwGlyphs) return E_INVALIDARG;
2842 fuOptions &= ETO_CLIPPED + ETO_OPAQUE;
2843 fuOptions |= ETO_IGNORELANGUAGE;
2844 if (!psa->fNoGlyphIndex) /* Have Glyphs? */
2845 fuOptions |= ETO_GLYPH_INDEX; /* Say don't do translation to glyph */
2847 if (psa->fRTL && psa->fLogicalOrder)
2849 int i;
2850 WORD *rtlGlyphs;
2852 rtlGlyphs = heap_alloc(cGlyphs * sizeof(WORD));
2853 if (!rtlGlyphs)
2854 return E_OUTOFMEMORY;
2856 for (i = 0; i < cGlyphs; i++)
2857 rtlGlyphs[i] = pwGlyphs[cGlyphs-1-i];
2859 if (!ExtTextOutW(hdc, x, y, fuOptions, lprc, rtlGlyphs, cGlyphs, NULL))
2860 hr = S_FALSE;
2861 heap_free(rtlGlyphs);
2863 else
2864 if (!ExtTextOutW(hdc, x, y, fuOptions, lprc, pwGlyphs, cGlyphs, NULL))
2865 hr = S_FALSE;
2867 return hr;
2870 /***********************************************************************
2871 * ScriptCacheGetHeight (USP10.@)
2873 * Retrieve the height of the font in the cache.
2875 * PARAMS
2876 * hdc [I] Device context.
2877 * psc [I/O] Opaque pointer to a script cache.
2878 * height [O] Receives font height.
2880 * RETURNS
2881 * Success: S_OK
2882 * Failure: Non-zero HRESULT value.
2884 HRESULT WINAPI ScriptCacheGetHeight(HDC hdc, SCRIPT_CACHE *psc, LONG *height)
2886 HRESULT hr;
2888 TRACE("(%p, %p, %p)\n", hdc, psc, height);
2890 if (!height) return E_INVALIDARG;
2891 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
2893 *height = get_cache_height(psc);
2894 return S_OK;
2897 /***********************************************************************
2898 * ScriptGetGlyphABCWidth (USP10.@)
2900 * Retrieve the width of a glyph.
2902 * PARAMS
2903 * hdc [I] Device context.
2904 * psc [I/O] Opaque pointer to a script cache.
2905 * glyph [I] Glyph to retrieve the width for.
2906 * abc [O] ABC widths of the glyph.
2908 * RETURNS
2909 * Success: S_OK
2910 * Failure: Non-zero HRESULT value.
2912 HRESULT WINAPI ScriptGetGlyphABCWidth(HDC hdc, SCRIPT_CACHE *psc, WORD glyph, ABC *abc)
2914 HRESULT hr;
2916 TRACE("(%p, %p, 0x%04x, %p)\n", hdc, psc, glyph, abc);
2918 if (!abc) return E_INVALIDARG;
2919 if ((hr = init_script_cache(hdc, psc)) != S_OK) return hr;
2921 if (!get_cache_glyph_widths(psc, glyph, abc))
2923 if (!hdc) return E_PENDING;
2924 if ((get_cache_pitch_family(psc) & TMPF_TRUETYPE))
2926 if (!GetCharABCWidthsI(hdc, 0, 1, &glyph, abc)) return S_FALSE;
2928 else
2930 INT width;
2931 if (!GetCharWidth32W(hdc, glyph, glyph, &width)) return S_FALSE;
2932 abc->abcB = width;
2933 abc->abcA = abc->abcC = 0;
2935 set_cache_glyph_widths(psc, glyph, abc);
2937 return S_OK;
2940 /***********************************************************************
2941 * ScriptLayout (USP10.@)
2943 * Map embedding levels to visual and/or logical order.
2945 * PARAMS
2946 * runs [I] Size of level array.
2947 * level [I] Array of embedding levels.
2948 * vistolog [O] Map of embedding levels from visual to logical order.
2949 * logtovis [O] Map of embedding levels from logical to visual order.
2951 * RETURNS
2952 * Success: S_OK
2953 * Failure: Non-zero HRESULT value.
2955 * BUGS
2956 * This stub works correctly for any sequence of a single
2957 * embedding level but not for sequences of different
2958 * embedding levels, i.e. mixtures of RTL and LTR scripts.
2960 HRESULT WINAPI ScriptLayout(int runs, const BYTE *level, int *vistolog, int *logtovis)
2962 int* indexs;
2963 int ich;
2965 TRACE("(%d, %p, %p, %p)\n", runs, level, vistolog, logtovis);
2967 if (!level || (!vistolog && !logtovis))
2968 return E_INVALIDARG;
2970 indexs = heap_alloc(sizeof(int) * runs);
2971 if (!indexs)
2972 return E_OUTOFMEMORY;
2975 if (vistolog)
2977 for( ich = 0; ich < runs; ich++)
2978 indexs[ich] = ich;
2980 ich = 0;
2981 while (ich < runs)
2982 ich += BIDI_ReorderV2lLevel(0, indexs+ich, level+ich, runs - ich, FALSE);
2983 for (ich = 0; ich < runs; ich++)
2984 vistolog[ich] = indexs[ich];
2988 if (logtovis)
2990 for( ich = 0; ich < runs; ich++)
2991 indexs[ich] = ich;
2993 ich = 0;
2994 while (ich < runs)
2995 ich += BIDI_ReorderL2vLevel(0, indexs+ich, level+ich, runs - ich, FALSE);
2996 for (ich = 0; ich < runs; ich++)
2997 logtovis[ich] = indexs[ich];
2999 heap_free(indexs);
3001 return S_OK;
3004 /***********************************************************************
3005 * ScriptStringGetLogicalWidths (USP10.@)
3007 * Returns logical widths from a string analysis.
3009 * PARAMS
3010 * ssa [I] string analysis.
3011 * piDx [O] logical widths returned.
3013 * RETURNS
3014 * Success: S_OK
3015 * Failure: a non-zero HRESULT.
3017 HRESULT WINAPI ScriptStringGetLogicalWidths(SCRIPT_STRING_ANALYSIS ssa, int *piDx)
3019 int i, j, next = 0;
3020 StringAnalysis *analysis = ssa;
3022 TRACE("%p, %p\n", ssa, piDx);
3024 if (!analysis) return S_FALSE;
3025 if (!(analysis->dwFlags & SSA_GLYPHS)) return S_FALSE;
3027 for (i = 0; i < analysis->numItems; i++)
3029 int cChar = analysis->pItem[i+1].iCharPos - analysis->pItem[i].iCharPos;
3030 int direction = 1;
3032 if (analysis->pItem[i].a.fRTL && ! analysis->pItem[i].a.fLogicalOrder)
3033 direction = -1;
3035 for (j = 0; j < cChar; j++)
3037 int k;
3038 int glyph = analysis->glyphs[i].pwLogClust[j];
3039 int clust_size = get_cluster_size(analysis->glyphs[i].pwLogClust,
3040 cChar, j, direction, NULL, NULL);
3041 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);
3043 for (k = 0; k < clust_size; k++)
3045 piDx[next] = advance / clust_size;
3046 next++;
3047 if (k) j++;
3051 return S_OK;
3054 /***********************************************************************
3055 * ScriptStringValidate (USP10.@)
3057 * Validate a string analysis.
3059 * PARAMS
3060 * ssa [I] string analysis.
3062 * RETURNS
3063 * Success: S_OK
3064 * Failure: S_FALSE if invalid sequences are found
3065 * or a non-zero HRESULT if it fails.
3067 HRESULT WINAPI ScriptStringValidate(SCRIPT_STRING_ANALYSIS ssa)
3069 StringAnalysis *analysis = ssa;
3071 TRACE("(%p)\n", ssa);
3073 if (!analysis) return E_INVALIDARG;
3074 return (analysis->invalid) ? S_FALSE : S_OK;
3077 /***********************************************************************
3078 * ScriptString_pSize (USP10.@)
3080 * Retrieve width and height of an analysed string.
3082 * PARAMS
3083 * ssa [I] string analysis.
3085 * RETURNS
3086 * Success: Pointer to a SIZE structure.
3087 * Failure: NULL
3089 const SIZE * WINAPI ScriptString_pSize(SCRIPT_STRING_ANALYSIS ssa)
3091 int i, j;
3092 StringAnalysis *analysis = ssa;
3094 TRACE("(%p)\n", ssa);
3096 if (!analysis) return NULL;
3097 if (!(analysis->dwFlags & SSA_GLYPHS)) return NULL;
3099 if (!analysis->sz)
3101 if (!(analysis->sz = heap_alloc(sizeof(SIZE)))) return NULL;
3102 analysis->sz->cy = analysis->glyphs[0].sc->tm.tmHeight;
3104 analysis->sz->cx = 0;
3105 for (i = 0; i < analysis->numItems; i++)
3107 if (analysis->glyphs[i].sc->tm.tmHeight > analysis->sz->cy)
3108 analysis->sz->cy = analysis->glyphs[i].sc->tm.tmHeight;
3109 for (j = 0; j < analysis->glyphs[i].numGlyphs; j++)
3110 analysis->sz->cx += analysis->glyphs[i].piAdvance[j];
3113 return analysis->sz;
3116 /***********************************************************************
3117 * ScriptString_pLogAttr (USP10.@)
3119 * Retrieve logical attributes of an analysed string.
3121 * PARAMS
3122 * ssa [I] string analysis.
3124 * RETURNS
3125 * Success: Pointer to an array of SCRIPT_LOGATTR structures.
3126 * Failure: NULL
3128 const SCRIPT_LOGATTR * WINAPI ScriptString_pLogAttr(SCRIPT_STRING_ANALYSIS ssa)
3130 StringAnalysis *analysis = ssa;
3132 TRACE("(%p)\n", ssa);
3134 if (!analysis) return NULL;
3135 if (!(analysis->dwFlags & SSA_BREAK)) return NULL;
3136 return analysis->logattrs;
3139 /***********************************************************************
3140 * ScriptString_pcOutChars (USP10.@)
3142 * Retrieve the length of a string after clipping.
3144 * PARAMS
3145 * ssa [I] String analysis.
3147 * RETURNS
3148 * Success: Pointer to the length.
3149 * Failure: NULL
3151 const int * WINAPI ScriptString_pcOutChars(SCRIPT_STRING_ANALYSIS ssa)
3153 StringAnalysis *analysis = ssa;
3155 TRACE("(%p)\n", ssa);
3157 if (!analysis) return NULL;
3158 return &analysis->clip_len;
3161 /***********************************************************************
3162 * ScriptStringGetOrder (USP10.@)
3164 * Retrieve a glyph order map.
3166 * PARAMS
3167 * ssa [I] String analysis.
3168 * order [I/O] Array of glyph positions.
3170 * RETURNS
3171 * Success: S_OK
3172 * Failure: a non-zero HRESULT.
3174 HRESULT WINAPI ScriptStringGetOrder(SCRIPT_STRING_ANALYSIS ssa, UINT *order)
3176 int i, j;
3177 unsigned int k;
3178 StringAnalysis *analysis = ssa;
3180 TRACE("(%p)\n", ssa);
3182 if (!analysis) return S_FALSE;
3183 if (!(analysis->dwFlags & SSA_GLYPHS)) return S_FALSE;
3185 /* FIXME: handle RTL scripts */
3186 for (i = 0, k = 0; i < analysis->numItems; i++)
3187 for (j = 0; j < analysis->glyphs[i].numGlyphs; j++, k++)
3188 order[k] = k;
3190 return S_OK;
3193 /***********************************************************************
3194 * ScriptGetLogicalWidths (USP10.@)
3196 * Convert advance widths to logical widths.
3198 * PARAMS
3199 * sa [I] Script analysis.
3200 * nbchars [I] Number of characters.
3201 * nbglyphs [I] Number of glyphs.
3202 * glyph_width [I] Array of glyph widths.
3203 * log_clust [I] Array of logical clusters.
3204 * sva [I] Visual attributes.
3205 * widths [O] Array of logical widths.
3207 * RETURNS
3208 * Success: S_OK
3209 * Failure: a non-zero HRESULT.
3211 HRESULT WINAPI ScriptGetLogicalWidths(const SCRIPT_ANALYSIS *sa, int nbchars, int nbglyphs,
3212 const int *glyph_width, const WORD *log_clust,
3213 const SCRIPT_VISATTR *sva, int *widths)
3215 int i;
3217 TRACE("(%p, %d, %d, %p, %p, %p, %p)\n",
3218 sa, nbchars, nbglyphs, glyph_width, log_clust, sva, widths);
3220 /* FIXME */
3221 for (i = 0; i < nbchars; i++) widths[i] = glyph_width[i];
3222 return S_OK;
3225 /***********************************************************************
3226 * ScriptApplyLogicalWidth (USP10.@)
3228 * Generate glyph advance widths.
3230 * PARAMS
3231 * dx [I] Array of logical advance widths.
3232 * num_chars [I] Number of characters.
3233 * num_glyphs [I] Number of glyphs.
3234 * log_clust [I] Array of logical clusters.
3235 * sva [I] Visual attributes.
3236 * advance [I] Array of glyph advance widths.
3237 * sa [I] Script analysis.
3238 * abc [I/O] Summed ABC widths.
3239 * justify [O] Array of glyph advance widths.
3241 * RETURNS
3242 * Success: S_OK
3243 * Failure: a non-zero HRESULT.
3245 HRESULT WINAPI ScriptApplyLogicalWidth(const int *dx, int num_chars, int num_glyphs,
3246 const WORD *log_clust, const SCRIPT_VISATTR *sva,
3247 const int *advance, const SCRIPT_ANALYSIS *sa,
3248 ABC *abc, int *justify)
3250 int i;
3252 FIXME("(%p, %d, %d, %p, %p, %p, %p, %p, %p)\n",
3253 dx, num_chars, num_glyphs, log_clust, sva, advance, sa, abc, justify);
3255 for (i = 0; i < num_chars; i++) justify[i] = advance[i];
3256 return S_OK;
3259 HRESULT WINAPI ScriptJustify(const SCRIPT_VISATTR *sva, const int *advance,
3260 int num_glyphs, int dx, int min_kashida, int *justify)
3262 int i;
3264 FIXME("(%p, %p, %d, %d, %d, %p)\n", sva, advance, num_glyphs, dx, min_kashida, justify);
3266 for (i = 0; i < num_glyphs; i++) justify[i] = advance[i];
3267 return S_OK;