usp10/tests: Add trailing '\n's to ok() calls.
[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 "usp10.h"
32
33 #include "wine/debug.h"
34
35 /**
36  * some documentation here:
37  *   http://www.microsoft.com/typography/developers/uniscribe/uniscribe.htm
38  */
39
40 WINE_DEFAULT_DEBUG_CHANNEL(uniscribe);
41
42 #define MAX_SCRIPTS  8
43
44 /*  Set up a default for ScriptGetProperties    */
45 static const SCRIPT_PROPERTIES Default_Script_0 = {0, 0, 0, 0, 0, 0, 0, 0, 
46                                             0, 0, 0, 0, 0, 0, 0};
47 static const SCRIPT_PROPERTIES Default_Script_1 = {0, 0, 0, 0, 0, 0, 0, 0, 
48                                             0, 0, 0, 0, 0, 0, 0};
49 static const SCRIPT_PROPERTIES Default_Script_2 = {0, 0, 0, 0, 0, 0, 0, 0, 
50                                             0, 0, 0, 0, 0, 0, 0};
51 static const SCRIPT_PROPERTIES Default_Script_3 = {9, 0, 0, 0, 0, 0, 0, 0, 
52                                             0, 0, 0, 0, 0, 0, 0};
53 static const SCRIPT_PROPERTIES Default_Script_4 = {9, 1, 0, 0, 0, 0, 0, 0, 
54                                             0, 0, 0, 0, 0, 0, 0};
55 static const SCRIPT_PROPERTIES Default_Script_5 = {9, 0, 0, 0, 0, 0, 0, 0, 
56                                             0, 0, 0, 0, 1, 0, 0};
57 static const SCRIPT_PROPERTIES Default_Script_6 = {9, 1, 0, 0, 0, 0, 0, 0, 
58                                             0, 0, 0, 0, 1, 0, 0};
59 static const SCRIPT_PROPERTIES Default_Script_7 = {8, 0, 0, 0, 0, 161, 0, 0, 
60                                             0, 0, 0, 0, 0, 0, 0};
61 static const SCRIPT_PROPERTIES *Global_Script[MAX_SCRIPTS] =
62                                       {&Default_Script_0,
63                                        &Default_Script_1,
64                                        &Default_Script_2,
65                                        &Default_Script_3,
66                                        &Default_Script_4,
67                                        &Default_Script_5,
68                                        &Default_Script_6,
69                                        &Default_Script_7};
70
71 typedef struct scriptcache {
72        HDC hdc;
73 } Scriptcache;
74
75 /***********************************************************************
76  *      DllMain
77  *
78  */
79 BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpv)
80 {
81     switch(fdwReason) {
82         case DLL_PROCESS_ATTACH:
83             DisableThreadLibraryCalls(hInstDLL);
84             break;
85         case DLL_PROCESS_DETACH:
86             break;
87     }
88     return TRUE;
89 }
90
91 /***********************************************************************
92  *      ScriptFreeCache (USP10.@)
93  *
94  */
95 HRESULT WINAPI ScriptFreeCache(SCRIPT_CACHE *psc)
96 {
97     TRACE("%p\n", psc);
98
99     if (psc) {
100        HeapFree ( GetProcessHeap(), 0, *psc);
101        *psc = NULL;
102     }
103     return 0;
104 }
105
106 /***********************************************************************
107  *      ScriptGetProperties (USP10.@)
108  *
109  */
110 HRESULT WINAPI ScriptGetProperties(const SCRIPT_PROPERTIES ***ppSp, int *piNumScripts)
111 {
112     TRACE("%p,%p\n", ppSp, piNumScripts);
113
114     if (!ppSp && !piNumScripts) return E_INVALIDARG;
115
116     /* Set up a sensible default and intialise pointers  */
117     if (piNumScripts) *piNumScripts = MAX_SCRIPTS;
118     if (ppSp) *ppSp = Global_Script;
119     TRACE("ppSp:%p, *ppSp:%p, **ppSp:%p, %d\n",
120           ppSp, ppSp ? *ppSp : NULL, (ppSp && *ppSp) ? **ppSp : NULL,
121           piNumScripts ? *piNumScripts : -1);
122     return 0;
123 }
124
125 /***********************************************************************
126  *      ScriptGetFontProperties (USP10.@)
127  *
128  */
129 HRESULT WINAPI ScriptGetFontProperties(HDC hdc, SCRIPT_CACHE *psc, SCRIPT_FONTPROPERTIES *sfp)
130 {
131     HDC phdc;
132     Scriptcache *pScriptcache;
133     TEXTMETRICW ptm;
134
135     TRACE("%p,%p,%p\n", hdc, psc, sfp);
136
137     if (!psc || !sfp)
138         return E_INVALIDARG;
139     if  (!hdc && !*psc) {
140         TRACE("No Script_Cache (psc) and no hdc. Ask for one. Hdc=%p, psc=%p\n", hdc, *psc);
141         return E_PENDING;
142     }   else 
143         if  (hdc && !*psc) {
144             pScriptcache = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(Scriptcache) );
145             pScriptcache->hdc = (HDC) hdc;
146             phdc = hdc;
147             *psc = (Scriptcache *) pScriptcache;
148         }   else
149             if  (*psc) {
150                 pScriptcache = (Scriptcache *) *psc;
151                 phdc = pScriptcache->hdc;
152             }
153                 
154     if (sfp->cBytes != sizeof(SCRIPT_FONTPROPERTIES))
155         return E_INVALIDARG;
156
157     /* return something sensible? */
158     sfp->wgBlank       = 0;
159     if  (GetTextMetricsW(phdc, &ptm)) 
160         sfp->wgDefault = ptm.tmDefaultChar;
161     else
162         sfp->wgDefault = 0;
163     sfp->wgInvalid     = 0;
164     sfp->wgKashida     = 0xffff;
165     sfp->iKashidaWidth = 0;
166     return 0;
167 }
168
169 /***********************************************************************
170  *      ScriptRecordDigitSubstitution (USP10.@)
171  *
172  */
173 HRESULT WINAPI ScriptRecordDigitSubstitution(LCID Locale,SCRIPT_DIGITSUBSTITUTE *psds)
174 {
175     FIXME("%ld,%p\n",Locale,psds);
176     return E_NOTIMPL;
177 }
178
179 /***********************************************************************
180  *      ScriptApplyDigitSubstitution (USP10.@)
181  *
182  */
183 HRESULT WINAPI ScriptApplyDigitSubstitution(const SCRIPT_DIGITSUBSTITUTE* psds, 
184                                             SCRIPT_CONTROL* psc, SCRIPT_STATE* pss)
185 {
186     FIXME("%p,%p,%p\n",psds,psc,pss);
187     return E_NOTIMPL;
188 }
189
190 /***********************************************************************
191  *      ScriptItemize (USP10.@)
192  *
193  */
194 HRESULT WINAPI ScriptItemize(const WCHAR *pwcInChars, int cInChars, int cMaxItems, 
195                              const SCRIPT_CONTROL *psControl, const SCRIPT_STATE *psState, 
196                              SCRIPT_ITEM *pItems, int *pcItems)
197 {
198     /* This implementation currently treats the entire string represented in 
199      * pwcInChars as a single entity.  Hence pcItems will be set to 1.          */
200
201     FIXME("%s,%d,%d,%p,%p,%p,%p: semi-stub\n", debugstr_wn(pwcInChars, cInChars), cInChars, cMaxItems, 
202           psControl, psState, pItems, pcItems);
203
204     if (!pwcInChars || !cInChars || !pItems || cMaxItems < 2)
205         return E_INVALIDARG;
206
207     /*  Set a sensible default                              */
208     /*  Set SCRIPT_ITEM                                     */
209     pItems[0].iCharPos = 0;
210     /*  Set the SCRIPT_ANALYSIS                             */
211     pItems[0].a.eScript = SCRIPT_UNDEFINED;
212     pItems[0].a.fRTL = 0;
213     pItems[0].a.fLayoutRTL = 0;
214     pItems[0].a.fLinkBefore = 0;
215     pItems[0].a.fLinkAfter = 0;
216     pItems[0].a.fLogicalOrder = 0;
217     pItems[0].a.fNoGlyphIndex = 0;
218     /*  set the SCRIPT_STATE                                */
219     pItems[0].a.s.uBidiLevel = 0;
220     pItems[0].a.s.fOverrideDirection = 0;
221     pItems[0].a.s.fInhibitSymSwap = FALSE;
222     pItems[0].a.s.fCharShape = 0;
223     pItems[0].a.s.fDigitSubstitute = 0;
224     pItems[0].a.s.fInhibitLigate = 0;
225     pItems[0].a.s.fDisplayZWG = 0;
226     pItems[0].a.s.fArabicNumContext = 0;
227     pItems[0].a.s.fGcpClusters = 0;
228     pItems[0].a.s.fReserved = 0;
229     pItems[0].a.s.fEngineReserved = 0;
230
231     /* While not strickly necessary according to the spec, make sure the n+1
232      * item is set up to prevent random behaviour if the caller eroneously
233      * checks the n+1 structure                                              */
234     pItems[1].a.eScript = 0;
235     pItems[1].a.fRTL = 0;
236     pItems[1].a.fLayoutRTL = 0;
237     pItems[1].a.fLinkBefore = 0;
238     pItems[1].a.fLinkAfter = 0;
239     pItems[1].a.fLogicalOrder = 0;
240     pItems[1].a.fNoGlyphIndex = 0;
241     /*  set the SCRIPT_STATE                                */
242     pItems[1].a.s.uBidiLevel = 0;
243     pItems[1].a.s.fOverrideDirection = 0;
244     pItems[1].a.s.fInhibitSymSwap = FALSE;
245     pItems[1].a.s.fCharShape = 0;
246     pItems[1].a.s.fDigitSubstitute = 0;
247     pItems[1].a.s.fInhibitLigate = 0;
248     pItems[1].a.s.fDisplayZWG = 0;
249     pItems[1].a.s.fArabicNumContext = 0;
250     pItems[1].a.s.fGcpClusters = 0;
251     pItems[1].a.s.fReserved = 0;
252     pItems[1].a.s.fEngineReserved = 0;
253
254     /*  Set one SCRIPT_STATE item being returned  */
255     *pcItems = 1;
256
257     /*  Set SCRIPT_ITEM                                     */
258     pItems[1].iCharPos = cInChars - pItems[0].iCharPos ; /* the last + 1 item
259                                              contains the ptr to the lastchar */
260     TRACE("%s,%d,%d,%p,%p,%p,%p,%d\n", debugstr_wn(pwcInChars, cInChars), cInChars, cMaxItems, 
261           psControl, psState, pItems, pcItems, *pcItems);
262     TRACE("Start Pos in string: %d, Stop Pos %d\n", pItems[0].iCharPos, pItems[1].iCharPos);
263     return 0;
264 }
265
266 /***********************************************************************
267  *      ScriptStringAnalyse (USP10.@)
268  *
269  */
270 HRESULT WINAPI ScriptStringAnalyse(HDC hdc, 
271                                    const void *pString, 
272                                    int cString, 
273                                    int cGlyphs,
274                                    int iCharset,
275                                    DWORD dwFlags,
276                                    int iReqWidth,
277                                    SCRIPT_CONTROL *psControl,
278                                    SCRIPT_STATE *psState,
279                                    const int *piDx,
280                                    SCRIPT_TABDEF *pTabdef,
281                                    const BYTE *pbInClass,
282                                    SCRIPT_STRING_ANALYSIS *pssa)
283 {
284   FIXME("(%p,%p,%d,%d,%d,0x%lx,%d,%p,%p,%p,%p,%p,%p): stub\n",
285         hdc, pString, cString, cGlyphs, iCharset, dwFlags,
286         iReqWidth, psControl, psState, piDx, pTabdef, pbInClass, pssa);
287   if (1 > cString || NULL == pString) {
288     return E_INVALIDARG;
289   }
290   if ((dwFlags & SSA_GLYPHS) && NULL == hdc) {
291     return E_PENDING;
292   }
293
294   return E_NOTIMPL;
295 }
296
297 /***********************************************************************
298  *      ScriptStringOut (USP10.@)
299  *
300  */
301 HRESULT WINAPI ScriptStringOut(SCRIPT_STRING_ANALYSIS ssa, 
302                                int iX, 
303                                int iY, 
304                                UINT uOptions, 
305                                const RECT *prc, 
306                                int iMinSel, 
307                                int iMaxSel, 
308                                BOOL fDisabled)
309 {
310     FIXME("(%p,%d,%d,0x%1x,%p,%d,%d,%d): stub\n",
311          ssa, iX, iY, uOptions, prc, iMinSel, iMaxSel, fDisabled);
312     if  (!ssa) {
313         return E_INVALIDARG;
314     }
315
316     return E_NOTIMPL;
317 }
318
319 /***********************************************************************
320  *      ScriptStringCPtoX (USP10.@)
321  *
322  */
323 HRESULT WINAPI ScriptStringCPtoX(SCRIPT_STRING_ANALYSIS ssa, int icp, BOOL fTrailing, int* pX)
324 {
325     FIXME("(%p), %d, %d, (%p): stub\n", ssa, icp, fTrailing, pX);
326     *pX = 0;                             /* Set a reasonable value */
327     return S_OK;
328 }
329
330 /***********************************************************************
331  *      ScriptStringXtoCP (USP10.@)
332  *
333  */
334 HRESULT WINAPI ScriptStringXtoCP(SCRIPT_STRING_ANALYSIS ssa, int iX, int* piCh, int* piTrailing) 
335 {
336     FIXME("(%p), %d, (%p), (%p): stub\n", ssa, iX, piCh, piTrailing);
337     *piCh = 0;                          /* Set a reasonable value */
338     *piTrailing = 0;
339     return S_OK;
340 }
341
342 /***********************************************************************
343  *      ScriptStringFree (USP10.@)
344  *
345  */
346 HRESULT WINAPI ScriptStringFree(SCRIPT_STRING_ANALYSIS *pssa) {
347     FIXME("(%p): stub\n",pssa);
348     return S_OK;
349 }
350
351 /***********************************************************************
352  *      ScriptCPtoX (USP10.@)
353  *
354  */
355 HRESULT WINAPI ScriptCPtoX(int iCP,
356                            BOOL fTrailing,
357                            int cChars,
358                            int cGlyphs,
359                            const WORD *pwLogClust,
360                            const SCRIPT_VISATTR *psva,
361                            const int *piAdvance,
362                            const SCRIPT_ANALYSIS *psa,
363                            int *piX)
364 {
365     int  item;
366     int  iPosX;
367     float  fMaxPosX = 0;
368     TRACE("(%d,%d,%d,%d,%p,%p,%p,%p,%p)\n",
369           iCP, fTrailing, cChars, cGlyphs, pwLogClust, psva, piAdvance,
370           psa, piX);
371     for (item=0; item < cGlyphs; item++)            /* total piAdvance           */
372         fMaxPosX += piAdvance[item];
373     iPosX = (fMaxPosX/cGlyphs)*(iCP+fTrailing);
374     if  (iPosX > fMaxPosX)
375         iPosX = fMaxPosX;
376     *piX = iPosX;                                    /* Return something in range */
377
378     TRACE("*piX=%d\n", *piX);
379     return S_OK;
380 }
381
382 /***********************************************************************
383  *      ScriptXtoCP (USP10.@)
384  *
385  */
386 HRESULT WINAPI ScriptXtoCP(int iX,
387                            int cChars,
388                            int cGlyphs,
389                            const WORD *pwLogClust,
390                            const SCRIPT_VISATTR *psva,
391                            const int *piAdvance,
392                            const SCRIPT_ANALYSIS *psa,
393                            int *piCP,
394                            int *piTrailing)
395 {
396     int item;
397     int iPosX = 1;
398     float fMaxPosX = 1;
399     float fAvePosX;
400     TRACE("(%d,%d,%d,%p,%p,%p,%p,%p,%p)\n",
401           iX, cChars, cGlyphs, pwLogClust, psva, piAdvance,
402           psa, piCP, piTrailing);
403     if  (iX < 0)                                    /* iX is before start of run */
404     {
405         *piCP = -1;
406         *piTrailing = TRUE;
407         return S_OK;
408     }
409
410     for (item=0; item < cGlyphs; item++)            /* total piAdvance           */
411         fMaxPosX += piAdvance[item];
412
413     if  (iX >= fMaxPosX)                            /* iX too large              */
414     {
415         *piCP = cChars;
416         *piTrailing = FALSE;
417         return S_OK;
418     }        
419
420     fAvePosX = fMaxPosX / cGlyphs;
421     for (item = 0; item < cGlyphs  && iPosX < iX; item++)
422         iPosX = fAvePosX * (item +1);
423     if  (iPosX - iX > fAvePosX/2)
424         *piTrailing = 0;
425     else
426         *piTrailing = 1;                            /* yep we are over half way  */
427     
428     *piCP = item -1;                                /* Return character position */
429     TRACE("*piCP=%d iPposX=%d\n", *piCP, iPosX);
430     return S_OK;
431 }
432
433 /***********************************************************************
434  *      ScriptBreak (USP10.@)
435  *
436  */
437 HRESULT WINAPI ScriptBreak(const WCHAR *pwcChars, int cChars,  const SCRIPT_ANALYSIS *psa,
438                     SCRIPT_LOGATTR *psla)
439 {
440     FIXME("(%p,%d,%p,%p): stub\n",
441           pwcChars, cChars, psa, psla);
442
443     return S_OK;
444 }
445
446 static const struct
447 {
448     WCHAR start;
449     WCHAR end;
450     DWORD flag;
451 }
452 complex_ranges[] =
453 {
454     { 0, 0x0b, SIC_COMPLEX },
455     { 0x0c, 0x0c, SIC_NEUTRAL },
456     { 0x0d, 0x1f, SIC_COMPLEX },
457     { 0x20, 0x2f, SIC_NEUTRAL },
458     { 0x30, 0x39, SIC_ASCIIDIGIT },
459     { 0x3a, 0x40, SIC_NEUTRAL },
460     { 0x5b, 0x60, SIC_NEUTRAL },
461     { 0x7b, 0x7e, SIC_NEUTRAL },
462     { 0x7f, 0x9f, SIC_COMPLEX },
463     { 0xa0, 0xa5, SIC_NEUTRAL },
464     { 0xa7, 0xa8, SIC_NEUTRAL },
465     { 0xab, 0xab, SIC_NEUTRAL },
466     { 0xad, 0xad, SIC_NEUTRAL },
467     { 0xaf, 0xaf, SIC_NEUTRAL },
468     { 0xb0, 0xb1, SIC_NEUTRAL },
469     { 0xb4, 0xb4, SIC_NEUTRAL },
470     { 0xb6, 0xb8, SIC_NEUTRAL },
471     { 0xbb, 0xbf, SIC_NEUTRAL },
472     { 0xd7, 0xd7, SIC_NEUTRAL },
473     { 0xf7, 0xf7, SIC_NEUTRAL },
474     { 0x2b9, 0x2ba, SIC_NEUTRAL },
475     { 0x2c2, 0x2cf, SIC_NEUTRAL },
476     { 0x2d2, 0x2df, SIC_NEUTRAL },
477     { 0x2e5, 0x2e9, SIC_COMPLEX },
478     { 0x2ea, 0x2ed, SIC_NEUTRAL },
479     { 0x300, 0x362, SIC_COMPLEX },
480     { 0x530, 0x60b, SIC_COMPLEX },
481     { 0x60c, 0x60d, SIC_NEUTRAL },
482     { 0x60e, 0x669, SIC_COMPLEX },
483     { 0x66a, 0x66a, SIC_NEUTRAL },
484     { 0x66b, 0x6e8, SIC_COMPLEX },
485     { 0x6e9, 0x6e9, SIC_NEUTRAL },
486     { 0x6ea, 0x7bf, SIC_COMPLEX },
487     { 0x900, 0x1360, SIC_COMPLEX },
488     { 0x137d, 0x137f, SIC_COMPLEX },
489     { 0x1680, 0x1680, SIC_NEUTRAL },
490     { 0x1780, 0x18af, SIC_COMPLEX },
491     { 0x2000, 0x200a, SIC_NEUTRAL },
492     { 0x200b, 0x200f, SIC_COMPLEX },
493     { 0x2010, 0x2016, SIC_NEUTRAL },
494     { 0x2018, 0x2022, SIC_NEUTRAL },
495     { 0x2024, 0x2028, SIC_NEUTRAL },
496     { 0x2029, 0x202e, SIC_COMPLEX },
497     { 0x202f, 0x2037, SIC_NEUTRAL },
498     { 0x2039, 0x203c, SIC_NEUTRAL },
499     { 0x2044, 0x2046, SIC_NEUTRAL },
500     { 0x206a, 0x206f, SIC_COMPLEX },
501     { 0x207a, 0x207e, SIC_NEUTRAL },
502     { 0x208a, 0x20aa, SIC_NEUTRAL },
503     { 0x20ac, 0x20cf, SIC_NEUTRAL },
504     { 0x20d0, 0x20ff, SIC_COMPLEX },
505     { 0x2103, 0x2103, SIC_NEUTRAL },
506     { 0x2105, 0x2105, SIC_NEUTRAL },
507     { 0x2109, 0x2109, SIC_NEUTRAL },
508     { 0x2116, 0x2116, SIC_NEUTRAL },
509     { 0x2121, 0x2122, SIC_NEUTRAL },
510     { 0x212e, 0x212e, SIC_NEUTRAL },
511     { 0x2153, 0x2154, SIC_NEUTRAL },
512     { 0x215b, 0x215e, SIC_NEUTRAL },
513     { 0x2190, 0x2199, SIC_NEUTRAL },
514     { 0x21b8, 0x21b9, SIC_NEUTRAL },
515     { 0x21d2, 0x21d2, SIC_NEUTRAL },
516     { 0x21d4, 0x21d4, SIC_NEUTRAL },
517     { 0x21e7, 0x21e7, SIC_NEUTRAL },
518     { 0x2200, 0x2200, SIC_NEUTRAL },
519     { 0x2202, 0x2203, SIC_NEUTRAL },
520     { 0x2207, 0x2208, SIC_NEUTRAL },
521     { 0x220b, 0x220b, SIC_NEUTRAL },
522     { 0x220f, 0x220f, SIC_NEUTRAL },
523     { 0x2211, 0x2213, SIC_NEUTRAL },
524     { 0x2215, 0x2215, SIC_NEUTRAL },
525     { 0x221a, 0x221a, SIC_NEUTRAL },
526     { 0x221d, 0x2220, SIC_NEUTRAL },
527     { 0x2223, 0x2223, SIC_NEUTRAL },
528     { 0x2225, 0x2225, SIC_NEUTRAL },
529     { 0x2227, 0x222c, SIC_NEUTRAL },
530     { 0x222e, 0x222e, SIC_NEUTRAL },
531     { 0x2234, 0x2237, SIC_NEUTRAL },
532     { 0x223c, 0x223d, SIC_NEUTRAL },
533     { 0x2248, 0x2248, SIC_NEUTRAL },
534     { 0x224c, 0x224c, SIC_NEUTRAL },
535     { 0x2252, 0x2252, SIC_NEUTRAL },
536     { 0x2260, 0x2261, SIC_NEUTRAL },
537     { 0x2264, 0x2267, SIC_NEUTRAL },
538     { 0x226a, 0x226b, SIC_NEUTRAL },
539     { 0x226e, 0x226f, SIC_NEUTRAL },
540     { 0x2282, 0x2283, SIC_NEUTRAL },
541     { 0x2286, 0x2287, SIC_NEUTRAL },
542     { 0x2295, 0x2295, SIC_NEUTRAL },
543     { 0x2299, 0x2299, SIC_NEUTRAL },
544     { 0x22a5, 0x22a5, SIC_NEUTRAL },
545     { 0x22bf, 0x22bf, SIC_NEUTRAL },
546     { 0x2312, 0x2312, SIC_NEUTRAL },
547     { 0x24ea, 0x24ea, SIC_COMPLEX },
548     { 0x2500, 0x254b, SIC_NEUTRAL },
549     { 0x2550, 0x256d, SIC_NEUTRAL },
550     { 0x256e, 0x2574, SIC_NEUTRAL },
551     { 0x2581, 0x258f, SIC_NEUTRAL },
552     { 0x2592, 0x2595, SIC_NEUTRAL },
553     { 0x25a0, 0x25a1, SIC_NEUTRAL },
554     { 0x25a3, 0x25a9, SIC_NEUTRAL },
555     { 0x25b2, 0x25b3, SIC_NEUTRAL },
556     { 0x25b6, 0x25b7, SIC_NEUTRAL },
557     { 0x25bc, 0x25bd, SIC_NEUTRAL },
558     { 0x25c0, 0x25c1, SIC_NEUTRAL },
559     { 0x25c6, 0x25c8, SIC_NEUTRAL },
560     { 0x25cb, 0x25cb, SIC_NEUTRAL },
561     { 0x25ce, 0x25d1, SIC_NEUTRAL },
562     { 0x25e2, 0x25e5, SIC_NEUTRAL },
563     { 0x25ef, 0x25ef, SIC_NEUTRAL },
564     { 0x2605, 0x2606, SIC_NEUTRAL },
565     { 0x2609, 0x2609, SIC_NEUTRAL },
566     { 0x260e, 0x260f, SIC_NEUTRAL },
567     { 0x261c, 0x261c, SIC_NEUTRAL },
568     { 0x261e, 0x261e, SIC_NEUTRAL },
569     { 0x2640, 0x2640, SIC_NEUTRAL },
570     { 0x2642, 0x2642, SIC_NEUTRAL },
571     { 0x2660, 0x2661, SIC_NEUTRAL },
572     { 0x2663, 0x2665, SIC_NEUTRAL },
573     { 0x2667, 0x266a, SIC_NEUTRAL },
574     { 0x266c, 0x266d, SIC_NEUTRAL },
575     { 0x266f, 0x266f, SIC_NEUTRAL },
576     { 0x273d, 0x273d, SIC_NEUTRAL },
577     { 0x2e80, 0x312f, SIC_COMPLEX },
578     { 0x3190, 0x31bf, SIC_COMPLEX },
579     { 0x31f0, 0x31ff, SIC_COMPLEX },
580     { 0x3220, 0x325f, SIC_COMPLEX },
581     { 0x3280, 0xa4ff, SIC_COMPLEX },
582     { 0xd800, 0xdfff, SIC_COMPLEX },
583     { 0xe000, 0xf8ff, SIC_NEUTRAL },
584     { 0xf900, 0xfaff, SIC_COMPLEX },
585     { 0xfb13, 0xfb28, SIC_COMPLEX },
586     { 0xfb29, 0xfb29, SIC_NEUTRAL },
587     { 0xfb2a, 0xfb4f, SIC_COMPLEX },
588     { 0xfd3e, 0xfd3f, SIC_NEUTRAL },
589     { 0xfdd0, 0xfdef, SIC_COMPLEX },
590     { 0xfe20, 0xfe6f, SIC_COMPLEX },
591     { 0xfeff, 0xfeff, SIC_COMPLEX },
592     { 0xff01, 0xff5e, SIC_COMPLEX },
593     { 0xff61, 0xff9f, SIC_COMPLEX },
594     { 0xffe0, 0xffe6, SIC_COMPLEX },
595     { 0xffe8, 0xffee, SIC_COMPLEX },
596     { 0xfff9, 0xfffb, SIC_COMPLEX },
597     { 0xfffe, 0xfffe, SIC_COMPLEX }
598 };
599
600 /***********************************************************************
601  *      ScriptIsComplex (USP10.@)
602  * 
603  *  Determine if a string is complex.
604  *
605  *  PARAMS
606  *   chars [I] Array of characters to test.
607  *   len   [I] Length in characters.
608  *   flag  [I] Flag.
609  *
610  *  RETURNS
611  *   Success: S_OK
612  *   Failure: S_FALSE
613  *
614  *  NOTES
615  *   Behaviour matches that of WinXP.
616  */
617 HRESULT WINAPI ScriptIsComplex(const WCHAR *chars, int len, DWORD flag)
618 {
619     unsigned int i, j;
620
621     TRACE("(%s,%d,0x%lx)\n", debugstr_wn(chars, len), len, flag);
622
623     for (i = 0; i < len; i++)
624     {
625         for (j = 0; j < sizeof(complex_ranges)/sizeof(complex_ranges[0]); j++)
626         {
627             if (chars[i] >= complex_ranges[j].start &&
628                 chars[i] <= complex_ranges[j].end &&
629                 (flag & complex_ranges[j].flag)) return S_OK;
630         }
631     }
632     return S_FALSE;
633 }
634
635 /***********************************************************************
636  *      ScriptShape (USP10.@)
637  *
638  */
639 HRESULT WINAPI ScriptShape(HDC hdc, SCRIPT_CACHE *psc, const WCHAR *pwcChars, 
640                            int cChars, int cMaxGlyphs,
641                            SCRIPT_ANALYSIS *psa, WORD *pwOutGlyphs, WORD *pwLogClust, 
642                            SCRIPT_VISATTR *psva, int *pcGlyphs)
643 {
644     /*  Note SCRIPT_CACHE (*psc) appears to be a good place to save info that needs to be 
645      *  passed between functions.                                                         */
646
647     HDC phdc;
648     int cnt;
649     DWORD hr;
650     Scriptcache *pScriptcache;
651     *pcGlyphs = cChars;
652     FIXME("(%p, %p, %p, %d, %d, %p): semi-stub\n",  hdc, psc, pwcChars,
653                                        cChars, cMaxGlyphs, psa);
654     if (psa) TRACE("psa values: %d, %d, %d, %d, %d, %d, %d\n", psa->eScript, psa->fRTL, psa->fLayoutRTL,
655                                          psa->fLinkBefore, psa->fLinkAfter,
656                                          psa->fLogicalOrder, psa->fNoGlyphIndex);
657
658     if  (cChars > cMaxGlyphs) return E_OUTOFMEMORY;
659
660     if  (!hdc && !*psc) {
661         TRACE("No Script_Cache (psc) and no hdc. Ask for one. Hdc=%p, psc=%p\n", hdc, *psc);
662         return E_PENDING;
663     }   else 
664         if  (hdc && !*psc) {
665             pScriptcache = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(Scriptcache) );
666             pScriptcache->hdc = (HDC) hdc;
667             phdc = hdc;
668             *psc = (Scriptcache *) pScriptcache;
669        }   else
670             if  (*psc) {
671                 pScriptcache = (Scriptcache *) *psc;
672                 phdc = pScriptcache->hdc;
673             }
674                 
675     TRACE("Before: ");
676     for (cnt = 0; cnt < cChars; cnt++)
677          TRACE("%4x",pwcChars[cnt]);
678     TRACE("\n");
679
680     if  (!psa->fNoGlyphIndex) {                                         /* Glyph translate */
681         hr = GetGlyphIndicesW(phdc, pwcChars, cChars, pwOutGlyphs, 0);
682         TRACE("After:  ");
683         for (cnt = 0; cnt < cChars; cnt++) {
684              TRACE("%04x",pwOutGlyphs[cnt]);
685         }
686         TRACE("\n");
687     }
688     else {
689         TRACE("After:  ");
690         for (cnt = 0; cnt < cChars; cnt++) {                           /* no translate so set up */
691              pwOutGlyphs[cnt] = pwcChars[cnt];                         /* copy in to out and     */
692              TRACE("%04x",pwOutGlyphs[cnt]);
693         }
694        TRACE("\n");
695     }
696
697     /*  Set up a valid SCRIPT_VISATTR and LogClust for each char in this run */     
698     for (cnt = 0;  cnt < cChars; cnt++) {
699          psva[cnt].uJustification = 2;
700          psva[cnt].fClusterStart = 1;
701          psva[cnt].fDiacritic = 0;
702          psva[cnt].fZeroWidth = 0;
703          pwLogClust[cnt] = cnt;
704     }
705     return 0; 
706 }
707
708 /***********************************************************************
709  *      ScriptPlace (USP10.@)
710  *
711  */
712 HRESULT WINAPI ScriptPlace(HDC hdc, SCRIPT_CACHE *psc, const WORD *pwGlyphs, 
713                            int cGlyphs, const SCRIPT_VISATTR *psva,
714                            SCRIPT_ANALYSIS *psa, int *piAdvance, GOFFSET *pGoffset, ABC *pABC )
715 {
716     HDC phdc;
717     int wcnt;
718     LPABC lpABC;
719     Scriptcache *pScriptcache;
720     FIXME("(%p, %p, %p, %s, %d, %p, %p, %p): semi-stub\n",  hdc, psc, pwGlyphs,
721                                                 debugstr_wn(pwGlyphs, cGlyphs), 
722                                                 cGlyphs, psva, psa, 
723                                                 piAdvance);
724
725     /*  We need a valid hdc to do any of the font calls.  The spec says that hdc is optional and 
726      *  psc will be used first.  If psc and hdc are not specified E_PENDING is returned to get 
727      *  the caller to return the hdc.  For convience, the hdc is cached in SCRIPT_CACHE.    */
728
729     if  (!hdc && !*psc) {
730         TRACE("No Script_Cache (psc) and no hdc. Ask for one. Hdc=%p, psc=%p\n", hdc, *psc);
731         return E_PENDING;
732     }   else 
733         if  (hdc && !*psc) {
734             pScriptcache = HeapAlloc( GetProcessHeap(), 0, sizeof(Scriptcache) );
735             pScriptcache->hdc = hdc;
736             phdc = hdc;
737             *psc = pScriptcache;
738         }   else
739             if  (*psc) {
740                 pScriptcache = *psc;
741                 phdc = pScriptcache->hdc;
742             }
743
744     /*   Here we need to calculate the width of the run unit.  At this point the input string
745      *   has been converted to glyphs and we till need to translate back to the original chars
746      *   to get the correct ABC widths.   */
747
748      lpABC = HeapAlloc(GetProcessHeap(), 0 , sizeof(ABC)*cGlyphs);
749      pABC->abcA = 0; 
750      pABC->abcB = 0; 
751      pABC->abcC = 0; 
752      if  (!GetCharABCWidthsI(phdc, 0, cGlyphs, (WORD *) pwGlyphs, lpABC )) 
753      {
754          WARN("Could not get ABC values\n");
755          for (wcnt = 0; wcnt < cGlyphs; wcnt++) {
756              piAdvance[wcnt] = 0;
757              pGoffset[wcnt].du = 0;
758              pGoffset[wcnt].dv = 0;
759          }
760      }
761      else
762      {
763          for (wcnt = 0; wcnt < cGlyphs ; wcnt++) {          /* add up the char lengths  */
764              TRACE("     Glyph=%04x,  abcA=%d,  abcB=%d,  abcC=%d  wcnt=%d\n",
765                                   pwGlyphs[wcnt],  
766                                   lpABC[wcnt].abcA,
767                                   lpABC[wcnt].abcB,
768                                   lpABC[wcnt].abcC, wcnt);
769              pABC->abcA += lpABC[wcnt].abcA;
770              pABC->abcB += lpABC[wcnt].abcB;
771              pABC->abcC += lpABC[wcnt].abcC;
772              piAdvance[wcnt] = lpABC[wcnt].abcA + lpABC[wcnt].abcB + lpABC[wcnt].abcC;
773              pGoffset[wcnt].du = 0;
774              pGoffset[wcnt].dv = 0;
775          }
776      }
777      TRACE("Total for run:   abcA=%d,  abcB=%d,  abcC=%d\n", pABC->abcA, pABC->abcB, pABC->abcC);
778
779      HeapFree(GetProcessHeap(), 0, lpABC );
780
781      return 0;
782 }
783
784 /***********************************************************************
785  *      ScriptGetCMap (USP10.@)
786  *
787  */
788 HRESULT WINAPI ScriptGetCMap(HDC hdc, SCRIPT_CACHE *psc, const WCHAR *pwcInChars,
789                               int cChars, DWORD dwFlags, WORD *pwOutGlyphs)
790 {
791     HDC phdc;
792     int cnt;
793     DWORD hr;
794     Scriptcache *pScriptcache;
795     FIXME("(%p,%p,%s,%d,0x%lx,%p): semi-stub\n", hdc, psc, debugstr_wn(pwcInChars,cChars), 
796                                                  cChars, dwFlags, pwOutGlyphs);
797
798     if  (!psc)
799         return E_INVALIDARG;
800
801     if  (!hdc && !*psc) {
802         TRACE("No Script_Cache (psc) and no hdc. Ask for one. Hdc=%p, psc=%p\n", hdc, *psc);
803         return E_PENDING;
804     }   else 
805         if  (hdc && !*psc) {
806             pScriptcache = HeapAlloc( GetProcessHeap(), 0, sizeof(Scriptcache) );
807             pScriptcache->hdc = hdc;
808             phdc = hdc;
809             *psc = pScriptcache;
810         }   else
811             if  (*psc) {
812                 pScriptcache = *psc;
813                 phdc = pScriptcache->hdc;
814             }
815
816     TRACE("Before: ");
817     for (cnt = 0; cnt < cChars; cnt++)
818          TRACE("%4x",pwcInChars[cnt]);
819     TRACE("\n");
820
821     hr = GetGlyphIndicesW(phdc, pwcInChars, cChars, pwOutGlyphs, 0);
822     TRACE("After:  ");
823     for (cnt = 0; cnt < cChars; cnt++) {
824          TRACE("%04x",pwOutGlyphs[cnt]);
825     }
826     TRACE("\n");
827
828     return 0; 
829 }
830
831 /***********************************************************************
832  *      ScriptTextOut (USP10.@)
833  *
834  */
835 HRESULT WINAPI ScriptTextOut(const HDC hdc, SCRIPT_CACHE *psc, int x, int y, UINT fuOptions, 
836                              const RECT *lprc, const SCRIPT_ANALYSIS *psa, const WCHAR *pwcReserved, 
837                              int iReserved, const WORD *pwGlyphs, int cGlyphs, const int *piAdvance, 
838                              const int *piJustify, const GOFFSET *pGoffset)
839 {
840     HDC phdc;
841     DWORD hr;
842     Scriptcache *pScriptcache;
843     TRACE     ("(%p, %p, %d, %d, %04x, %p, %p, %p, %d, %p, %d, %p, %p, %p): stub\n",
844          hdc, psc, x, y, fuOptions, lprc, psa, pwcReserved, iReserved, pwGlyphs, cGlyphs,
845          piAdvance, piJustify, pGoffset);
846
847     if  (!hdc || !psc || !piAdvance || !psa || !pwGlyphs)         /* hdc is mandatory                 */
848         return E_INVALIDARG;
849         
850     if  (!*psc) {
851         pScriptcache = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(Scriptcache) );
852         pScriptcache->hdc = hdc;
853         phdc = hdc;
854         *psc = pScriptcache;
855     } else {
856         pScriptcache = *psc;
857         phdc = pScriptcache->hdc;
858     }
859
860     fuOptions &= ETO_CLIPPED + ETO_OPAQUE;
861     if  (!psa->fNoGlyphIndex)                                     /* Have Glyphs?                      */
862         fuOptions |= ETO_GLYPH_INDEX;                             /* Say don't do tranlastion to glyph */
863
864     hr = ExtTextOutW(phdc, x, y, fuOptions, lprc, pwGlyphs, cGlyphs, NULL);
865
866     if  (hr) return S_OK;
867     else {
868         FIXME("ExtTextOut returned:=%ld\n", hr);
869         return hr;
870     }
871 }
872
873 /***********************************************************************
874  *      ScriptCacheGetHeight (USP10.@)
875  *
876  * Retrieve the height of the font in the cache.
877  *
878  * PARAMS
879  *  hdc    [I]    Device context.
880  *  psc    [I/O]  Opaque pointer to a script cache.
881  *  height [O]    Receives font height.
882  *
883  * RETURNS
884  *  Success: S_OK
885  *  Failure: Non-zero HRESULT value.
886  */
887 HRESULT WINAPI ScriptCacheGetHeight(HDC hdc, SCRIPT_CACHE *psc, long *height)
888 {
889     HDC phdc;
890     Scriptcache *pScriptcache;
891     TEXTMETRICW metric;
892
893     TRACE("(%p, %p, %p)\n", hdc, psc, height);
894
895     if  (!psc || !height)
896         return E_INVALIDARG;
897
898     if (!hdc) return E_PENDING;
899
900     if  (!*psc) {
901         pScriptcache = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(Scriptcache));
902         pScriptcache->hdc = hdc;
903         phdc = hdc;
904         *psc = pScriptcache;
905     } else {
906         pScriptcache = *psc;
907         phdc = pScriptcache->hdc;
908     }
909
910     /* FIXME: get this from the cache */
911     if (!GetTextMetricsW(phdc, &metric))
912         return E_INVALIDARG;
913
914     *height = metric.tmHeight;
915     return S_OK;
916 }
917
918 /***********************************************************************
919  *      ScriptGetGlyphABCWidth (USP10.@)
920  *
921  * Retrieve the width of a glyph.
922  *
923  * PARAMS
924  *  hdc    [I]    Device context.
925  *  psc    [I/O]  Opaque pointer to a script cache.
926  *  glyph  [I]    Glyph to retrieve the width for.
927  *  abc    [O]    ABC widths of the glyph.
928  *
929  * RETURNS
930  *  Success: S_OK
931  *  Failure: Non-zero HRESULT value.
932  */
933 HRESULT WINAPI ScriptGetGlyphABCWidth(HDC hdc, SCRIPT_CACHE *psc, WORD glyph, ABC *abc)
934 {
935     HDC phdc;
936     Scriptcache *pScriptcache;
937
938     TRACE("(%p, %p, 0x%04x, %p)\n", hdc, psc, glyph, abc);
939
940     if  (!psc)
941         return E_INVALIDARG;
942
943     if (!hdc) return E_PENDING;
944
945     if  (!*psc) {
946         pScriptcache = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(Scriptcache));
947         pScriptcache->hdc = hdc;
948         phdc = hdc;
949         *psc = pScriptcache;
950     } else {
951         pScriptcache = *psc;
952         phdc = pScriptcache->hdc;
953     }
954
955     /* FIXME: get this from the cache */
956     if (!GetCharABCWidthsW(phdc, glyph, glyph, abc))
957         return E_HANDLE;
958
959     return S_OK;
960 }