wintrust: Only set the error on the root element of the chain.
[wine] / dlls / usp10 / usp10.c
1 /*
2  * Implementation of Uniscribe Script Processor (usp10.dll)
3  *
4  * Copyright 2005 Steven Edwards for CodeWeavers
5  * Copyright 2006 Hans Leidekker
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  *
21  * Notes:
22  * Uniscribe allows for processing of complex scripts such as joining
23  * and filtering characters and bi-directional text with custom line breaks.
24  */
25
26 #include <stdarg.h>
27
28 #include "windef.h"
29 #include "winbase.h"
30 #include "wingdi.h"
31 #include "winuser.h"
32 #include "winnls.h"
33 #include "usp10.h"
34
35 #include "wine/debug.h"
36 #include "wine/unicode.h"
37
38 WINE_DEFAULT_DEBUG_CHANNEL(uniscribe);
39
40 static const SCRIPT_PROPERTIES props[] =
41 {
42     { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
43     { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
44     { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
45     { 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
46     { 9, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
47     { 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 },
48     { 9, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 },
49     { 8, 0, 0, 0, 0, 161, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
50     { 25, 0, 0, 0, 0, 204, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
51     { 43, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 },
52     { 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 },
53     { 42, 0, 0, 0, 0, 163, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
54     { 9, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0 },
55     { 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0 },
56     { 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 },
57     { 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 },
58     { 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 },
59     { 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 },
60     { 18, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 },
61     { 18, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 },
62     { 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 },
63     { 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 },
64     { 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 },
65     { 9, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0 },
66     { 13, 0, 1, 0, 1, 177, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
67     { 13, 0, 1, 0, 0, 177, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
68     { 1, 0, 1, 0, 0, 178, 0, 0, 0, 0, 0, 0, 1, 1, 0 },
69     { 1, 1, 1, 0, 0, 178, 0, 0, 0, 0, 0, 0, 1, 0, 0 },
70     { 41, 1, 1, 0, 0, 178, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
71     { 32, 1, 1, 0, 0, 178, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
72     { 90, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0 },
73     { 30, 0, 1, 1, 1, 222, 0, 0, 1, 0, 1, 0, 0, 0, 1 },
74     { 30, 1, 1, 0, 0, 222, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
75     { 30, 0, 1, 0, 0, 222, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
76     { 57, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0 },
77     { 57, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
78     { 73, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0 },
79     { 73, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
80     { 69, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0 },
81     { 69, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
82     { 69, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
83     { 70, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0 },
84     { 70, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
85     { 71, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0 },
86     { 71, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
87     { 72, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0 },
88     { 72, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
89     { 74, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0 },
90     { 74, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
91     { 75, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0 },
92     { 75, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
93     { 76, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0 },
94     { 76, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
95     { 81, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0 },
96     { 81, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
97     { 84, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0 },
98     { 84, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
99     { 83, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0 },
100     { 83, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
101     { 85, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0 },
102     { 85, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
103     { 80, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
104     { 80, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
105     { 94, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
106     { 94, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
107     { 101, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
108     { 93, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
109     { 92, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
110     { 9, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
111     { 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
112     { 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
113     { 91, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
114     { 9, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0 },
115     { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
116 };
117
118 static const SCRIPT_PROPERTIES *script_props[] =
119 {
120     &props[0], &props[1], &props[2], &props[3],
121     &props[4], &props[5], &props[6], &props[7],
122     &props[8], &props[9], &props[11], &props[12],
123     &props[13], &props[14], &props[15], &props[16],
124     &props[17], &props[18], &props[19], &props[20],
125     &props[21], &props[22], &props[23], &props[24],
126     &props[25], &props[26], &props[27], &props[28],
127     &props[29], &props[30], &props[31], &props[32],
128     &props[33], &props[34], &props[35], &props[36],
129     &props[37], &props[38], &props[39], &props[40],
130     &props[41], &props[42], &props[43], &props[44],
131     &props[45], &props[46], &props[47], &props[48],
132     &props[49], &props[50], &props[51], &props[52],
133     &props[53], &props[54], &props[55], &props[56],
134     &props[57], &props[58], &props[59], &props[60],
135     &props[61], &props[62], &props[63], &props[64],
136     &props[65], &props[66], &props[67], &props[68],
137     &props[69], &props[70], &props[71], &props[72],
138     &props[73]
139 };
140
141 typedef struct {
142     HDC hdc;
143     LOGFONTW lf;
144     TEXTMETRICW tm;
145 } ScriptCache;
146
147 typedef struct {
148     int numGlyphs;
149     WORD* glyphs;
150     WORD* pwLogClust;
151     int* piAdvance;
152     SCRIPT_VISATTR* psva;
153     GOFFSET* pGoffset;
154     ABC* abc;
155 } StringGlyphs;
156
157 typedef struct {
158     BOOL invalid;
159     int clip_len;
160     ScriptCache *sc;
161     int cItems;
162     int cMaxGlyphs;
163     SCRIPT_ITEM* pItem;
164     int numItems;
165     StringGlyphs* glyphs;
166     SCRIPT_LOGATTR* logattrs;
167     SIZE* sz;
168 } StringAnalysis;
169
170 static inline void *heap_alloc(SIZE_T size)
171 {
172     return HeapAlloc(GetProcessHeap(), 0, size);
173 }
174
175 static inline void *heap_alloc_zero(SIZE_T size)
176 {
177     return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);
178 }
179
180 static inline void *heap_realloc_zero(LPVOID mem, SIZE_T size)
181 {
182     return HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, mem, size);
183 }
184
185 static inline BOOL heap_free(LPVOID mem)
186 {
187     return HeapFree(GetProcessHeap(), 0, mem);
188 }
189
190 static HDC get_cache_hdc(SCRIPT_CACHE *psc)
191 {
192     return ((ScriptCache *)*psc)->hdc;
193 }
194
195 static WCHAR get_cache_default_char(SCRIPT_CACHE *psc)
196 {
197     return ((ScriptCache *)*psc)->tm.tmDefaultChar;
198 }
199
200 static LONG get_cache_height(SCRIPT_CACHE *psc)
201 {
202     return ((ScriptCache *)*psc)->tm.tmHeight;
203 }
204
205 static BYTE get_cache_pitch_family(SCRIPT_CACHE *psc)
206 {
207     return ((ScriptCache *)*psc)->tm.tmPitchAndFamily;
208 }
209
210 static HRESULT init_script_cache(const HDC hdc, ScriptCache *sc)
211 {
212     if (!GetTextMetricsW(hdc, &sc->tm)) return E_INVALIDARG;
213     if (!GetObjectW(GetCurrentObject(hdc, OBJ_FONT), sizeof(LOGFONTW), &sc->lf)) return E_INVALIDARG;
214     sc->hdc = hdc;
215     return S_OK;
216 }
217
218 static HRESULT get_script_cache(const HDC hdc, SCRIPT_CACHE *psc)
219 {
220     if (!psc) return E_INVALIDARG;
221     if (!*psc)
222     {
223         HRESULT ret;
224         ScriptCache *sc;
225
226         if (!hdc) return E_PENDING;
227         if (!(sc = heap_alloc_zero(sizeof(ScriptCache)))) return E_OUTOFMEMORY;
228         ret = init_script_cache(hdc, sc);
229         if (ret != S_OK)
230         {
231             heap_free(sc);
232             return ret;
233         }
234         *psc = sc;
235     }
236     TRACE("<- %p\n", *psc);
237     return S_OK;
238 }
239
240 static HFONT select_cached_font(SCRIPT_CACHE *psc)
241 {
242     HFONT old_font;
243     ScriptCache *sc = *psc;
244
245     old_font = SelectObject(sc->hdc, CreateFontIndirectW(&sc->lf));
246     return old_font;
247 }
248
249 static void unselect_cached_font(SCRIPT_CACHE *psc, HFONT old_font)
250 {
251     ScriptCache *sc = *psc;
252     DeleteObject(SelectObject(sc->hdc, old_font));
253 }
254
255 /***********************************************************************
256  *      DllMain
257  *
258  */
259 BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpv)
260 {
261     switch(fdwReason)
262     {
263     case DLL_PROCESS_ATTACH:
264         DisableThreadLibraryCalls(hInstDLL);
265         break;
266     case DLL_PROCESS_DETACH:
267         break;
268     }
269     return TRUE;
270 }
271
272 /***********************************************************************
273  *      ScriptFreeCache (USP10.@)
274  *
275  * Free a script cache.
276  *
277  * PARAMS
278  *   psc [I/O] Script cache.
279  *
280  * RETURNS
281  *  Success: S_OK
282  *  Failure: Non-zero HRESULT value.
283  */
284 HRESULT WINAPI ScriptFreeCache(SCRIPT_CACHE *psc)
285 {
286     TRACE("%p\n", psc);
287
288     if (psc)
289     {
290        heap_free(*psc);
291        *psc = NULL;
292     }
293     return S_OK;
294 }
295
296 /***********************************************************************
297  *      ScriptGetProperties (USP10.@)
298  *
299  * Retrieve a list of script properties.
300  *
301  * PARAMS
302  *  props [I] Pointer to an array of SCRIPT_PROPERTIES pointers.
303  *  num   [I] Pointer to the number of scripts.
304  *
305  * RETURNS
306  *  Success: S_OK
307  *  Failure: Non-zero HRESULT value.
308  *
309  * NOTES
310  *  Behaviour matches WinXP.
311  */
312 HRESULT WINAPI ScriptGetProperties(const SCRIPT_PROPERTIES ***props, int *num)
313 {
314     TRACE("(%p,%p)\n", props, num);
315
316     if (!props && !num) return E_INVALIDARG;
317
318     if (num) *num = sizeof(script_props)/sizeof(script_props[0]);
319     if (props) *props = script_props;
320
321     return S_OK;
322 }
323
324 /***********************************************************************
325  *      ScriptGetFontProperties (USP10.@)
326  *
327  * Get information on special glyphs.
328  *
329  * PARAMS
330  *  hdc [I]   Device context.
331  *  psc [I/O] Opaque pointer to a script cache.
332  *  sfp [O]   Font properties structure.
333  */
334 HRESULT WINAPI ScriptGetFontProperties(HDC hdc, SCRIPT_CACHE *psc, SCRIPT_FONTPROPERTIES *sfp)
335 {
336     HRESULT hr;
337
338     TRACE("%p,%p,%p\n", hdc, psc, sfp);
339
340     if (!sfp) return E_INVALIDARG;
341
342     hr = get_script_cache(hdc, psc);
343     if (hr != S_OK)
344         return hr;
345
346     if (sfp->cBytes != sizeof(SCRIPT_FONTPROPERTIES))
347         return E_INVALIDARG;
348
349     /* return something sensible? */
350     sfp->wgBlank = 0;
351     sfp->wgDefault = get_cache_default_char(psc);
352     sfp->wgInvalid = 0;
353     sfp->wgKashida = 0xffff;
354     sfp->iKashidaWidth = 0;
355
356     return S_OK;
357 }
358
359 /***********************************************************************
360  *      ScriptRecordDigitSubstitution (USP10.@)
361  *
362  *  Record digit substitution settings for a given locale.
363  *
364  *  PARAMS
365  *   locale [I] Locale identifier.
366  *   sds    [I] Structure to record substitution settings.
367  *
368  *  RETURNS
369  *   Success: S_OK
370  *   Failure: E_POINTER if sds is NULL, E_INVALIDARG otherwise.
371  *
372  *  SEE ALSO
373  *   http://blogs.msdn.com/michkap/archive/2006/02/22/536877.aspx
374  */
375 HRESULT WINAPI ScriptRecordDigitSubstitution(LCID locale, SCRIPT_DIGITSUBSTITUTE *sds)
376 {
377     DWORD plgid, sub;
378
379     TRACE("0x%x, %p\n", locale, sds);
380
381     /* This implementation appears to be correct for all languages, but it's
382      * not clear if sds->DigitSubstitute is ever set to anything except 
383      * CONTEXT or NONE in reality */
384
385     if (!sds) return E_POINTER;
386
387     locale = ConvertDefaultLocale(locale);
388
389     if (!IsValidLocale(locale, LCID_INSTALLED))
390         return E_INVALIDARG;
391
392     plgid = PRIMARYLANGID(LANGIDFROMLCID(locale));
393     sds->TraditionalDigitLanguage = plgid;
394
395     if (plgid == LANG_ARABIC || plgid == LANG_FARSI)
396         sds->NationalDigitLanguage = plgid;
397     else
398         sds->NationalDigitLanguage = LANG_ENGLISH;
399
400     if (!GetLocaleInfoW(locale, LOCALE_IDIGITSUBSTITUTION | LOCALE_RETURN_NUMBER,
401                         (LPWSTR)&sub, sizeof(sub)/sizeof(WCHAR))) return E_INVALIDARG;
402
403     switch (sub)
404     {
405     case 0: 
406         if (plgid == LANG_ARABIC || plgid == LANG_FARSI)
407             sds->DigitSubstitute = SCRIPT_DIGITSUBSTITUTE_CONTEXT;
408         else
409             sds->DigitSubstitute = SCRIPT_DIGITSUBSTITUTE_NONE;
410         break;
411     case 1:
412         sds->DigitSubstitute = SCRIPT_DIGITSUBSTITUTE_NONE;
413         break;
414     case 2:
415         sds->DigitSubstitute = SCRIPT_DIGITSUBSTITUTE_NATIONAL;
416         break;
417     default:
418         sds->DigitSubstitute = SCRIPT_DIGITSUBSTITUTE_TRADITIONAL;
419         break;
420     }
421
422     sds->dwReserved = 0;
423     return S_OK;
424 }
425
426 /***********************************************************************
427  *      ScriptApplyDigitSubstitution (USP10.@)
428  *
429  *  Apply digit substitution settings.
430  *
431  *  PARAMS
432  *   sds [I] Structure with recorded substitution settings.
433  *   sc  [I] Script control structure.
434  *   ss  [I] Script state structure.
435  *
436  *  RETURNS
437  *   Success: S_OK
438  *   Failure: E_INVALIDARG if sds is invalid. Otherwise an HRESULT.
439  */
440 HRESULT WINAPI ScriptApplyDigitSubstitution(const SCRIPT_DIGITSUBSTITUTE *sds, 
441                                             SCRIPT_CONTROL *sc, SCRIPT_STATE *ss)
442 {
443     SCRIPT_DIGITSUBSTITUTE psds;
444
445     TRACE("%p, %p, %p\n", sds, sc, ss);
446
447     if (!sc || !ss) return E_POINTER;
448     if (!sds)
449     {
450         sds = &psds;
451         if (ScriptRecordDigitSubstitution(LOCALE_USER_DEFAULT, &psds) != S_OK)
452             return E_INVALIDARG;
453     }
454
455     sc->uDefaultLanguage = LANG_ENGLISH;
456     sc->fContextDigits = 0;
457     ss->fDigitSubstitute = 0;
458
459     switch (sds->DigitSubstitute) {
460         case SCRIPT_DIGITSUBSTITUTE_CONTEXT:
461         case SCRIPT_DIGITSUBSTITUTE_NATIONAL:
462         case SCRIPT_DIGITSUBSTITUTE_NONE:
463         case SCRIPT_DIGITSUBSTITUTE_TRADITIONAL:
464             return S_OK;
465         default:
466             return E_INVALIDARG;
467     }
468 }
469
470 /***********************************************************************
471  *      ScriptItemize (USP10.@)
472  *
473  * Split a Unicode string into shapeable parts.
474  *
475  * PARAMS
476  *  pwcInChars [I] String to split.
477  *  cInChars   [I] Number of characters in pwcInChars.
478  *  cMaxItems  [I] Maximum number of items to return.
479  *  psControl  [I] Pointer to a SCRIPT_CONTROL structure.
480  *  psState    [I] Pointer to a SCRIPT_STATE structure.
481  *  pItems     [O] Buffer to receive SCRIPT_ITEM structures.
482  *  pcItems    [O] Number of script items returned.
483  *
484  * RETURNS
485  *  Success: S_OK
486  *  Failure: Non-zero HRESULT value.
487  */
488 HRESULT WINAPI ScriptItemize(const WCHAR *pwcInChars, int cInChars, int cMaxItems,
489                              const SCRIPT_CONTROL *psControl, const SCRIPT_STATE *psState,
490                              SCRIPT_ITEM *pItems, int *pcItems)
491 {
492
493 #define Numeric_start 0x0030
494 #define Numeric_stop  0x0039
495 #define Numeric_space 0x0020
496 #define Arabic_start  0x0600
497 #define Arabic_stop   0x06ff
498 #define Latin_start   0x0001
499 #define Latin_stop    0x024f
500 #define Script_Arabic  6
501 #define Script_Latin   1
502 #define Script_Numeric 5
503
504     int   cnt = 0, index = 0;
505     int   New_Script = SCRIPT_UNDEFINED;
506
507     TRACE("%s,%d,%d,%p,%p,%p,%p\n", debugstr_wn(pwcInChars, cInChars), cInChars, cMaxItems, 
508           psControl, psState, pItems, pcItems);
509
510     if (!pwcInChars || !cInChars || !pItems || cMaxItems < 2)
511         return E_INVALIDARG;
512
513     pItems[index].iCharPos = 0;
514     memset(&pItems[index].a, 0, sizeof(SCRIPT_ANALYSIS));
515
516     if  (pwcInChars[cnt] >= Numeric_start && pwcInChars[cnt] <= Numeric_stop)
517         pItems[index].a.eScript = Script_Numeric;
518     else
519     if  (pwcInChars[cnt] >= Arabic_start && pwcInChars[cnt] <= Arabic_stop)
520         pItems[index].a.eScript = Script_Arabic;
521     else
522     if  (pwcInChars[cnt] >= Latin_start && pwcInChars[cnt] <= Latin_stop)
523         pItems[index].a.eScript = Script_Latin;
524
525     if  (pItems[index].a.eScript  == Script_Arabic)
526         pItems[index].a.s.uBidiLevel = 1;
527
528     TRACE("New_Script=%d, eScript=%d index=%d cnt=%d iCharPos=%d\n",
529           New_Script, pItems[index].a.eScript, index, cnt,
530           pItems[index].iCharPos = cnt);
531
532     for (cnt=0; cnt < cInChars; cnt++)
533     {
534         if  ((pwcInChars[cnt] >= Numeric_start && pwcInChars[cnt] <= Numeric_stop)
535              || (New_Script == Script_Numeric && pwcInChars[cnt] == Numeric_space))
536             New_Script = Script_Numeric;
537         else
538         if  ((pwcInChars[cnt] >= Arabic_start && pwcInChars[cnt] <= Arabic_stop)
539              || (New_Script == Script_Arabic && pwcInChars[cnt] == Numeric_space))
540             New_Script = Script_Arabic;
541         else
542         if  ((WCHAR) pwcInChars[cnt] >= Latin_start && (WCHAR) pwcInChars[cnt] <= Latin_stop)
543             New_Script = Script_Latin;
544         else
545             New_Script = SCRIPT_UNDEFINED;
546
547         if  (New_Script != pItems[index].a.eScript)
548         {
549             TRACE("New_Script=%d, eScript=%d ", New_Script, pItems[index].a.eScript);
550             index++;
551             if  (index+1 > cMaxItems)
552                 return E_OUTOFMEMORY;
553
554             pItems[index].iCharPos = cnt;
555             memset(&pItems[index].a, 0, sizeof(SCRIPT_ANALYSIS));
556
557             if  (New_Script == Script_Arabic)
558                 pItems[index].a.s.uBidiLevel = 1;
559
560             pItems[index].a.eScript = New_Script;
561             if  (New_Script == Script_Arabic)
562                 pItems[index].a.s.uBidiLevel = 1;
563
564             TRACE("index=%d cnt=%d iCharPos=%d\n", index, cnt, pItems[index].iCharPos = cnt);
565         }
566     }
567
568     /* While not strictly necessary according to the spec, make sure the n+1
569      * item is set up to prevent random behaviour if the caller erroneously
570      * checks the n+1 structure                                              */
571     memset(&pItems[index+1].a, 0, sizeof(SCRIPT_ANALYSIS));
572
573     TRACE("index=%d cnt=%d iCharPos=%d\n", index+1, cnt, pItems[index+1].iCharPos = cnt);
574
575     /*  Set one SCRIPT_STATE item being returned  */
576     if (pcItems) *pcItems = index + 1;
577
578     /*  Set SCRIPT_ITEM                                     */
579     pItems[index+1].iCharPos = cnt;       /* the last + 1 item
580                                              contains the ptr to the lastchar */
581     return S_OK;
582 }
583
584 /***********************************************************************
585  *      ScriptStringAnalyse (USP10.@)
586  *
587  */
588 HRESULT WINAPI ScriptStringAnalyse(HDC hdc, const void *pString, int cString,
589                                    int cGlyphs, int iCharset, DWORD dwFlags,
590                                    int iReqWidth, SCRIPT_CONTROL *psControl,
591                                    SCRIPT_STATE *psState, const int *piDx,
592                                    SCRIPT_TABDEF *pTabdef, const BYTE *pbInClass,
593                                    SCRIPT_STRING_ANALYSIS *pssa)
594 {
595     HRESULT hr = E_OUTOFMEMORY;
596     StringAnalysis *analysis = NULL;
597     int i, num_items = 255;
598
599     TRACE("(%p,%p,%d,%d,%d,0x%x,%d,%p,%p,%p,%p,%p,%p)\n",
600           hdc, pString, cString, cGlyphs, iCharset, dwFlags, iReqWidth,
601           psControl, psState, piDx, pTabdef, pbInClass, pssa);
602
603     if (iCharset != -1)
604     {
605         FIXME("Only Unicode strings are supported\n");
606         return E_INVALIDARG;
607     }
608     if (cString < 1 || !pString) return E_INVALIDARG;
609     if ((dwFlags & SSA_GLYPHS) && !hdc) return E_PENDING;
610
611     if (!(analysis = heap_alloc_zero(sizeof(StringAnalysis)))) return E_OUTOFMEMORY;
612     if (!(analysis->pItem = heap_alloc_zero(num_items * sizeof(SCRIPT_ITEM) + 1))) goto error;
613
614     /* FIXME: handle clipping */
615     analysis->clip_len = cString;
616
617     hr = ScriptItemize(pString, cString, num_items, psControl, psState, analysis->pItem,
618                        &analysis->numItems);
619
620     while (hr == E_OUTOFMEMORY)
621     {
622         SCRIPT_ITEM *tmp;
623
624         num_items *= 2;
625         if (!(tmp = heap_realloc_zero(analysis->pItem, num_items * sizeof(SCRIPT_ITEM) + 1)))
626             goto error;
627
628         analysis->pItem = tmp;
629         hr = ScriptItemize(pString, cString, num_items, psControl, psState, analysis->pItem,
630                            &analysis->numItems);
631     }
632     if (hr != S_OK) goto error;
633
634     if ((analysis->logattrs = heap_alloc(sizeof(SCRIPT_LOGATTR) * cString)))
635         ScriptBreak(pString, cString, (SCRIPT_STRING_ANALYSIS)analysis, analysis->logattrs);
636     else
637         goto error;
638
639     if (!(analysis->glyphs = heap_alloc_zero(sizeof(StringGlyphs) * analysis->numItems)))
640         goto error;
641
642     for (i = 0; i < analysis->numItems; i++)
643     {
644         SCRIPT_CACHE *sc = (SCRIPT_CACHE *)&analysis->sc;
645         int cChar = analysis->pItem[i+1].iCharPos - analysis->pItem[i].iCharPos;
646         int numGlyphs = 1.5 * cChar + 16;
647         WORD *glyphs = heap_alloc_zero(sizeof(WORD) * numGlyphs);
648         WORD *pwLogClust = heap_alloc_zero(sizeof(WORD) * cChar);
649         int *piAdvance = heap_alloc_zero(sizeof(int) * numGlyphs);
650         SCRIPT_VISATTR *psva = heap_alloc_zero(sizeof(SCRIPT_VISATTR) * cChar);
651         GOFFSET *pGoffset = heap_alloc_zero(sizeof(GOFFSET) * numGlyphs);
652         ABC *abc = heap_alloc_zero(sizeof(ABC));
653         int numGlyphsReturned;
654
655         /* FIXME: non unicode strings */
656         WCHAR* pStr = (WCHAR*)pString;
657         hr = ScriptShape(hdc, sc, &pStr[analysis->pItem[i].iCharPos],
658                          cChar, numGlyphs, &analysis->pItem[i].a,
659                          glyphs, pwLogClust, psva, &numGlyphsReturned);
660         hr = ScriptPlace(hdc, sc, glyphs, numGlyphsReturned, psva, &analysis->pItem[i].a,
661                          piAdvance, pGoffset, abc);
662
663         analysis->glyphs[i].numGlyphs = numGlyphsReturned;
664         analysis->glyphs[i].glyphs = glyphs;
665         analysis->glyphs[i].pwLogClust = pwLogClust;
666         analysis->glyphs[i].piAdvance = piAdvance;
667         analysis->glyphs[i].psva = psva;
668         analysis->glyphs[i].pGoffset = pGoffset;
669         analysis->glyphs[i].abc = abc;
670     }
671
672     *pssa = analysis;
673     return S_OK;
674
675 error:
676     heap_free(analysis->glyphs);
677     heap_free(analysis->logattrs);
678     heap_free(analysis->pItem);
679     heap_free(analysis->sc);
680     heap_free(analysis);
681     return hr;
682 }
683
684 /***********************************************************************
685  *      ScriptStringOut (USP10.@)
686  *
687  * This function takes the output of ScriptStringAnalyse and joins the segments
688  * of glyphs and passes the resulting string to ScriptTextOut.  ScriptStringOut
689  * only processes glyphs.
690  *
691  * Parameters:
692  *  ssa       [I] buffer to hold the analysed string components
693  *  iX        [I] X axis displacement for output
694  *  iY        [I] Y axis displacement for output
695  *  uOptions  [I] flags controling output processing
696  *  prc       [I] rectangle coordinates
697  *  iMinSel   [I] starting pos for substringing output string
698  *  iMaxSel   [I] ending pos for substringing output string
699  *  fDisabled [I] controls text highlighting
700  *
701  *  RETURNS
702  *   Success: S_OK
703  *   Failure: is the value returned by ScriptTextOut
704  */
705 HRESULT WINAPI ScriptStringOut(SCRIPT_STRING_ANALYSIS ssa,
706                                int iX,
707                                int iY, 
708                                UINT uOptions, 
709                                const RECT *prc, 
710                                int iMinSel, 
711                                int iMaxSel,
712                                BOOL fDisabled)
713 {
714     StringAnalysis *analysis;
715     WORD *glyphs;
716     int   item, cnt, x;
717     HRESULT hr;
718
719     TRACE("(%p,%d,%d,0x%1x,%p,%d,%d,%d)\n",
720          ssa, iX, iY, uOptions, prc, iMinSel, iMaxSel, fDisabled);
721
722     if (!(analysis = ssa)) return E_INVALIDARG;
723
724     /*
725      * Get storage for the output buffer for the consolidated strings
726      */
727     cnt = 0;
728     for (item = 0; item < analysis->numItems; item++)
729     {
730         cnt += analysis->glyphs[item].numGlyphs;
731     }
732     if (!(glyphs = heap_alloc(sizeof(WCHAR) * cnt))) return E_OUTOFMEMORY;
733
734     /*
735      * ScriptStringOut only processes glyphs hence set ETO_GLYPH_INDEX
736      */
737     uOptions |= ETO_GLYPH_INDEX;
738     analysis->pItem[0].a.fNoGlyphIndex = FALSE; /* say that we have glyphs */
739
740     /*
741      * Copy the string items into the output buffer
742      */
743
744     TRACE("numItems %d\n", analysis->numItems);
745
746     cnt = 0;
747     for (item = 0; item < analysis->numItems; item++)
748     {
749         memcpy(&glyphs[cnt], analysis->glyphs[item].glyphs,
750               sizeof(WCHAR) * analysis->glyphs[item].numGlyphs);
751
752         TRACE("Item %d, Glyphs %d ", item, analysis->glyphs[item].numGlyphs);
753         for (x = cnt; x < analysis->glyphs[item].numGlyphs + cnt; x ++)
754             TRACE("%04x", glyphs[x]);
755         TRACE("\n");
756
757         cnt += analysis->glyphs[item].numGlyphs; /* point to the end of the copied text */
758     }
759
760     hr = ScriptTextOut(analysis->sc->hdc, (SCRIPT_CACHE *)&analysis->sc, iX, iY,
761                        uOptions, prc, &analysis->pItem->a, NULL, 0, glyphs, cnt,
762                        analysis->glyphs->piAdvance, NULL, analysis->glyphs->pGoffset);
763     TRACE("ScriptTextOut hr=%08x\n", hr);
764
765     /*
766      * Free the output buffer and script cache
767      */
768     heap_free(glyphs);
769     return hr;
770 }
771
772 /***********************************************************************
773  *      ScriptStringCPtoX (USP10.@)
774  *
775  */
776 HRESULT WINAPI ScriptStringCPtoX(SCRIPT_STRING_ANALYSIS ssa, int icp, BOOL fTrailing, int* pX)
777 {
778     int i, j;
779     int runningX = 0;
780     int runningCp = 0;
781     StringAnalysis* analysis = ssa;
782
783     TRACE("(%p), %d, %d, (%p)\n", ssa, icp, fTrailing, pX);
784
785     if (!ssa || !pX) return S_FALSE;
786
787     /* icp out of range */
788     if(icp < 0)
789     {
790         analysis->invalid = TRUE;
791         return E_INVALIDARG;
792     }
793
794     for(i=0; i<analysis->numItems; i++)
795     {
796         for(j=0; j<analysis->glyphs[i].numGlyphs; j++)
797         {
798             if(runningCp == icp && fTrailing == FALSE)
799             {
800                 *pX = runningX;
801                 return S_OK;
802             }
803             runningX += analysis->glyphs[i].piAdvance[j];
804             if(runningCp == icp && fTrailing == TRUE)
805             {
806                 *pX = runningX;
807                 return S_OK;
808             }
809             runningCp++;
810         }
811     }
812
813     /* icp out of range */
814     analysis->invalid = TRUE;
815     return E_INVALIDARG;
816 }
817
818 /***********************************************************************
819  *      ScriptStringXtoCP (USP10.@)
820  *
821  */
822 HRESULT WINAPI ScriptStringXtoCP(SCRIPT_STRING_ANALYSIS ssa, int iX, int* piCh, int* piTrailing) 
823 {
824     StringAnalysis* analysis = ssa;
825     int i;
826     int j;
827     int runningX = 0;
828     int runningCp = 0;
829     int width;
830
831     TRACE("(%p), %d, (%p), (%p)\n", ssa, iX, piCh, piTrailing);
832
833     if (!ssa || !piCh || !piTrailing) return S_FALSE;
834
835     /* out of range */
836     if(iX < 0)
837     {
838         *piCh = -1;
839         *piTrailing = TRUE;
840         return S_OK;
841     }
842
843     for(i=0; i<analysis->numItems; i++)
844     {
845         for(j=0; j<analysis->glyphs[i].numGlyphs; j++)
846         {
847             width = analysis->glyphs[i].piAdvance[j];
848             if(iX < (runningX + width))
849             {
850                 *piCh = runningCp;
851                 if((iX - runningX) > width/2)
852                     *piTrailing = TRUE;
853                 else
854                     *piTrailing = FALSE;
855                 return S_OK;
856             }
857             runningX += width;
858             runningCp++;
859         }
860     }
861
862     /* out of range */
863     *piCh = analysis->pItem[analysis->numItems].iCharPos;
864     *piTrailing = FALSE;
865
866     return S_OK;
867 }
868
869
870 /***********************************************************************
871  *      ScriptStringFree (USP10.@)
872  *
873  * Free a string analysis.
874  *
875  * PARAMS
876  *  pssa [I] string analysis.
877  *
878  * RETURNS
879  *  Success: S_OK
880  *  Failure: Non-zero HRESULT value.
881  */
882 HRESULT WINAPI ScriptStringFree(SCRIPT_STRING_ANALYSIS *pssa)
883 {
884     StringAnalysis* analysis;
885     BOOL invalid;
886     int i;
887
888     TRACE("(%p)\n", pssa);
889
890     if (!pssa || !(analysis = *pssa)) return E_INVALIDARG;
891     invalid = analysis->invalid;
892
893     for (i = 0; i < analysis->numItems; i++)
894     {
895         heap_free(analysis->glyphs[i].glyphs);
896         heap_free(analysis->glyphs[i].pwLogClust);
897         heap_free(analysis->glyphs[i].piAdvance);
898         heap_free(analysis->glyphs[i].psva);
899         heap_free(analysis->glyphs[i].pGoffset);
900         heap_free(analysis->glyphs[i].abc);
901     }
902
903     heap_free(analysis->glyphs);
904     heap_free(analysis->pItem);
905     heap_free(analysis->logattrs);
906     heap_free(analysis->sz);
907     heap_free(analysis->sc);
908     heap_free(analysis);
909
910     if (invalid) return E_INVALIDARG;
911     return S_OK;
912 }
913
914 /***********************************************************************
915  *      ScriptCPtoX (USP10.@)
916  *
917  */
918 HRESULT WINAPI ScriptCPtoX(int iCP,
919                            BOOL fTrailing,
920                            int cChars,
921                            int cGlyphs,
922                            const WORD *pwLogClust,
923                            const SCRIPT_VISATTR *psva,
924                            const int *piAdvance,
925                            const SCRIPT_ANALYSIS *psa,
926                            int *piX)
927 {
928     int  item;
929     int  iPosX;
930     float  fMaxPosX = 0;
931     TRACE("(%d,%d,%d,%d,%p,%p,%p,%p,%p)\n",
932           iCP, fTrailing, cChars, cGlyphs, pwLogClust, psva, piAdvance,
933           psa, piX);
934     for (item=0; item < cGlyphs; item++)            /* total piAdvance           */
935         fMaxPosX += piAdvance[item];
936     iPosX = (fMaxPosX/cGlyphs)*(iCP+fTrailing);
937     if  (iPosX > fMaxPosX)
938         iPosX = fMaxPosX;
939     *piX = iPosX;                                    /* Return something in range */
940
941     TRACE("*piX=%d\n", *piX);
942     return S_OK;
943 }
944
945 /***********************************************************************
946  *      ScriptXtoCP (USP10.@)
947  *
948  */
949 HRESULT WINAPI ScriptXtoCP(int iX,
950                            int cChars,
951                            int cGlyphs,
952                            const WORD *pwLogClust,
953                            const SCRIPT_VISATTR *psva,
954                            const int *piAdvance,
955                            const SCRIPT_ANALYSIS *psa,
956                            int *piCP,
957                            int *piTrailing)
958 {
959     int item;
960     int iPosX;
961     float fMaxPosX = 1;
962     float fAvePosX;
963     TRACE("(%d,%d,%d,%p,%p,%p,%p,%p,%p)\n",
964           iX, cChars, cGlyphs, pwLogClust, psva, piAdvance,
965           psa, piCP, piTrailing);
966     if  (iX < 0)                                    /* iX is before start of run */
967     {
968         *piCP = -1;
969         *piTrailing = TRUE;
970         return S_OK;
971     }
972
973     for (item=0; item < cGlyphs; item++)            /* total piAdvance           */
974         fMaxPosX += piAdvance[item];
975
976     if  (iX >= fMaxPosX)                            /* iX too large              */
977     {
978         *piCP = cChars;
979         *piTrailing = FALSE;
980         return S_OK;
981     }
982
983     fAvePosX = fMaxPosX / cGlyphs;
984     iPosX = fAvePosX;
985     for (item = 1; item < cGlyphs  && iPosX < iX; item++)
986         iPosX += fAvePosX;
987     if  (iPosX - iX > fAvePosX/2)
988         *piTrailing = 0;
989     else
990         *piTrailing = 1;                            /* yep we are over halfway */
991
992     *piCP = item -1;                                /* Return character position */
993     TRACE("*piCP=%d iPposX=%d\n", *piCP, iPosX);
994     return S_OK;
995 }
996
997 /***********************************************************************
998  *      ScriptBreak (USP10.@)
999  *
1000  *  Retrieve line break information.
1001  *
1002  *  PARAMS
1003  *   chars [I] Array of characters.
1004  *   sa    [I] String analysis.
1005  *   la    [I] Array of logical attribute structures.
1006  *
1007  *  RETURNS
1008  *   Success: S_OK
1009  *   Failure: S_FALSE
1010  */
1011 HRESULT WINAPI ScriptBreak(const WCHAR *chars, int count, const SCRIPT_ANALYSIS *sa, SCRIPT_LOGATTR *la)
1012 {
1013     unsigned int i;
1014
1015     TRACE("(%s, %d, %p, %p)\n", debugstr_wn(chars, count), count, sa, la);
1016
1017     if (!la) return S_FALSE;
1018
1019     for (i = 0; i < count; i++)
1020     {
1021         memset(&la[i], 0, sizeof(SCRIPT_LOGATTR));
1022
1023         /* FIXME: set the other flags */
1024         la[i].fWhiteSpace = isspaceW(chars[i]);
1025         la[i].fCharStop = 1;
1026     }
1027     return S_OK;
1028 }
1029
1030 static const struct
1031 {
1032     WCHAR start;
1033     WCHAR end;
1034     DWORD flag;
1035 }
1036 complex_ranges[] =
1037 {
1038     { 0, 0x0b, SIC_COMPLEX },
1039     { 0x0c, 0x0c, SIC_NEUTRAL },
1040     { 0x0d, 0x1f, SIC_COMPLEX },
1041     { 0x20, 0x2f, SIC_NEUTRAL },
1042     { 0x30, 0x39, SIC_ASCIIDIGIT },
1043     { 0x3a, 0x40, SIC_NEUTRAL },
1044     { 0x5b, 0x60, SIC_NEUTRAL },
1045     { 0x7b, 0x7e, SIC_NEUTRAL },
1046     { 0x7f, 0x9f, SIC_COMPLEX },
1047     { 0xa0, 0xa5, SIC_NEUTRAL },
1048     { 0xa7, 0xa8, SIC_NEUTRAL },
1049     { 0xab, 0xab, SIC_NEUTRAL },
1050     { 0xad, 0xad, SIC_NEUTRAL },
1051     { 0xaf, 0xaf, SIC_NEUTRAL },
1052     { 0xb0, 0xb1, SIC_NEUTRAL },
1053     { 0xb4, 0xb4, SIC_NEUTRAL },
1054     { 0xb6, 0xb8, SIC_NEUTRAL },
1055     { 0xbb, 0xbf, SIC_NEUTRAL },
1056     { 0xd7, 0xd7, SIC_NEUTRAL },
1057     { 0xf7, 0xf7, SIC_NEUTRAL },
1058     { 0x2b9, 0x2ba, SIC_NEUTRAL },
1059     { 0x2c2, 0x2cf, SIC_NEUTRAL },
1060     { 0x2d2, 0x2df, SIC_NEUTRAL },
1061     { 0x2e5, 0x2e9, SIC_COMPLEX },
1062     { 0x2ea, 0x2ed, SIC_NEUTRAL },
1063     { 0x300, 0x362, SIC_COMPLEX },
1064     { 0x530, 0x60b, SIC_COMPLEX },
1065     { 0x60c, 0x60d, SIC_NEUTRAL },
1066     { 0x60e, 0x669, SIC_COMPLEX },
1067     { 0x66a, 0x66a, SIC_NEUTRAL },
1068     { 0x66b, 0x6e8, SIC_COMPLEX },
1069     { 0x6e9, 0x6e9, SIC_NEUTRAL },
1070     { 0x6ea, 0x7bf, SIC_COMPLEX },
1071     { 0x900, 0x1360, SIC_COMPLEX },
1072     { 0x137d, 0x137f, SIC_COMPLEX },
1073     { 0x1680, 0x1680, SIC_NEUTRAL },
1074     { 0x1780, 0x18af, SIC_COMPLEX },
1075     { 0x2000, 0x200a, SIC_NEUTRAL },
1076     { 0x200b, 0x200f, SIC_COMPLEX },
1077     { 0x2010, 0x2016, SIC_NEUTRAL },
1078     { 0x2018, 0x2022, SIC_NEUTRAL },
1079     { 0x2024, 0x2028, SIC_NEUTRAL },
1080     { 0x2029, 0x202e, SIC_COMPLEX },
1081     { 0x202f, 0x2037, SIC_NEUTRAL },
1082     { 0x2039, 0x203c, SIC_NEUTRAL },
1083     { 0x2044, 0x2046, SIC_NEUTRAL },
1084     { 0x206a, 0x206f, SIC_COMPLEX },
1085     { 0x207a, 0x207e, SIC_NEUTRAL },
1086     { 0x208a, 0x20aa, SIC_NEUTRAL },
1087     { 0x20ac, 0x20cf, SIC_NEUTRAL },
1088     { 0x20d0, 0x20ff, SIC_COMPLEX },
1089     { 0x2103, 0x2103, SIC_NEUTRAL },
1090     { 0x2105, 0x2105, SIC_NEUTRAL },
1091     { 0x2109, 0x2109, SIC_NEUTRAL },
1092     { 0x2116, 0x2116, SIC_NEUTRAL },
1093     { 0x2121, 0x2122, SIC_NEUTRAL },
1094     { 0x212e, 0x212e, SIC_NEUTRAL },
1095     { 0x2153, 0x2154, SIC_NEUTRAL },
1096     { 0x215b, 0x215e, SIC_NEUTRAL },
1097     { 0x2190, 0x2199, SIC_NEUTRAL },
1098     { 0x21b8, 0x21b9, SIC_NEUTRAL },
1099     { 0x21d2, 0x21d2, SIC_NEUTRAL },
1100     { 0x21d4, 0x21d4, SIC_NEUTRAL },
1101     { 0x21e7, 0x21e7, SIC_NEUTRAL },
1102     { 0x2200, 0x2200, SIC_NEUTRAL },
1103     { 0x2202, 0x2203, SIC_NEUTRAL },
1104     { 0x2207, 0x2208, SIC_NEUTRAL },
1105     { 0x220b, 0x220b, SIC_NEUTRAL },
1106     { 0x220f, 0x220f, SIC_NEUTRAL },
1107     { 0x2211, 0x2213, SIC_NEUTRAL },
1108     { 0x2215, 0x2215, SIC_NEUTRAL },
1109     { 0x221a, 0x221a, SIC_NEUTRAL },
1110     { 0x221d, 0x2220, SIC_NEUTRAL },
1111     { 0x2223, 0x2223, SIC_NEUTRAL },
1112     { 0x2225, 0x2225, SIC_NEUTRAL },
1113     { 0x2227, 0x222c, SIC_NEUTRAL },
1114     { 0x222e, 0x222e, SIC_NEUTRAL },
1115     { 0x2234, 0x2237, SIC_NEUTRAL },
1116     { 0x223c, 0x223d, SIC_NEUTRAL },
1117     { 0x2248, 0x2248, SIC_NEUTRAL },
1118     { 0x224c, 0x224c, SIC_NEUTRAL },
1119     { 0x2252, 0x2252, SIC_NEUTRAL },
1120     { 0x2260, 0x2261, SIC_NEUTRAL },
1121     { 0x2264, 0x2267, SIC_NEUTRAL },
1122     { 0x226a, 0x226b, SIC_NEUTRAL },
1123     { 0x226e, 0x226f, SIC_NEUTRAL },
1124     { 0x2282, 0x2283, SIC_NEUTRAL },
1125     { 0x2286, 0x2287, SIC_NEUTRAL },
1126     { 0x2295, 0x2295, SIC_NEUTRAL },
1127     { 0x2299, 0x2299, SIC_NEUTRAL },
1128     { 0x22a5, 0x22a5, SIC_NEUTRAL },
1129     { 0x22bf, 0x22bf, SIC_NEUTRAL },
1130     { 0x2312, 0x2312, SIC_NEUTRAL },
1131     { 0x24ea, 0x24ea, SIC_COMPLEX },
1132     { 0x2500, 0x254b, SIC_NEUTRAL },
1133     { 0x2550, 0x256d, SIC_NEUTRAL },
1134     { 0x256e, 0x2574, SIC_NEUTRAL },
1135     { 0x2581, 0x258f, SIC_NEUTRAL },
1136     { 0x2592, 0x2595, SIC_NEUTRAL },
1137     { 0x25a0, 0x25a1, SIC_NEUTRAL },
1138     { 0x25a3, 0x25a9, SIC_NEUTRAL },
1139     { 0x25b2, 0x25b3, SIC_NEUTRAL },
1140     { 0x25b6, 0x25b7, SIC_NEUTRAL },
1141     { 0x25bc, 0x25bd, SIC_NEUTRAL },
1142     { 0x25c0, 0x25c1, SIC_NEUTRAL },
1143     { 0x25c6, 0x25c8, SIC_NEUTRAL },
1144     { 0x25cb, 0x25cb, SIC_NEUTRAL },
1145     { 0x25ce, 0x25d1, SIC_NEUTRAL },
1146     { 0x25e2, 0x25e5, SIC_NEUTRAL },
1147     { 0x25ef, 0x25ef, SIC_NEUTRAL },
1148     { 0x2605, 0x2606, SIC_NEUTRAL },
1149     { 0x2609, 0x2609, SIC_NEUTRAL },
1150     { 0x260e, 0x260f, SIC_NEUTRAL },
1151     { 0x261c, 0x261c, SIC_NEUTRAL },
1152     { 0x261e, 0x261e, SIC_NEUTRAL },
1153     { 0x2640, 0x2640, SIC_NEUTRAL },
1154     { 0x2642, 0x2642, SIC_NEUTRAL },
1155     { 0x2660, 0x2661, SIC_NEUTRAL },
1156     { 0x2663, 0x2665, SIC_NEUTRAL },
1157     { 0x2667, 0x266a, SIC_NEUTRAL },
1158     { 0x266c, 0x266d, SIC_NEUTRAL },
1159     { 0x266f, 0x266f, SIC_NEUTRAL },
1160     { 0x273d, 0x273d, SIC_NEUTRAL },
1161     { 0x2e80, 0x312f, SIC_COMPLEX },
1162     { 0x3190, 0x31bf, SIC_COMPLEX },
1163     { 0x31f0, 0x31ff, SIC_COMPLEX },
1164     { 0x3220, 0x325f, SIC_COMPLEX },
1165     { 0x3280, 0xa4ff, SIC_COMPLEX },
1166     { 0xd800, 0xdfff, SIC_COMPLEX },
1167     { 0xe000, 0xf8ff, SIC_NEUTRAL },
1168     { 0xf900, 0xfaff, SIC_COMPLEX },
1169     { 0xfb13, 0xfb28, SIC_COMPLEX },
1170     { 0xfb29, 0xfb29, SIC_NEUTRAL },
1171     { 0xfb2a, 0xfb4f, SIC_COMPLEX },
1172     { 0xfd3e, 0xfd3f, SIC_NEUTRAL },
1173     { 0xfdd0, 0xfdef, SIC_COMPLEX },
1174     { 0xfe20, 0xfe6f, SIC_COMPLEX },
1175     { 0xfeff, 0xfeff, SIC_COMPLEX },
1176     { 0xff01, 0xff5e, SIC_COMPLEX },
1177     { 0xff61, 0xff9f, SIC_COMPLEX },
1178     { 0xffe0, 0xffe6, SIC_COMPLEX },
1179     { 0xffe8, 0xffee, SIC_COMPLEX },
1180     { 0xfff9, 0xfffb, SIC_COMPLEX },
1181     { 0xfffe, 0xfffe, SIC_COMPLEX }
1182 };
1183
1184 /***********************************************************************
1185  *      ScriptIsComplex (USP10.@)
1186  * 
1187  *  Determine if a string is complex.
1188  *
1189  *  PARAMS
1190  *   chars [I] Array of characters to test.
1191  *   len   [I] Length in characters.
1192  *   flag  [I] Flag.
1193  *
1194  *  RETURNS
1195  *   Success: S_OK
1196  *   Failure: S_FALSE
1197  *
1198  *  NOTES
1199  *   Behaviour matches that of WinXP.
1200  */
1201 HRESULT WINAPI ScriptIsComplex(const WCHAR *chars, int len, DWORD flag)
1202 {
1203     unsigned int i, j;
1204
1205     TRACE("(%s,%d,0x%x)\n", debugstr_wn(chars, len), len, flag);
1206
1207     for (i = 0; i < len; i++)
1208     {
1209         for (j = 0; j < sizeof(complex_ranges)/sizeof(complex_ranges[0]); j++)
1210         {
1211             if (chars[i] >= complex_ranges[j].start &&
1212                 chars[i] <= complex_ranges[j].end &&
1213                 (flag & complex_ranges[j].flag)) return S_OK;
1214         }
1215     }
1216     return S_FALSE;
1217 }
1218
1219 /***********************************************************************
1220  *      ScriptShape (USP10.@)
1221  *
1222  * Produce glyphs and visual attributes for a run.
1223  *
1224  * PARAMS
1225  *  hdc         [I]   Device context.
1226  *  psc         [I/O] Opaque pointer to a script cache.
1227  *  pwcChars    [I]   Array of characters specifying the run.
1228  *  cChars      [I]   Number of characters in pwcChars.
1229  *  cMaxGlyphs  [I]   Length of pwOutGlyphs.
1230  *  psa         [I/O] Script analysis.
1231  *  pwOutGlyphs [O]   Array of glyphs.
1232  *  pwLogClust  [O]   Array of logical cluster info.
1233  *  psva        [O]   Array of visual attributes.
1234  *  pcGlyphs    [O]   Number of glyphs returned.
1235  *
1236  * RETURNS
1237  *  Success: S_OK
1238  *  Failure: Non-zero HRESULT value.
1239  */
1240 HRESULT WINAPI ScriptShape(HDC hdc, SCRIPT_CACHE *psc, const WCHAR *pwcChars, 
1241                            int cChars, int cMaxGlyphs,
1242                            SCRIPT_ANALYSIS *psa, WORD *pwOutGlyphs, WORD *pwLogClust,
1243                            SCRIPT_VISATTR *psva, int *pcGlyphs)
1244 {
1245     int cnt;
1246     HRESULT hr;
1247     HFONT hfont;
1248
1249     TRACE("(%p, %p, %p, %d, %d, %p)\n",  hdc, psc, pwcChars, cChars, cMaxGlyphs, psa);
1250     if (psa) TRACE("psa values: %d, %d, %d, %d, %d, %d, %d\n", psa->eScript, psa->fRTL, psa->fLayoutRTL,
1251                    psa->fLinkBefore, psa->fLinkAfter, psa->fLogicalOrder, psa->fNoGlyphIndex);
1252
1253     if (!psva || !pcGlyphs) return E_INVALIDARG;
1254     if (cChars > cMaxGlyphs) return E_OUTOFMEMORY;
1255
1256     hr = get_script_cache(hdc, psc);
1257     if (hr != S_OK) return hr;
1258
1259     *pcGlyphs = cChars;
1260
1261     hfont = select_cached_font(psc);
1262
1263     if ((get_cache_pitch_family(psc) & TMPF_TRUETYPE) && !psa->fNoGlyphIndex)
1264     {
1265         GetGlyphIndicesW(get_cache_hdc(psc), pwcChars, cChars, pwOutGlyphs, 0);
1266     }
1267     else
1268     {
1269         TRACE("no glyph translation\n");
1270         for (cnt = 0; cnt < cChars; cnt++) pwOutGlyphs[cnt] = pwcChars[cnt];
1271     }
1272
1273     if (psva)
1274     {
1275         /* set up a valid SCRIPT_VISATTR and LogClust for each char in this run */
1276         for (cnt = 0; cnt < cChars; cnt++)
1277         {
1278             /* FIXME: set to better values */
1279             psva[cnt].uJustification = 2;
1280             psva[cnt].fClusterStart  = 1;
1281             psva[cnt].fDiacritic     = 0;
1282             psva[cnt].fZeroWidth     = 0;
1283
1284             if (pwLogClust) pwLogClust[cnt] = cnt;
1285         }
1286     }
1287     unselect_cached_font(psc, hfont);
1288     return S_OK;
1289 }
1290
1291 /***********************************************************************
1292  *      ScriptPlace (USP10.@)
1293  *
1294  * Produce advance widths for a run.
1295  *
1296  * PARAMS
1297  *  hdc       [I]   Device context.
1298  *  psc       [I/O] Opaque pointer to a script cache.
1299  *  pwGlyphs  [I]   Array of glyphs.
1300  *  cGlyphs   [I]   Number of glyphs in pwGlyphs.
1301  *  psva      [I]   Array of visual attributes.
1302  *  psa       [I/O] String analysis.
1303  *  piAdvance [O]   Array of advance widths.
1304  *  pGoffset  [O]   Glyph offsets.
1305  *  pABC      [O]   Combined ABC width.
1306  *
1307  * RETURNS
1308  *  Success: S_OK
1309  *  Failure: Non-zero HRESULT value.
1310  */
1311 HRESULT WINAPI ScriptPlace(HDC hdc, SCRIPT_CACHE *psc, const WORD *pwGlyphs, 
1312                            int cGlyphs, const SCRIPT_VISATTR *psva,
1313                            SCRIPT_ANALYSIS *psa, int *piAdvance, GOFFSET *pGoffset, ABC *pABC )
1314 {
1315     HRESULT hr;
1316     LPABC lpABC;
1317     HFONT hfont;
1318     unsigned int i;
1319
1320     TRACE("(%p, %p, %p, %s, %d, %p, %p, %p)\n",  hdc, psc, pwGlyphs,
1321           debugstr_wn(pwGlyphs, cGlyphs), cGlyphs, psva, psa, piAdvance);
1322
1323     if (!psva) return E_INVALIDARG;
1324
1325     hr = get_script_cache(hdc, psc);
1326     if (hr != S_OK) return hr;
1327
1328     hfont = select_cached_font(psc);
1329
1330     /*   Here we need to calculate the width of the run unit.  At this point the input string
1331      *   has been converted to glyphs and we still need to translate back to the original chars
1332      *   to get the correct ABC widths.   */
1333
1334     if (!(lpABC = heap_alloc_zero(sizeof(ABC) * cGlyphs))) return E_OUTOFMEMORY;
1335     if (pABC) memset(pABC, 0, sizeof(ABC));
1336
1337     if ((get_cache_pitch_family(psc) & TMPF_TRUETYPE) && !psa->fNoGlyphIndex)
1338     {
1339         GetCharABCWidthsI(get_cache_hdc(psc), 0, cGlyphs, (WORD *)pwGlyphs, lpABC);
1340     }
1341     else
1342     {
1343         INT width;
1344         for (i = 0; i < cGlyphs; i++)
1345         {
1346             GetCharWidth32W(get_cache_hdc(psc), pwGlyphs[i], pwGlyphs[i], &width);
1347             lpABC[i].abcB = width;
1348         }
1349     }
1350
1351     for (i = 0; i < cGlyphs; i++)
1352     {
1353         TRACE("     Glyph=%04x, abcA=%d, abcB=%d, abcC=%d index=%d\n",
1354               pwGlyphs[i], lpABC[i].abcA, lpABC[i].abcB, lpABC[i].abcC, i);
1355
1356         if (pABC)
1357         {
1358             pABC->abcA += lpABC[i].abcA;
1359             pABC->abcB += lpABC[i].abcB;
1360             pABC->abcC += lpABC[i].abcC;
1361         }
1362         /* FIXME: set to more reasonable values */
1363         if (pGoffset)  pGoffset[i].du = pGoffset[i].dv = 0;
1364         if (piAdvance) piAdvance[i] = lpABC[i].abcA + lpABC[i].abcB + lpABC[i].abcC;
1365     }
1366     if (pABC) TRACE("Total for run: abcA=%d, abcB=%d, abcC=%d\n", pABC->abcA, pABC->abcB, pABC->abcC);
1367
1368     heap_free(lpABC);
1369     unselect_cached_font(psc, hfont);
1370     return S_OK;
1371 }
1372
1373 /***********************************************************************
1374  *      ScriptGetCMap (USP10.@)
1375  *
1376  * Retrieve glyph indices.
1377  *
1378  * PARAMS
1379  *  hdc         [I]   Device context.
1380  *  psc         [I/O] Opaque pointer to a script cache.
1381  *  pwcInChars  [I]   Array of Unicode characters.
1382  *  cChars      [I]   Number of characters in pwcInChars.
1383  *  dwFlags     [I]   Flags.
1384  *  pwOutGlyphs [O]   Buffer to receive the array of glyph indices.
1385  *
1386  * RETURNS
1387  *  Success: S_OK
1388  *  Failure: Non-zero HRESULT value.
1389  */
1390 HRESULT WINAPI ScriptGetCMap(HDC hdc, SCRIPT_CACHE *psc, const WCHAR *pwcInChars,
1391                              int cChars, DWORD dwFlags, WORD *pwOutGlyphs)
1392 {
1393     HRESULT hr;
1394     HFONT hfont;
1395
1396     TRACE("(%p,%p,%s,%d,0x%x,%p)\n", hdc, psc, debugstr_wn(pwcInChars, cChars),
1397           cChars, dwFlags, pwOutGlyphs);
1398
1399     hr = get_script_cache(hdc, psc);
1400     if (hr != S_OK) return hr;
1401
1402     hfont = select_cached_font(psc);
1403     if (GetGlyphIndicesW(get_cache_hdc(psc), pwcInChars, cChars, pwOutGlyphs, 0) == GDI_ERROR)
1404         hr = S_FALSE;
1405
1406     unselect_cached_font(psc, hfont);
1407     return hr;
1408 }
1409
1410 /***********************************************************************
1411  *      ScriptTextOut (USP10.@)
1412  *
1413  */
1414 HRESULT WINAPI ScriptTextOut(const HDC hdc, SCRIPT_CACHE *psc, int x, int y, UINT fuOptions, 
1415                              const RECT *lprc, const SCRIPT_ANALYSIS *psa, const WCHAR *pwcReserved, 
1416                              int iReserved, const WORD *pwGlyphs, int cGlyphs, const int *piAdvance,
1417                              const int *piJustify, const GOFFSET *pGoffset)
1418 {
1419     HRESULT hr = S_OK;
1420
1421     TRACE("(%p, %p, %d, %d, %04x, %p, %p, %p, %d, %p, %d, %p, %p, %p)\n",
1422          hdc, psc, x, y, fuOptions, lprc, psa, pwcReserved, iReserved, pwGlyphs, cGlyphs,
1423          piAdvance, piJustify, pGoffset);
1424
1425     if (!hdc || !psc) return E_INVALIDARG;
1426     if (!piAdvance || !psa || !pwGlyphs) return E_INVALIDARG;
1427
1428     fuOptions &= ETO_CLIPPED + ETO_OPAQUE;
1429     if  (!psa->fNoGlyphIndex)                                     /* Have Glyphs?                      */
1430         fuOptions |= ETO_GLYPH_INDEX;                             /* Say don't do translation to glyph */
1431
1432     if (!ExtTextOutW(hdc, x, y, fuOptions, lprc, pwGlyphs, cGlyphs, NULL))
1433         hr = S_FALSE;
1434
1435     return hr;
1436 }
1437
1438 /***********************************************************************
1439  *      ScriptCacheGetHeight (USP10.@)
1440  *
1441  * Retrieve the height of the font in the cache.
1442  *
1443  * PARAMS
1444  *  hdc    [I]    Device context.
1445  *  psc    [I/O]  Opaque pointer to a script cache.
1446  *  height [O]    Receives font height.
1447  *
1448  * RETURNS
1449  *  Success: S_OK
1450  *  Failure: Non-zero HRESULT value.
1451  */
1452 HRESULT WINAPI ScriptCacheGetHeight(HDC hdc, SCRIPT_CACHE *psc, LONG *height)
1453 {
1454     HRESULT hr;
1455
1456     TRACE("(%p, %p, %p)\n", hdc, psc, height);
1457
1458     if (!height) return E_INVALIDARG;
1459
1460     hr = get_script_cache(hdc, psc);
1461     if (hr != S_OK) return hr;
1462
1463     *height = get_cache_height(psc);
1464     return S_OK;
1465 }
1466
1467 /***********************************************************************
1468  *      ScriptGetGlyphABCWidth (USP10.@)
1469  *
1470  * Retrieve the width of a glyph.
1471  *
1472  * PARAMS
1473  *  hdc    [I]    Device context.
1474  *  psc    [I/O]  Opaque pointer to a script cache.
1475  *  glyph  [I]    Glyph to retrieve the width for.
1476  *  abc    [O]    ABC widths of the glyph.
1477  *
1478  * RETURNS
1479  *  Success: S_OK
1480  *  Failure: Non-zero HRESULT value.
1481  */
1482 HRESULT WINAPI ScriptGetGlyphABCWidth(HDC hdc, SCRIPT_CACHE *psc, WORD glyph, ABC *abc)
1483 {
1484     HFONT hfont;
1485     HRESULT hr = S_OK;
1486
1487     TRACE("(%p, %p, 0x%04x, %p)\n", hdc, psc, glyph, abc);
1488
1489     hr = get_script_cache(hdc, psc);
1490     if (hr != S_OK) return hr;
1491
1492     hfont = select_cached_font(psc);
1493     if (!GetCharABCWidthsI(get_cache_hdc(psc), 0, 1, &glyph, abc)) hr = E_HANDLE;
1494
1495     unselect_cached_font(psc, hfont);
1496     return hr;
1497 }
1498
1499 /***********************************************************************
1500  *      ScriptLayout (USP10.@)
1501  *
1502  * Map embedding levels to visual and/or logical order.
1503  *
1504  * PARAMS
1505  *  runs     [I] Size of level array.
1506  *  level    [I] Array of embedding levels.
1507  *  vistolog [O] Map of embedding levels from visual to logical order.
1508  *  logtovis [O] Map of embedding levels from logical to visual order.
1509  *
1510  * RETURNS
1511  *  Success: S_OK
1512  *  Failure: Non-zero HRESULT value.
1513  *
1514  * BUGS
1515  *  This stub works correctly for any sequence of a single
1516  *  embedding level but not for sequences of different
1517  *  embedding levels, i.e. mixtures of RTL and LTR scripts.
1518  */
1519 HRESULT WINAPI ScriptLayout(int runs, const BYTE *level, int *vistolog, int *logtovis)
1520 {
1521     int i, j = runs - 1, k = 0;
1522
1523     TRACE("(%d, %p, %p, %p)\n", runs, level, vistolog, logtovis);
1524
1525     if (!level || (!vistolog && !logtovis))
1526         return E_INVALIDARG;
1527
1528     for (i = 0; i < runs; i++)
1529     {
1530         if (level[i] % 2)
1531         {
1532             if (vistolog) *vistolog++ = j;
1533             if (logtovis) *logtovis++ = j;
1534             j--;
1535         }
1536         else
1537         {
1538             if (vistolog) *vistolog++ = k;
1539             if (logtovis) *logtovis++ = k;
1540             k++;
1541         }
1542     }
1543     return S_OK;
1544 }
1545
1546 /***********************************************************************
1547  *      ScriptStringGetLogicalWidths (USP10.@)
1548  *
1549  * Returns logical widths from a string analysis.
1550  *
1551  * PARAMS
1552  *  ssa  [I] string analysis.
1553  *  piDx [O] logical widths returned.
1554  *
1555  * RETURNS
1556  *  Success: S_OK
1557  *  Failure: a non-zero HRESULT.
1558  */
1559 HRESULT WINAPI ScriptStringGetLogicalWidths(SCRIPT_STRING_ANALYSIS ssa, int *piDx)
1560 {
1561     int i, j, next = 0;
1562     StringAnalysis *analysis = ssa;
1563
1564     TRACE("%p, %p\n", ssa, piDx);
1565
1566     if (!analysis) return S_FALSE;
1567
1568     for (i = 0; i < analysis->numItems; i++)
1569     {
1570         for (j = 0; j < analysis->glyphs[i].numGlyphs; j++)
1571         {
1572             piDx[next] = analysis->glyphs[i].piAdvance[j];
1573             next++;
1574         }
1575     }
1576     return S_OK;
1577 }
1578
1579 /***********************************************************************
1580  *      ScriptStringValidate (USP10.@)
1581  *
1582  * Validate a string analysis.
1583  *
1584  * PARAMS
1585  *  ssa [I] string analysis.
1586  *
1587  * RETURNS
1588  *  Success: S_OK
1589  *  Failure: S_FALSE if invalid sequences are found
1590  *           or a non-zero HRESULT if it fails.
1591  */
1592 HRESULT WINAPI ScriptStringValidate(SCRIPT_STRING_ANALYSIS ssa)
1593 {
1594     StringAnalysis *analysis = ssa;
1595
1596     TRACE("(%p)\n", ssa);
1597
1598     if (!analysis) return E_INVALIDARG;
1599     return (analysis->invalid) ? S_FALSE : S_OK;
1600 }
1601
1602 /***********************************************************************
1603  *      ScriptString_pSize (USP10.@)
1604  *
1605  * Retrieve width and height of an analysed string.
1606  *
1607  * PARAMS
1608  *  ssa [I] string analysis.
1609  *
1610  * RETURNS
1611  *  Success: Pointer to a SIZE structure.
1612  *  Failure: NULL
1613  */
1614 const SIZE * WINAPI ScriptString_pSize(SCRIPT_STRING_ANALYSIS ssa)
1615 {
1616     unsigned int i, j;
1617     StringAnalysis *analysis = ssa;
1618
1619     TRACE("(%p)\n", ssa);
1620
1621     if (!analysis) return NULL;
1622
1623     if (!analysis->sz)
1624     {
1625         if (!(analysis->sz = heap_alloc(sizeof(SIZE)))) return NULL;
1626         analysis->sz->cy = analysis->sc->tm.tmHeight;
1627
1628         analysis->sz->cx = 0;
1629         for (i = 0; i < analysis->numItems; i++)
1630             for (j = 0; j < analysis->glyphs[i].numGlyphs; j++)
1631                 analysis->sz->cx += analysis->glyphs[i].piAdvance[j];
1632     }
1633     return analysis->sz;
1634 }
1635
1636 /***********************************************************************
1637  *      ScriptString_pLogAttr (USP10.@)
1638  *
1639  * Retrieve logical attributes of an analysed string.
1640  *
1641  * PARAMS
1642  *  ssa [I] string analysis.
1643  *
1644  * RETURNS
1645  *  Success: Pointer to an array of SCRIPT_LOGATTR structures.
1646  *  Failure: NULL
1647  */
1648 const SCRIPT_LOGATTR * WINAPI ScriptString_pLogAttr(SCRIPT_STRING_ANALYSIS ssa)
1649 {
1650     StringAnalysis *analysis = ssa;
1651
1652     TRACE("(%p)\n", ssa);
1653
1654     if (!analysis) return NULL;
1655     return analysis->logattrs;
1656 }
1657
1658 /***********************************************************************
1659  *      ScriptString_pcOutChars (USP10.@)
1660  *
1661  * Retrieve the length of a string after clipping.
1662  *
1663  * PARAMS
1664  *  ssa [I] String analysis.
1665  *
1666  * RETURNS
1667  *  Success: Pointer to the length.
1668  *  Failure: NULL
1669  */
1670 const int * WINAPI ScriptString_pcOutChars(SCRIPT_STRING_ANALYSIS ssa)
1671 {
1672     StringAnalysis *analysis = ssa;
1673
1674     TRACE("(%p)\n", ssa);
1675
1676     if (!analysis) return NULL;
1677     return &analysis->clip_len;
1678 }
1679
1680 /***********************************************************************
1681  *      ScriptStringGetOrder (USP10.@)
1682  *
1683  * Retrieve a glyph order map.
1684  *
1685  * PARAMS
1686  *  ssa   [I]   String analysis.
1687  *  order [I/O] Array of glyph positions.
1688  *
1689  * RETURNS
1690  *  Success: S_OK
1691  *  Failure: a non-zero HRESULT.
1692  */
1693 HRESULT WINAPI ScriptStringGetOrder(SCRIPT_STRING_ANALYSIS ssa, UINT *order)
1694 {
1695     unsigned int i, j, k;
1696     StringAnalysis *analysis = ssa;
1697
1698     TRACE("(%p)\n", ssa);
1699
1700     if (!analysis) return S_FALSE;
1701
1702     /* FIXME: handle RTL scripts */
1703     for (i = 0, k = 0; i < analysis->numItems; i++)
1704         for (j = 0; j < analysis->glyphs[i].numGlyphs; j++, k++)
1705             order[k] = k;
1706
1707     return S_OK;
1708 }
1709
1710 /***********************************************************************
1711  *      ScriptGetLogicalWidths (USP10.@)
1712  *
1713  * Convert advance widths to logical widths.
1714  *
1715  * PARAMS
1716  *  sa          [I] Script analysis.
1717  *  nbchars     [I] Number of characters.
1718  *  nbglyphs    [I] Number of glyphs.
1719  *  glyph_width [I] Array of glyph widths.
1720  *  log_clust   [I] Array of logical clusters.
1721  *  sva         [I] Visual attributes.
1722  *  widths      [O] Array of logical widths.
1723  *
1724  * RETURNS
1725  *  Success: S_OK
1726  *  Failure: a non-zero HRESULT.
1727  */
1728 HRESULT WINAPI ScriptGetLogicalWidths(const SCRIPT_ANALYSIS *sa, int nbchars, int nbglyphs,
1729                                       const int *glyph_width, const WORD *log_clust,
1730                                       const SCRIPT_VISATTR *sva, int *widths)
1731 {
1732     int i;
1733
1734     TRACE("(%p, %d, %d, %p, %p, %p, %p)\n",
1735           sa, nbchars, nbglyphs, glyph_width, log_clust, sva, widths);
1736
1737     /* FIXME */
1738     for (i = 0; i < nbchars; i++) widths[i] = glyph_width[i];
1739     return S_OK;
1740 }