d3d: Remove AddRef from IWineD3DDevice_GetDirect3D.
[wine] / dlls / usp10 / usp10.c
1 /*
2  * Implementation of Uniscribe Script Processor (usp10.dll)
3  *
4  * Copyright 2005 Steven Edwards for CodeWeavers
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Notes:
21  * Uniscribe allows for processing of complex scripts such as joining
22  * and filtering characters and bi-directional text with custom line breaks.
23  */
24
25 #include <stdarg.h>
26
27 #include "windef.h"
28 #include "winbase.h"
29 #include "wingdi.h"
30 #include "winuser.h"
31 #include "winnls.h"
32 #include "usp10.h"
33
34 #include "wine/debug.h"
35
36 /**
37  * some documentation here:
38  *   http://www.microsoft.com/typography/developers/uniscribe/uniscribe.htm
39  */
40
41 WINE_DEFAULT_DEBUG_CHANNEL(uniscribe);
42
43 #define MAX_SCRIPTS  8
44
45 /*  Set up a default for ScriptGetProperties    */
46 static const SCRIPT_PROPERTIES Default_Script_0 = {0, 0, 0, 0, 0, 0, 0, 0, 
47                                             0, 0, 0, 0, 0, 0, 0};
48 static const SCRIPT_PROPERTIES Default_Script_1 = {0, 0, 0, 0, 0, 0, 0, 0, 
49                                             0, 0, 0, 0, 0, 0, 0};
50 static const SCRIPT_PROPERTIES Default_Script_2 = {0, 0, 0, 0, 0, 0, 0, 0, 
51                                             0, 0, 0, 0, 0, 0, 0};
52 static const SCRIPT_PROPERTIES Default_Script_3 = {9, 0, 0, 0, 0, 0, 0, 0, 
53                                             0, 0, 0, 0, 0, 0, 0};
54 static const SCRIPT_PROPERTIES Default_Script_4 = {9, 1, 0, 0, 0, 0, 0, 0, 
55                                             0, 0, 0, 0, 0, 0, 0};
56 static const SCRIPT_PROPERTIES Default_Script_5 = {9, 0, 0, 0, 0, 0, 0, 0, 
57                                             0, 0, 0, 0, 1, 0, 0};
58 static const SCRIPT_PROPERTIES Default_Script_6 = {9, 1, 0, 0, 0, 0, 0, 0, 
59                                             0, 0, 0, 0, 1, 0, 0};
60 static const SCRIPT_PROPERTIES Default_Script_7 = {8, 0, 0, 0, 0, 161, 0, 0, 
61                                             0, 0, 0, 0, 0, 0, 0};
62 static const SCRIPT_PROPERTIES *Global_Script[MAX_SCRIPTS] =
63                                       {&Default_Script_0,
64                                        &Default_Script_1,
65                                        &Default_Script_2,
66                                        &Default_Script_3,
67                                        &Default_Script_4,
68                                        &Default_Script_5,
69                                        &Default_Script_6,
70                                        &Default_Script_7};
71
72 typedef struct scriptcache {
73        HDC hdc;
74 } Scriptcache;
75
76 /***********************************************************************
77  *      DllMain
78  *
79  */
80 BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpv)
81 {
82     switch(fdwReason) {
83         case DLL_PROCESS_ATTACH:
84             DisableThreadLibraryCalls(hInstDLL);
85             break;
86         case DLL_PROCESS_DETACH:
87             break;
88     }
89     return TRUE;
90 }
91
92 /***********************************************************************
93  *      ScriptFreeCache (USP10.@)
94  *
95  */
96 HRESULT WINAPI ScriptFreeCache(SCRIPT_CACHE *psc)
97 {
98     TRACE("%p\n", psc);
99
100     if (psc) {
101        HeapFree ( GetProcessHeap(), 0, *psc);
102        *psc = NULL;
103     }
104     return 0;
105 }
106
107 /***********************************************************************
108  *      ScriptGetProperties (USP10.@)
109  *
110  */
111 HRESULT WINAPI ScriptGetProperties(const SCRIPT_PROPERTIES ***ppSp, int *piNumScripts)
112 {
113     TRACE("%p,%p\n", ppSp, piNumScripts);
114
115     if (!ppSp && !piNumScripts) return E_INVALIDARG;
116
117     /* Set up a sensible default and intialise pointers  */
118     if (piNumScripts) *piNumScripts = MAX_SCRIPTS;
119     if (ppSp) *ppSp = Global_Script;
120     TRACE("ppSp:%p, *ppSp:%p, **ppSp:%p, %d\n",
121           ppSp, ppSp ? *ppSp : NULL, (ppSp && *ppSp) ? **ppSp : NULL,
122           piNumScripts ? *piNumScripts : -1);
123     return 0;
124 }
125
126 /***********************************************************************
127  *      ScriptGetFontProperties (USP10.@)
128  *
129  */
130 HRESULT WINAPI ScriptGetFontProperties(HDC hdc, SCRIPT_CACHE *psc, SCRIPT_FONTPROPERTIES *sfp)
131 {
132     HDC phdc;
133     Scriptcache *pScriptcache;
134     TEXTMETRICW ptm;
135
136     TRACE("%p,%p,%p\n", hdc, psc, sfp);
137
138     if (!psc || !sfp)
139         return E_INVALIDARG;
140     if  (!hdc && !*psc) {
141         TRACE("No Script_Cache (psc) and no hdc. Ask for one. Hdc=%p, psc=%p\n", hdc, *psc);
142         return E_PENDING;
143     }   else 
144         if  (hdc && !*psc) {
145             pScriptcache = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(Scriptcache) );
146             pScriptcache->hdc = (HDC) hdc;
147             phdc = hdc;
148             *psc = (Scriptcache *) pScriptcache;
149         }   else
150             if  (*psc) {
151                 pScriptcache = (Scriptcache *) *psc;
152                 phdc = pScriptcache->hdc;
153             }
154                 
155     if (sfp->cBytes != sizeof(SCRIPT_FONTPROPERTIES))
156         return E_INVALIDARG;
157
158     /* return something sensible? */
159     sfp->wgBlank       = 0;
160     if  (GetTextMetricsW(phdc, &ptm)) 
161         sfp->wgDefault = ptm.tmDefaultChar;
162     else
163         sfp->wgDefault = 0;
164     sfp->wgInvalid     = 0;
165     sfp->wgKashida     = 0xffff;
166     sfp->iKashidaWidth = 0;
167     return 0;
168 }
169
170 /***********************************************************************
171  *      ScriptRecordDigitSubstitution (USP10.@)
172  *
173  *  Record digit substitution settings for a given locale.
174  *
175  *  PARAMS
176  *   locale [I] Locale identifier.
177  *   sds    [I] Structure to record substitution settings.
178  *
179  *  RETURNS
180  *   Success: S_OK
181  *   Failure: E_POINTER if sds is NULL, E_INVALIDARG otherwise.
182  *
183  *  SEE ALSO
184  *   http://blogs.msdn.com/michkap/archive/2006/02/22/536877.aspx
185  */
186 HRESULT WINAPI ScriptRecordDigitSubstitution(LCID locale, SCRIPT_DIGITSUBSTITUTE *sds)
187 {
188     DWORD plgid, sub;
189
190     TRACE("0x%x, %p\n", locale, sds);
191
192     /* This implementation appears to be correct for all languages, but it's
193      * not clear if sds->DigitSubstitute is ever set to anything except 
194      * CONTEXT or NONE in reality */
195
196     if (!sds) return E_POINTER;
197     
198     locale = ConvertDefaultLocale(locale);
199
200     if (!IsValidLocale(locale, LCID_INSTALLED))
201         return E_INVALIDARG;
202     
203     plgid = PRIMARYLANGID(LANGIDFROMLCID(locale));
204     sds->TraditionalDigitLanguage = plgid;
205
206     if (plgid == LANG_ARABIC || plgid == LANG_FARSI)
207         sds->NationalDigitLanguage = plgid;
208     else
209         sds->NationalDigitLanguage = LANG_ENGLISH;
210
211     if (!GetLocaleInfoW(locale, LOCALE_IDIGITSUBSTITUTION | LOCALE_RETURN_NUMBER,
212                         (LPWSTR)&sub, sizeof(sub)/sizeof(WCHAR))) return E_INVALIDARG;
213
214     switch (sub)
215     {
216     case 0: 
217         if (plgid == LANG_ARABIC || plgid == LANG_FARSI)
218             sds->DigitSubstitute = SCRIPT_DIGITSUBSTITUTE_CONTEXT;
219         else
220             sds->DigitSubstitute = SCRIPT_DIGITSUBSTITUTE_NONE;
221         break;
222     case 1:
223         sds->DigitSubstitute = SCRIPT_DIGITSUBSTITUTE_NONE;
224         break;
225     case 2:
226         sds->DigitSubstitute = SCRIPT_DIGITSUBSTITUTE_NATIONAL;
227         break;
228     default:
229         sds->DigitSubstitute = SCRIPT_DIGITSUBSTITUTE_TRADITIONAL;
230         break;
231     }
232
233     sds->dwReserved = 0;
234     return S_OK;
235 }
236
237 /***********************************************************************
238  *      ScriptApplyDigitSubstitution (USP10.@)
239  *
240  *  Apply digit substitution settings.
241  *
242  *  PARAMS
243  *   sds [I] Structure with recorded substitution settings.
244  *   sc  [I] Script control structure.
245  *   ss  [I] Script state structure.
246  *
247  *  RETURNS
248  *   Success: S_OK
249  *   Failure: E_INVALIDARG if sds is invalid. Otherwise an HRESULT.
250  */
251 HRESULT WINAPI ScriptApplyDigitSubstitution(const SCRIPT_DIGITSUBSTITUTE *sds, 
252                                             SCRIPT_CONTROL *sc, SCRIPT_STATE *ss)
253 {
254     SCRIPT_DIGITSUBSTITUTE psds;
255
256     TRACE("%p, %p, %p\n", sds, sc, ss);
257
258     if (!sc || !ss) return E_POINTER;
259     if (!sds)
260     {
261         sds = &psds;
262         if (ScriptRecordDigitSubstitution(LOCALE_USER_DEFAULT, &psds) != S_OK)
263             return E_INVALIDARG;
264     }
265
266     sc->uDefaultLanguage = LANG_ENGLISH;
267     sc->fContextDigits = 0;
268     ss->fDigitSubstitute = 0;
269
270     switch (sds->DigitSubstitute) {
271         case SCRIPT_DIGITSUBSTITUTE_CONTEXT:
272         case SCRIPT_DIGITSUBSTITUTE_NATIONAL:
273         case SCRIPT_DIGITSUBSTITUTE_NONE:
274         case SCRIPT_DIGITSUBSTITUTE_TRADITIONAL:
275             return S_OK;
276         default:
277             return E_INVALIDARG;
278     }
279 }
280
281 /***********************************************************************
282  *      ScriptItemize (USP10.@)
283  *
284  */
285 HRESULT WINAPI ScriptItemize(const WCHAR *pwcInChars, int cInChars, int cMaxItems, 
286                              const SCRIPT_CONTROL *psControl, const SCRIPT_STATE *psState, 
287                              SCRIPT_ITEM *pItems, int *pcItems)
288 {
289
290 #define Numeric_start 0x0030
291 #define Numeric_stop  0x0039
292 #define Numeric_space 0x0020
293 #define Arabic_start  0x0600
294 #define Arabic_stop   0x06ff
295 #define Latin_start   0x0001
296 #define Latin_stop    0x024f
297 #define Script_Arabic  6
298 #define Script_Latin   1
299 #define Script_Numeric 5
300
301     int   cnt = 0, index = 0;
302     int   New_Script = SCRIPT_UNDEFINED;
303
304     TRACE("%s,%d,%d,%p,%p,%p,%p\n", debugstr_wn(pwcInChars, cInChars), cInChars, cMaxItems, 
305           psControl, psState, pItems, pcItems);
306
307     if (!pwcInChars || !cInChars || !pItems || cMaxItems < 2)
308         return E_INVALIDARG;
309
310     if  (pwcInChars[cnt] >= Numeric_start && pwcInChars[cnt] <= Numeric_stop)
311         pItems[index].a.eScript = Script_Numeric;
312     else
313     if  (pwcInChars[cnt] >= Arabic_start && pwcInChars[cnt] <= Arabic_stop)
314         pItems[index].a.eScript = Script_Arabic;
315     else
316     if  (pwcInChars[cnt] >= Latin_start && pwcInChars[cnt] <= Latin_stop)
317         pItems[index].a.eScript = Script_Latin;
318     else
319         pItems[index].a.eScript = SCRIPT_UNDEFINED;
320     pItems[index].iCharPos = 0;
321     /*  Set the SCRIPT_ANALYSIS                             */
322     pItems[index].a.fRTL = 0;
323     pItems[index].a.fLayoutRTL = 0;
324     pItems[index].a.fLinkBefore = 0;
325     pItems[index].a.fLinkAfter = 0;
326     pItems[index].a.fLogicalOrder = 0;
327     pItems[index].a.fNoGlyphIndex = 0;
328     /*  set the SCRIPT_STATE                                */
329     if  (pItems[index].a.eScript  == Script_Arabic)
330         pItems[index].a.s.uBidiLevel = 1;
331     else
332         pItems[index].a.s.uBidiLevel = 0;
333     pItems[index].a.s.fOverrideDirection = 0;
334     pItems[index].a.s.fInhibitSymSwap = FALSE;
335     pItems[index].a.s.fCharShape = 0;
336     pItems[index].a.s.fDigitSubstitute = 0;
337     pItems[index].a.s.fInhibitLigate = 0;
338     pItems[index].a.s.fDisplayZWG = 0;
339     pItems[index].a.s.fArabicNumContext = 0;
340     pItems[index].a.s.fGcpClusters = 0;
341     pItems[index].a.s.fReserved = 0;
342     pItems[index].a.s.fEngineReserved = 0;
343     TRACE("New_Script=%d, eScript=%d index=%d cnt=%d iCharPos=%d\n",
344           New_Script, pItems[index].a.eScript, index, cnt,
345           pItems[index].iCharPos = cnt);
346
347     for (cnt=0; cnt < cInChars; cnt++)
348     {
349         if  ((pwcInChars[cnt] >= Numeric_start && pwcInChars[cnt] <= Numeric_stop)
350              || (New_Script == Script_Numeric && pwcInChars[cnt] == Numeric_space))
351             New_Script = Script_Numeric;
352         else
353         if  ((pwcInChars[cnt] >= Arabic_start && pwcInChars[cnt] <= Arabic_stop)
354              || (New_Script == Script_Arabic && pwcInChars[cnt] == Numeric_space))
355             New_Script = Script_Arabic;
356         else
357         if  ((WCHAR) pwcInChars[cnt] >= Latin_start && (WCHAR) pwcInChars[cnt] <= Latin_stop)
358             New_Script = Script_Latin;
359         else
360             New_Script = SCRIPT_UNDEFINED;
361         
362         if  (New_Script != pItems[index].a.eScript)
363         {
364             TRACE("New_Script=%d, eScript=%d ", New_Script, pItems[index].a.eScript);
365             index++;
366             if  (index+1 > cMaxItems)
367                 return E_OUTOFMEMORY;
368             pItems[index].iCharPos = cnt;
369             if  (New_Script == Script_Arabic)
370                 pItems[index].a.s.uBidiLevel = 1;
371             /*  Set SCRIPT_ITEM                                     */
372             pItems[index].iCharPos = cnt;
373             /*  Set the SCRIPT_ANALYSIS                             */
374             pItems[index].a.eScript = New_Script;
375             pItems[index].a.fRTL = 0;
376             pItems[index].a.fLayoutRTL = 0;
377             pItems[index].a.fLinkBefore = 0;
378             pItems[index].a.fLinkAfter = 0;
379             pItems[index].a.fLogicalOrder = 0;
380             pItems[index].a.fNoGlyphIndex = 0;
381             /*  set the SCRIPT_STATE                                */
382             if  (New_Script == Script_Arabic)
383                 pItems[index].a.s.uBidiLevel = 1;
384             else
385                 pItems[index].a.s.uBidiLevel = 0;
386             pItems[index].a.s.fOverrideDirection = 0;
387             pItems[index].a.s.fInhibitSymSwap = FALSE;
388             pItems[index].a.s.fCharShape = 0;
389             pItems[index].a.s.fDigitSubstitute = 0;
390             pItems[index].a.s.fInhibitLigate = 0;
391             pItems[index].a.s.fDisplayZWG = 0;
392             pItems[index].a.s.fArabicNumContext = 0;
393             pItems[index].a.s.fGcpClusters = 0;
394             pItems[index].a.s.fReserved = 0;
395             pItems[index].a.s.fEngineReserved = 0;
396             TRACE("index=%d cnt=%d iCharPos=%d\n", index, cnt, pItems[index].iCharPos = cnt);
397         }
398     }
399
400     /* While not strictly necessary according to the spec, make sure the n+1
401      * item is set up to prevent random behaviour if the caller erroneously
402      * checks the n+1 structure                                              */
403     pItems[index+1].a.eScript = 0;
404     pItems[index+1].a.fRTL = 0;
405     pItems[index+1].a.fLayoutRTL = 0;
406     pItems[index+1].a.fLinkBefore = 0;
407     pItems[index+1].a.fLinkAfter = 0;
408     pItems[index+1].a.fLogicalOrder = 0;
409     pItems[index+1].a.fNoGlyphIndex = 0;
410     /*  set the SCRIPT_STATE                                */
411     pItems[index+1].a.s.uBidiLevel = 0;
412     pItems[index+1].a.s.fOverrideDirection = 0;
413     pItems[index+1].a.s.fInhibitSymSwap = FALSE;
414     pItems[index+1].a.s.fCharShape = 0;
415     pItems[index+1].a.s.fDigitSubstitute = 0;
416     pItems[index+1].a.s.fInhibitLigate = 0;
417     pItems[index+1].a.s.fDisplayZWG = 0;
418     pItems[index+1].a.s.fArabicNumContext = 0;
419     pItems[index+1].a.s.fGcpClusters = 0;
420     pItems[index+1].a.s.fReserved = 0;
421     pItems[index+1].a.s.fEngineReserved = 0;
422     TRACE("index=%d cnt=%d iCharPos=%d\n", index+1, cnt, pItems[index+1].iCharPos = cnt);
423
424     /*  Set one SCRIPT_STATE item being returned  */
425     *pcItems = index + 1;
426
427     /*  Set SCRIPT_ITEM                                     */
428     pItems[index+1].iCharPos = cnt;       /* the last + 1 item
429                                              contains the ptr to the lastchar */
430     return S_OK;
431 }
432
433 /***********************************************************************
434  *      ScriptStringAnalyse (USP10.@)
435  *
436  */
437 HRESULT WINAPI ScriptStringAnalyse(HDC hdc, 
438                                    const void *pString, 
439                                    int cString, 
440                                    int cGlyphs,
441                                    int iCharset,
442                                    DWORD dwFlags,
443                                    int iReqWidth,
444                                    SCRIPT_CONTROL *psControl,
445                                    SCRIPT_STATE *psState,
446                                    const int *piDx,
447                                    SCRIPT_TABDEF *pTabdef,
448                                    const BYTE *pbInClass,
449                                    SCRIPT_STRING_ANALYSIS *pssa)
450 {
451   FIXME("(%p,%p,%d,%d,%d,0x%x,%d,%p,%p,%p,%p,%p,%p): stub\n",
452         hdc, pString, cString, cGlyphs, iCharset, dwFlags,
453         iReqWidth, psControl, psState, piDx, pTabdef, pbInClass, pssa);
454   if (1 > cString || NULL == pString) {
455     return E_INVALIDARG;
456   }
457   if ((dwFlags & SSA_GLYPHS) && NULL == hdc) {
458     return E_PENDING;
459   }
460
461   return E_NOTIMPL;
462 }
463
464 /***********************************************************************
465  *      ScriptStringOut (USP10.@)
466  *
467  */
468 HRESULT WINAPI ScriptStringOut(SCRIPT_STRING_ANALYSIS ssa, 
469                                int iX, 
470                                int iY, 
471                                UINT uOptions, 
472                                const RECT *prc, 
473                                int iMinSel, 
474                                int iMaxSel, 
475                                BOOL fDisabled)
476 {
477     FIXME("(%p,%d,%d,0x%1x,%p,%d,%d,%d): stub\n",
478          ssa, iX, iY, uOptions, prc, iMinSel, iMaxSel, fDisabled);
479     if  (!ssa) {
480         return E_INVALIDARG;
481     }
482
483     return E_NOTIMPL;
484 }
485
486 /***********************************************************************
487  *      ScriptStringCPtoX (USP10.@)
488  *
489  */
490 HRESULT WINAPI ScriptStringCPtoX(SCRIPT_STRING_ANALYSIS ssa, int icp, BOOL fTrailing, int* pX)
491 {
492     FIXME("(%p), %d, %d, (%p): stub\n", ssa, icp, fTrailing, pX);
493     *pX = 0;                             /* Set a reasonable value */
494     return S_OK;
495 }
496
497 /***********************************************************************
498  *      ScriptStringXtoCP (USP10.@)
499  *
500  */
501 HRESULT WINAPI ScriptStringXtoCP(SCRIPT_STRING_ANALYSIS ssa, int iX, int* piCh, int* piTrailing) 
502 {
503     FIXME("(%p), %d, (%p), (%p): stub\n", ssa, iX, piCh, piTrailing);
504     *piCh = 0;                          /* Set a reasonable value */
505     *piTrailing = 0;
506     return S_OK;
507 }
508
509 /***********************************************************************
510  *      ScriptStringFree (USP10.@)
511  *
512  */
513 HRESULT WINAPI ScriptStringFree(SCRIPT_STRING_ANALYSIS *pssa) {
514     FIXME("(%p): stub\n",pssa);
515     return S_OK;
516 }
517
518 /***********************************************************************
519  *      ScriptCPtoX (USP10.@)
520  *
521  */
522 HRESULT WINAPI ScriptCPtoX(int iCP,
523                            BOOL fTrailing,
524                            int cChars,
525                            int cGlyphs,
526                            const WORD *pwLogClust,
527                            const SCRIPT_VISATTR *psva,
528                            const int *piAdvance,
529                            const SCRIPT_ANALYSIS *psa,
530                            int *piX)
531 {
532     int  item;
533     int  iPosX;
534     float  fMaxPosX = 0;
535     TRACE("(%d,%d,%d,%d,%p,%p,%p,%p,%p)\n",
536           iCP, fTrailing, cChars, cGlyphs, pwLogClust, psva, piAdvance,
537           psa, piX);
538     for (item=0; item < cGlyphs; item++)            /* total piAdvance           */
539         fMaxPosX += piAdvance[item];
540     iPosX = (fMaxPosX/cGlyphs)*(iCP+fTrailing);
541     if  (iPosX > fMaxPosX)
542         iPosX = fMaxPosX;
543     *piX = iPosX;                                    /* Return something in range */
544
545     TRACE("*piX=%d\n", *piX);
546     return S_OK;
547 }
548
549 /***********************************************************************
550  *      ScriptXtoCP (USP10.@)
551  *
552  */
553 HRESULT WINAPI ScriptXtoCP(int iX,
554                            int cChars,
555                            int cGlyphs,
556                            const WORD *pwLogClust,
557                            const SCRIPT_VISATTR *psva,
558                            const int *piAdvance,
559                            const SCRIPT_ANALYSIS *psa,
560                            int *piCP,
561                            int *piTrailing)
562 {
563     int item;
564     int iPosX = 1;
565     float fMaxPosX = 1;
566     float fAvePosX;
567     TRACE("(%d,%d,%d,%p,%p,%p,%p,%p,%p)\n",
568           iX, cChars, cGlyphs, pwLogClust, psva, piAdvance,
569           psa, piCP, piTrailing);
570     if  (iX < 0)                                    /* iX is before start of run */
571     {
572         *piCP = -1;
573         *piTrailing = TRUE;
574         return S_OK;
575     }
576
577     for (item=0; item < cGlyphs; item++)            /* total piAdvance           */
578         fMaxPosX += piAdvance[item];
579
580     if  (iX >= fMaxPosX)                            /* iX too large              */
581     {
582         *piCP = cChars;
583         *piTrailing = FALSE;
584         return S_OK;
585     }        
586
587     fAvePosX = fMaxPosX / cGlyphs;
588     for (item = 0; item < cGlyphs  && iPosX < iX; item++)
589         iPosX = fAvePosX * (item +1);
590     if  (iPosX - iX > fAvePosX/2)
591         *piTrailing = 0;
592     else
593         *piTrailing = 1;                            /* yep we are over half way  */
594     
595     *piCP = item -1;                                /* Return character position */
596     TRACE("*piCP=%d iPposX=%d\n", *piCP, iPosX);
597     return S_OK;
598 }
599
600 /***********************************************************************
601  *      ScriptBreak (USP10.@)
602  *
603  */
604 HRESULT WINAPI ScriptBreak(const WCHAR *pwcChars, int cChars,  const SCRIPT_ANALYSIS *psa,
605                     SCRIPT_LOGATTR *psla)
606 {
607     FIXME("(%p,%d,%p,%p): stub\n",
608           pwcChars, cChars, psa, psla);
609
610     return S_OK;
611 }
612
613 static const struct
614 {
615     WCHAR start;
616     WCHAR end;
617     DWORD flag;
618 }
619 complex_ranges[] =
620 {
621     { 0, 0x0b, SIC_COMPLEX },
622     { 0x0c, 0x0c, SIC_NEUTRAL },
623     { 0x0d, 0x1f, SIC_COMPLEX },
624     { 0x20, 0x2f, SIC_NEUTRAL },
625     { 0x30, 0x39, SIC_ASCIIDIGIT },
626     { 0x3a, 0x40, SIC_NEUTRAL },
627     { 0x5b, 0x60, SIC_NEUTRAL },
628     { 0x7b, 0x7e, SIC_NEUTRAL },
629     { 0x7f, 0x9f, SIC_COMPLEX },
630     { 0xa0, 0xa5, SIC_NEUTRAL },
631     { 0xa7, 0xa8, SIC_NEUTRAL },
632     { 0xab, 0xab, SIC_NEUTRAL },
633     { 0xad, 0xad, SIC_NEUTRAL },
634     { 0xaf, 0xaf, SIC_NEUTRAL },
635     { 0xb0, 0xb1, SIC_NEUTRAL },
636     { 0xb4, 0xb4, SIC_NEUTRAL },
637     { 0xb6, 0xb8, SIC_NEUTRAL },
638     { 0xbb, 0xbf, SIC_NEUTRAL },
639     { 0xd7, 0xd7, SIC_NEUTRAL },
640     { 0xf7, 0xf7, SIC_NEUTRAL },
641     { 0x2b9, 0x2ba, SIC_NEUTRAL },
642     { 0x2c2, 0x2cf, SIC_NEUTRAL },
643     { 0x2d2, 0x2df, SIC_NEUTRAL },
644     { 0x2e5, 0x2e9, SIC_COMPLEX },
645     { 0x2ea, 0x2ed, SIC_NEUTRAL },
646     { 0x300, 0x362, SIC_COMPLEX },
647     { 0x530, 0x60b, SIC_COMPLEX },
648     { 0x60c, 0x60d, SIC_NEUTRAL },
649     { 0x60e, 0x669, SIC_COMPLEX },
650     { 0x66a, 0x66a, SIC_NEUTRAL },
651     { 0x66b, 0x6e8, SIC_COMPLEX },
652     { 0x6e9, 0x6e9, SIC_NEUTRAL },
653     { 0x6ea, 0x7bf, SIC_COMPLEX },
654     { 0x900, 0x1360, SIC_COMPLEX },
655     { 0x137d, 0x137f, SIC_COMPLEX },
656     { 0x1680, 0x1680, SIC_NEUTRAL },
657     { 0x1780, 0x18af, SIC_COMPLEX },
658     { 0x2000, 0x200a, SIC_NEUTRAL },
659     { 0x200b, 0x200f, SIC_COMPLEX },
660     { 0x2010, 0x2016, SIC_NEUTRAL },
661     { 0x2018, 0x2022, SIC_NEUTRAL },
662     { 0x2024, 0x2028, SIC_NEUTRAL },
663     { 0x2029, 0x202e, SIC_COMPLEX },
664     { 0x202f, 0x2037, SIC_NEUTRAL },
665     { 0x2039, 0x203c, SIC_NEUTRAL },
666     { 0x2044, 0x2046, SIC_NEUTRAL },
667     { 0x206a, 0x206f, SIC_COMPLEX },
668     { 0x207a, 0x207e, SIC_NEUTRAL },
669     { 0x208a, 0x20aa, SIC_NEUTRAL },
670     { 0x20ac, 0x20cf, SIC_NEUTRAL },
671     { 0x20d0, 0x20ff, SIC_COMPLEX },
672     { 0x2103, 0x2103, SIC_NEUTRAL },
673     { 0x2105, 0x2105, SIC_NEUTRAL },
674     { 0x2109, 0x2109, SIC_NEUTRAL },
675     { 0x2116, 0x2116, SIC_NEUTRAL },
676     { 0x2121, 0x2122, SIC_NEUTRAL },
677     { 0x212e, 0x212e, SIC_NEUTRAL },
678     { 0x2153, 0x2154, SIC_NEUTRAL },
679     { 0x215b, 0x215e, SIC_NEUTRAL },
680     { 0x2190, 0x2199, SIC_NEUTRAL },
681     { 0x21b8, 0x21b9, SIC_NEUTRAL },
682     { 0x21d2, 0x21d2, SIC_NEUTRAL },
683     { 0x21d4, 0x21d4, SIC_NEUTRAL },
684     { 0x21e7, 0x21e7, SIC_NEUTRAL },
685     { 0x2200, 0x2200, SIC_NEUTRAL },
686     { 0x2202, 0x2203, SIC_NEUTRAL },
687     { 0x2207, 0x2208, SIC_NEUTRAL },
688     { 0x220b, 0x220b, SIC_NEUTRAL },
689     { 0x220f, 0x220f, SIC_NEUTRAL },
690     { 0x2211, 0x2213, SIC_NEUTRAL },
691     { 0x2215, 0x2215, SIC_NEUTRAL },
692     { 0x221a, 0x221a, SIC_NEUTRAL },
693     { 0x221d, 0x2220, SIC_NEUTRAL },
694     { 0x2223, 0x2223, SIC_NEUTRAL },
695     { 0x2225, 0x2225, SIC_NEUTRAL },
696     { 0x2227, 0x222c, SIC_NEUTRAL },
697     { 0x222e, 0x222e, SIC_NEUTRAL },
698     { 0x2234, 0x2237, SIC_NEUTRAL },
699     { 0x223c, 0x223d, SIC_NEUTRAL },
700     { 0x2248, 0x2248, SIC_NEUTRAL },
701     { 0x224c, 0x224c, SIC_NEUTRAL },
702     { 0x2252, 0x2252, SIC_NEUTRAL },
703     { 0x2260, 0x2261, SIC_NEUTRAL },
704     { 0x2264, 0x2267, SIC_NEUTRAL },
705     { 0x226a, 0x226b, SIC_NEUTRAL },
706     { 0x226e, 0x226f, SIC_NEUTRAL },
707     { 0x2282, 0x2283, SIC_NEUTRAL },
708     { 0x2286, 0x2287, SIC_NEUTRAL },
709     { 0x2295, 0x2295, SIC_NEUTRAL },
710     { 0x2299, 0x2299, SIC_NEUTRAL },
711     { 0x22a5, 0x22a5, SIC_NEUTRAL },
712     { 0x22bf, 0x22bf, SIC_NEUTRAL },
713     { 0x2312, 0x2312, SIC_NEUTRAL },
714     { 0x24ea, 0x24ea, SIC_COMPLEX },
715     { 0x2500, 0x254b, SIC_NEUTRAL },
716     { 0x2550, 0x256d, SIC_NEUTRAL },
717     { 0x256e, 0x2574, SIC_NEUTRAL },
718     { 0x2581, 0x258f, SIC_NEUTRAL },
719     { 0x2592, 0x2595, SIC_NEUTRAL },
720     { 0x25a0, 0x25a1, SIC_NEUTRAL },
721     { 0x25a3, 0x25a9, SIC_NEUTRAL },
722     { 0x25b2, 0x25b3, SIC_NEUTRAL },
723     { 0x25b6, 0x25b7, SIC_NEUTRAL },
724     { 0x25bc, 0x25bd, SIC_NEUTRAL },
725     { 0x25c0, 0x25c1, SIC_NEUTRAL },
726     { 0x25c6, 0x25c8, SIC_NEUTRAL },
727     { 0x25cb, 0x25cb, SIC_NEUTRAL },
728     { 0x25ce, 0x25d1, SIC_NEUTRAL },
729     { 0x25e2, 0x25e5, SIC_NEUTRAL },
730     { 0x25ef, 0x25ef, SIC_NEUTRAL },
731     { 0x2605, 0x2606, SIC_NEUTRAL },
732     { 0x2609, 0x2609, SIC_NEUTRAL },
733     { 0x260e, 0x260f, SIC_NEUTRAL },
734     { 0x261c, 0x261c, SIC_NEUTRAL },
735     { 0x261e, 0x261e, SIC_NEUTRAL },
736     { 0x2640, 0x2640, SIC_NEUTRAL },
737     { 0x2642, 0x2642, SIC_NEUTRAL },
738     { 0x2660, 0x2661, SIC_NEUTRAL },
739     { 0x2663, 0x2665, SIC_NEUTRAL },
740     { 0x2667, 0x266a, SIC_NEUTRAL },
741     { 0x266c, 0x266d, SIC_NEUTRAL },
742     { 0x266f, 0x266f, SIC_NEUTRAL },
743     { 0x273d, 0x273d, SIC_NEUTRAL },
744     { 0x2e80, 0x312f, SIC_COMPLEX },
745     { 0x3190, 0x31bf, SIC_COMPLEX },
746     { 0x31f0, 0x31ff, SIC_COMPLEX },
747     { 0x3220, 0x325f, SIC_COMPLEX },
748     { 0x3280, 0xa4ff, SIC_COMPLEX },
749     { 0xd800, 0xdfff, SIC_COMPLEX },
750     { 0xe000, 0xf8ff, SIC_NEUTRAL },
751     { 0xf900, 0xfaff, SIC_COMPLEX },
752     { 0xfb13, 0xfb28, SIC_COMPLEX },
753     { 0xfb29, 0xfb29, SIC_NEUTRAL },
754     { 0xfb2a, 0xfb4f, SIC_COMPLEX },
755     { 0xfd3e, 0xfd3f, SIC_NEUTRAL },
756     { 0xfdd0, 0xfdef, SIC_COMPLEX },
757     { 0xfe20, 0xfe6f, SIC_COMPLEX },
758     { 0xfeff, 0xfeff, SIC_COMPLEX },
759     { 0xff01, 0xff5e, SIC_COMPLEX },
760     { 0xff61, 0xff9f, SIC_COMPLEX },
761     { 0xffe0, 0xffe6, SIC_COMPLEX },
762     { 0xffe8, 0xffee, SIC_COMPLEX },
763     { 0xfff9, 0xfffb, SIC_COMPLEX },
764     { 0xfffe, 0xfffe, SIC_COMPLEX }
765 };
766
767 /***********************************************************************
768  *      ScriptIsComplex (USP10.@)
769  * 
770  *  Determine if a string is complex.
771  *
772  *  PARAMS
773  *   chars [I] Array of characters to test.
774  *   len   [I] Length in characters.
775  *   flag  [I] Flag.
776  *
777  *  RETURNS
778  *   Success: S_OK
779  *   Failure: S_FALSE
780  *
781  *  NOTES
782  *   Behaviour matches that of WinXP.
783  */
784 HRESULT WINAPI ScriptIsComplex(const WCHAR *chars, int len, DWORD flag)
785 {
786     unsigned int i, j;
787
788     TRACE("(%s,%d,0x%x)\n", debugstr_wn(chars, len), len, flag);
789
790     for (i = 0; i < len; i++)
791     {
792         for (j = 0; j < sizeof(complex_ranges)/sizeof(complex_ranges[0]); j++)
793         {
794             if (chars[i] >= complex_ranges[j].start &&
795                 chars[i] <= complex_ranges[j].end &&
796                 (flag & complex_ranges[j].flag)) return S_OK;
797         }
798     }
799     return S_FALSE;
800 }
801
802 /***********************************************************************
803  *      ScriptShape (USP10.@)
804  *
805  */
806 HRESULT WINAPI ScriptShape(HDC hdc, SCRIPT_CACHE *psc, const WCHAR *pwcChars, 
807                            int cChars, int cMaxGlyphs,
808                            SCRIPT_ANALYSIS *psa, WORD *pwOutGlyphs, WORD *pwLogClust, 
809                            SCRIPT_VISATTR *psva, int *pcGlyphs)
810 {
811     /*  Note SCRIPT_CACHE (*psc) appears to be a good place to save info that needs to be 
812      *  passed between functions.                                                         */
813
814     HDC phdc;
815     int cnt;
816     DWORD hr;
817     Scriptcache *pScriptcache;
818     *pcGlyphs = cChars;
819     FIXME("(%p, %p, %p, %d, %d, %p): semi-stub\n",  hdc, psc, pwcChars,
820                                        cChars, cMaxGlyphs, psa);
821     if (psa) TRACE("psa values: %d, %d, %d, %d, %d, %d, %d\n", psa->eScript, psa->fRTL, psa->fLayoutRTL,
822                                          psa->fLinkBefore, psa->fLinkAfter,
823                                          psa->fLogicalOrder, psa->fNoGlyphIndex);
824
825     if  (cChars > cMaxGlyphs) return E_OUTOFMEMORY;
826
827     if  (!hdc && !*psc) {
828         TRACE("No Script_Cache (psc) and no hdc. Ask for one. Hdc=%p, psc=%p\n", hdc, *psc);
829         return E_PENDING;
830     }   else 
831         if  (hdc && !*psc) {
832             pScriptcache = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(Scriptcache) );
833             pScriptcache->hdc = (HDC) hdc;
834             phdc = hdc;
835             *psc = (Scriptcache *) pScriptcache;
836        }   else
837             if  (*psc) {
838                 pScriptcache = (Scriptcache *) *psc;
839                 phdc = pScriptcache->hdc;
840             }
841                 
842     TRACE("Before: ");
843     for (cnt = 0; cnt < cChars; cnt++)
844          TRACE("%4x",pwcChars[cnt]);
845     TRACE("\n");
846
847     if  (!psa->fNoGlyphIndex) {                                         /* Glyph translate */
848         hr = GetGlyphIndicesW(phdc, pwcChars, cChars, pwOutGlyphs, 0);
849         TRACE("After:  ");
850         for (cnt = 0; cnt < cChars; cnt++) {
851              TRACE("%04x",pwOutGlyphs[cnt]);
852         }
853         TRACE("\n");
854     }
855     else {
856         TRACE("After:  ");
857         for (cnt = 0; cnt < cChars; cnt++) {                           /* no translate so set up */
858              pwOutGlyphs[cnt] = pwcChars[cnt];                         /* copy in to out and     */
859              TRACE("%04x",pwOutGlyphs[cnt]);
860         }
861        TRACE("\n");
862     }
863
864     /*  Set up a valid SCRIPT_VISATTR and LogClust for each char in this run */     
865     for (cnt = 0;  cnt < cChars; cnt++) {
866          psva[cnt].uJustification = 2;
867          psva[cnt].fClusterStart = 1;
868          psva[cnt].fDiacritic = 0;
869          psva[cnt].fZeroWidth = 0;
870          pwLogClust[cnt] = cnt;
871     }
872     return 0; 
873 }
874
875 /***********************************************************************
876  *      ScriptPlace (USP10.@)
877  *
878  */
879 HRESULT WINAPI ScriptPlace(HDC hdc, SCRIPT_CACHE *psc, const WORD *pwGlyphs, 
880                            int cGlyphs, const SCRIPT_VISATTR *psva,
881                            SCRIPT_ANALYSIS *psa, int *piAdvance, GOFFSET *pGoffset, ABC *pABC )
882 {
883     HDC phdc;
884     int wcnt;
885     LPABC lpABC;
886     Scriptcache *pScriptcache;
887     FIXME("(%p, %p, %p, %s, %d, %p, %p, %p): semi-stub\n",  hdc, psc, pwGlyphs,
888                                                 debugstr_wn(pwGlyphs, cGlyphs), 
889                                                 cGlyphs, psva, psa, 
890                                                 piAdvance);
891
892     /*  We need a valid hdc to do any of the font calls.  The spec says that hdc is optional and 
893      *  psc will be used first.  If psc and hdc are not specified E_PENDING is returned to get 
894      *  the caller to return the hdc.  For convenience, the hdc is cached in SCRIPT_CACHE.    */
895
896     if  (!hdc && !*psc) {
897         TRACE("No Script_Cache (psc) and no hdc. Ask for one. Hdc=%p, psc=%p\n", hdc, *psc);
898         return E_PENDING;
899     }   else 
900         if  (hdc && !*psc) {
901             pScriptcache = HeapAlloc( GetProcessHeap(), 0, sizeof(Scriptcache) );
902             pScriptcache->hdc = hdc;
903             phdc = hdc;
904             *psc = pScriptcache;
905         }   else
906             if  (*psc) {
907                 pScriptcache = *psc;
908                 phdc = pScriptcache->hdc;
909             }
910
911     /*   Here we need to calculate the width of the run unit.  At this point the input string
912      *   has been converted to glyphs and we still need to translate back to the original chars
913      *   to get the correct ABC widths.   */
914
915      lpABC = HeapAlloc(GetProcessHeap(), 0 , sizeof(ABC)*cGlyphs);
916      pABC->abcA = 0; 
917      pABC->abcB = 0; 
918      pABC->abcC = 0; 
919      if  (!GetCharABCWidthsI(phdc, 0, cGlyphs, (WORD *) pwGlyphs, lpABC )) 
920      {
921          WARN("Could not get ABC values\n");
922          for (wcnt = 0; wcnt < cGlyphs; wcnt++) {
923              piAdvance[wcnt] = 0;
924              pGoffset[wcnt].du = 0;
925              pGoffset[wcnt].dv = 0;
926          }
927      }
928      else
929      {
930          for (wcnt = 0; wcnt < cGlyphs ; wcnt++) {          /* add up the char lengths  */
931              TRACE("     Glyph=%04x,  abcA=%d,  abcB=%d,  abcC=%d  wcnt=%d\n",
932                                   pwGlyphs[wcnt],  
933                                   lpABC[wcnt].abcA,
934                                   lpABC[wcnt].abcB,
935                                   lpABC[wcnt].abcC, wcnt);
936              pABC->abcA += lpABC[wcnt].abcA;
937              pABC->abcB += lpABC[wcnt].abcB;
938              pABC->abcC += lpABC[wcnt].abcC;
939              piAdvance[wcnt] = lpABC[wcnt].abcA + lpABC[wcnt].abcB + lpABC[wcnt].abcC;
940              pGoffset[wcnt].du = 0;
941              pGoffset[wcnt].dv = 0;
942          }
943      }
944      TRACE("Total for run:   abcA=%d,  abcB=%d,  abcC=%d\n", pABC->abcA, pABC->abcB, pABC->abcC);
945
946      HeapFree(GetProcessHeap(), 0, lpABC );
947
948      return 0;
949 }
950
951 /***********************************************************************
952  *      ScriptGetCMap (USP10.@)
953  *
954  */
955 HRESULT WINAPI ScriptGetCMap(HDC hdc, SCRIPT_CACHE *psc, const WCHAR *pwcInChars,
956                               int cChars, DWORD dwFlags, WORD *pwOutGlyphs)
957 {
958     HDC phdc;
959     int cnt;
960     DWORD hr;
961     Scriptcache *pScriptcache;
962     FIXME("(%p,%p,%s,%d,0x%x,%p): semi-stub\n", hdc, psc, debugstr_wn(pwcInChars,cChars),
963                                                  cChars, dwFlags, pwOutGlyphs);
964
965     if  (!psc)
966         return E_INVALIDARG;
967
968     if  (!hdc && !*psc) {
969         TRACE("No Script_Cache (psc) and no hdc. Ask for one. Hdc=%p, psc=%p\n", hdc, *psc);
970         return E_PENDING;
971     }   else 
972         if  (hdc && !*psc) {
973             pScriptcache = HeapAlloc( GetProcessHeap(), 0, sizeof(Scriptcache) );
974             pScriptcache->hdc = hdc;
975             phdc = hdc;
976             *psc = pScriptcache;
977         }   else
978             if  (*psc) {
979                 pScriptcache = *psc;
980                 phdc = pScriptcache->hdc;
981             }
982
983     TRACE("Before: ");
984     for (cnt = 0; cnt < cChars; cnt++)
985          TRACE("%4x",pwcInChars[cnt]);
986     TRACE("\n");
987
988     hr = GetGlyphIndicesW(phdc, pwcInChars, cChars, pwOutGlyphs, 0);
989     TRACE("After:  ");
990     for (cnt = 0; cnt < cChars; cnt++) {
991          TRACE("%04x",pwOutGlyphs[cnt]);
992     }
993     TRACE("\n");
994
995     return 0; 
996 }
997
998 /***********************************************************************
999  *      ScriptTextOut (USP10.@)
1000  *
1001  */
1002 HRESULT WINAPI ScriptTextOut(const HDC hdc, SCRIPT_CACHE *psc, int x, int y, UINT fuOptions, 
1003                              const RECT *lprc, const SCRIPT_ANALYSIS *psa, const WCHAR *pwcReserved, 
1004                              int iReserved, const WORD *pwGlyphs, int cGlyphs, const int *piAdvance, 
1005                              const int *piJustify, const GOFFSET *pGoffset)
1006 {
1007     HDC phdc;
1008     DWORD hr;
1009     Scriptcache *pScriptcache;
1010     TRACE     ("(%p, %p, %d, %d, %04x, %p, %p, %p, %d, %p, %d, %p, %p, %p): stub\n",
1011          hdc, psc, x, y, fuOptions, lprc, psa, pwcReserved, iReserved, pwGlyphs, cGlyphs,
1012          piAdvance, piJustify, pGoffset);
1013
1014     if  (!hdc || !psc || !piAdvance || !psa || !pwGlyphs)         /* hdc is mandatory                 */
1015         return E_INVALIDARG;
1016         
1017     if  (!*psc) {
1018         pScriptcache = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(Scriptcache) );
1019         pScriptcache->hdc = hdc;
1020         phdc = hdc;
1021         *psc = pScriptcache;
1022     } else {
1023         pScriptcache = *psc;
1024         phdc = pScriptcache->hdc;
1025     }
1026
1027     fuOptions &= ETO_CLIPPED + ETO_OPAQUE;
1028     if  (!psa->fNoGlyphIndex)                                     /* Have Glyphs?                      */
1029         fuOptions |= ETO_GLYPH_INDEX;                             /* Say don't do translation to glyph */
1030
1031     hr = ExtTextOutW(phdc, x, y, fuOptions, lprc, pwGlyphs, cGlyphs, NULL);
1032
1033     if  (hr) return S_OK;
1034     else {
1035         FIXME("ExtTextOut returned:=%d\n", hr);
1036         return hr;
1037     }
1038 }
1039
1040 /***********************************************************************
1041  *      ScriptCacheGetHeight (USP10.@)
1042  *
1043  * Retrieve the height of the font in the cache.
1044  *
1045  * PARAMS
1046  *  hdc    [I]    Device context.
1047  *  psc    [I/O]  Opaque pointer to a script cache.
1048  *  height [O]    Receives font height.
1049  *
1050  * RETURNS
1051  *  Success: S_OK
1052  *  Failure: Non-zero HRESULT value.
1053  */
1054 HRESULT WINAPI ScriptCacheGetHeight(HDC hdc, SCRIPT_CACHE *psc, long *height)
1055 {
1056     HDC phdc;
1057     Scriptcache *pScriptcache;
1058     TEXTMETRICW metric;
1059
1060     TRACE("(%p, %p, %p)\n", hdc, psc, height);
1061
1062     if  (!psc || !height)
1063         return E_INVALIDARG;
1064
1065     if (!hdc) return E_PENDING;
1066
1067     if  (!*psc) {
1068         pScriptcache = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(Scriptcache));
1069         pScriptcache->hdc = hdc;
1070         phdc = hdc;
1071         *psc = pScriptcache;
1072     } else {
1073         pScriptcache = *psc;
1074         phdc = pScriptcache->hdc;
1075     }
1076
1077     /* FIXME: get this from the cache */
1078     if (!GetTextMetricsW(phdc, &metric))
1079         return E_INVALIDARG;
1080
1081     *height = metric.tmHeight;
1082     return S_OK;
1083 }
1084
1085 /***********************************************************************
1086  *      ScriptGetGlyphABCWidth (USP10.@)
1087  *
1088  * Retrieve the width of a glyph.
1089  *
1090  * PARAMS
1091  *  hdc    [I]    Device context.
1092  *  psc    [I/O]  Opaque pointer to a script cache.
1093  *  glyph  [I]    Glyph to retrieve the width for.
1094  *  abc    [O]    ABC widths of the glyph.
1095  *
1096  * RETURNS
1097  *  Success: S_OK
1098  *  Failure: Non-zero HRESULT value.
1099  */
1100 HRESULT WINAPI ScriptGetGlyphABCWidth(HDC hdc, SCRIPT_CACHE *psc, WORD glyph, ABC *abc)
1101 {
1102     HDC phdc;
1103     Scriptcache *pScriptcache;
1104
1105     TRACE("(%p, %p, 0x%04x, %p)\n", hdc, psc, glyph, abc);
1106
1107     if  (!psc)
1108         return E_INVALIDARG;
1109
1110     if (!hdc) return E_PENDING;
1111
1112     if  (!*psc) {
1113         pScriptcache = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(Scriptcache));
1114         pScriptcache->hdc = hdc;
1115         phdc = hdc;
1116         *psc = pScriptcache;
1117     } else {
1118         pScriptcache = *psc;
1119         phdc = pScriptcache->hdc;
1120     }
1121
1122     /* FIXME: get this from the cache */
1123     if (!GetCharABCWidthsW(phdc, glyph, glyph, abc))
1124         return E_HANDLE;
1125
1126     return S_OK;
1127 }
1128
1129 /***********************************************************************
1130  *      ScriptLayout (USP10.@)
1131  *
1132  * Map embedding levels to visual and/or logical order.
1133  *
1134  * PARAMS
1135  *  runs     [I] Size of level array.
1136  *  level    [I] Array of embedding levels.
1137  *  vistolog [O] Map of embedding levels from visual to logical order.
1138  *  logtovis [O] Map of embedding levels from logical to visual order.
1139  *
1140  * RETURNS
1141  *  Success: S_OK
1142  *  Failure: Non-zero HRESULT value.
1143  *
1144  * BUGS
1145  *  This stub works correctly for any sequence of a single
1146  *  embedding level but not for sequences of different
1147  *  embedding levels, i.e. mixtures of RTL and LTR scripts.
1148  */
1149 HRESULT WINAPI ScriptLayout(int runs, const BYTE *level, int *vistolog, int *logtovis)
1150 {
1151     int i, j = runs - 1, k = 0;
1152
1153     FIXME("(%d, %p, %p, %p): stub\n", runs, level, vistolog, logtovis);
1154
1155     if (!level || (!vistolog && !logtovis))
1156         return E_INVALIDARG;
1157
1158     for (i = 0; i < runs; i++)
1159     {
1160         if (level[i] % 2)
1161         {
1162             if (vistolog) *vistolog++ = j;
1163             if (logtovis) *logtovis++ = j;
1164             j--;
1165         }
1166         else
1167         {
1168             if (vistolog) *vistolog++ = k;
1169             if (logtovis) *logtovis++ = k;
1170             k++;
1171         }
1172     }
1173     return S_OK;
1174 }
1175
1176 /***********************************************************************
1177  *      ScriptStringValidate (USP10.@)
1178  *
1179  * Validate a string analysis.
1180  *
1181  * PARAMS
1182  *  ssa [I] string analysis.
1183  *
1184  * RETURNS
1185  *  Success: S_OK
1186  *  Failure: S_FALSE if invalid sequences are found
1187  *           or a non-zero HRESULT if it fails.
1188  */
1189 HRESULT WINAPI ScriptStringValidate(SCRIPT_STRING_ANALYSIS ssa)
1190 {
1191     FIXME("(%p): stub\n", ssa);
1192     return S_OK;
1193 }