shdocvw: Support URLs passed by reference in WebBrowser_Navigate2.
[wine] / dlls / usp10 / tests / usp10.c
1 /*
2  * Tests for usp10 dll
3  *
4  * Copyright 2006 Jeff Latimer
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 <assert.h>
27 #include <stdio.h>
28
29 #include <wine/test.h>
30 #include <winbase.h>
31 #include <wingdi.h>
32 #include <winuser.h>
33 #include <winerror.h>
34 #include <winnls.h>
35 #include <usp10.h>
36
37 static void test_ScriptShape(HDC hdc)
38 {
39     static const WCHAR test1[] = {'t', 'e', 's', 't',0};
40     BOOL ret;
41     HRESULT hr;
42     SCRIPT_CACHE sc = NULL;
43     WORD glyphs[4], logclust[4];
44     SCRIPT_VISATTR attrs[4];
45     SCRIPT_ITEM items[2];
46     int nb, widths[4];
47     GOFFSET offset[4];
48     ABC abc[4];
49
50     hr = ScriptItemize(NULL, 4, 2, NULL, NULL, items, NULL);
51     ok(hr == E_INVALIDARG, "ScriptItemize should return E_INVALIDARG not %08x\n", hr);
52
53     hr = ScriptItemize(test1, 4, 2, NULL, NULL, NULL, NULL);
54     ok(hr == E_INVALIDARG, "ScriptItemize should return E_INVALIDARG not %08x\n", hr);
55
56     hr = ScriptItemize(test1, 4, 2, NULL, NULL, items, NULL);
57     ok(!hr, "ScriptItemize should return S_OK not %08x\n", hr);
58     ok(items[0].a.fNoGlyphIndex == FALSE, "fNoGlyphIndex TRUE\n");
59
60     hr = ScriptShape(hdc, &sc, test1, 4, 4, &items[0].a, glyphs, NULL, NULL, &nb);
61     ok(hr == E_INVALIDARG, "ScriptShape should return E_INVALIDARG not %08x\n", hr);
62
63     hr = ScriptShape(hdc, &sc, test1, 4, 4, &items[0].a, glyphs, NULL, attrs, NULL);
64     ok(hr == E_INVALIDARG, "ScriptShape should return E_INVALIDARG not %08x\n", hr);
65
66     hr = ScriptShape(NULL, &sc, test1, 4, 4, &items[0].a, glyphs, NULL, attrs, &nb);
67     ok(hr == E_PENDING, "ScriptShape should return E_PENDING not %08x\n", hr);
68
69     hr = ScriptShape(hdc, &sc, test1, 4, 4, &items[0].a, glyphs, NULL, attrs, &nb);
70     ok(!hr ||
71        hr == E_INVALIDARG, /* Vista, W2K8 */
72        "ScriptShape should return S_OK or E_INVALIDARG, not %08x\n", hr);
73     ok(items[0].a.fNoGlyphIndex == FALSE, "fNoGlyphIndex TRUE\n");
74
75     hr = ScriptShape(hdc, &sc, test1, 4, 4, &items[0].a, glyphs, logclust, attrs, &nb);
76     ok(!hr, "ScriptShape should return S_OK not %08x\n", hr);
77     ok(items[0].a.fNoGlyphIndex == FALSE, "fNoGlyphIndex TRUE\n");
78
79     hr = ScriptShape(NULL, &sc, test1, 4, 4, &items[0].a, glyphs, logclust, attrs, &nb);
80     ok(!hr, "ScriptShape should return S_OK not %08x\n", hr);
81
82     hr = ScriptPlace(hdc, &sc, glyphs, 4, NULL, &items[0].a, widths, NULL, NULL);
83     ok(hr == E_INVALIDARG, "ScriptPlace should return E_INVALIDARG not %08x\n", hr);
84
85     hr = ScriptPlace(NULL, &sc, glyphs, 4, attrs, &items[0].a, widths, NULL, NULL);
86     ok(hr == E_PENDING ||
87        hr == E_INVALIDARG, /* Vista, W2K8 */
88        "ScriptPlace should return E_PENDING or E_INVALIDARG, not %08x\n", hr);
89
90     hr = ScriptPlace(NULL, &sc, glyphs, 4, attrs, &items[0].a, widths, offset, NULL);
91     ok(hr == E_PENDING, "ScriptPlace should return E_PENDING not %08x\n", hr);
92
93     hr = ScriptPlace(NULL, &sc, glyphs, 4, attrs, &items[0].a, widths, NULL, abc);
94     ok(hr == E_PENDING ||
95        hr == E_INVALIDARG, /* Vista, W2K8 */
96        "ScriptPlace should return E_PENDING or E_INVALIDARG, not %08x\n", hr);
97
98     hr = ScriptPlace(hdc, &sc, glyphs, 4, attrs, &items[0].a, widths, offset, NULL);
99     ok(!hr, "ScriptPlace should return S_OK not %08x\n", hr);
100     ok(items[0].a.fNoGlyphIndex == FALSE, "fNoGlyphIndex TRUE\n");
101
102     ret = ExtTextOutW(hdc, 1, 1, 0, NULL, glyphs, 4, widths);
103     ok(ret, "ExtTextOutW should return TRUE\n");
104
105     ScriptFreeCache(&sc);
106 }
107
108 static void test_ScriptItemIzeShapePlace(HDC hdc, unsigned short pwOutGlyphs[256])
109 {
110     HRESULT         hr;
111     int             iMaxProps;
112     const SCRIPT_PROPERTIES **ppSp;
113
114     int             cInChars;
115     int             cMaxItems;
116     SCRIPT_ITEM     pItem[255];
117     int             pcItems;
118     WCHAR           TestItem1[] = {'T', 'e', 's', 't', 'a', 0}; 
119     WCHAR           TestItem2[] = {'T', 'e', 's', 't', 'b', 0}; 
120     WCHAR           TestItem3[] = {'T', 'e', 's', 't', 'c',' ','1','2','3',' ',' ','e','n','d',0}; 
121     WCHAR           TestItem4[] = {'T', 'e', 's', 't', 'c',' ',0x0684,0x0694,0x06a4,' ',' ','e','n','d',0};
122     WCHAR           TestItem5[] = {0x0684,'T','e','s','t','c',' ',0x0684,0x0694,0x06a4,' ',' ','e','n','d',0}; 
123
124     SCRIPT_CACHE    psc;
125     int             cChars;
126     int             cMaxGlyphs;
127     unsigned short  pwOutGlyphs1[256];
128     unsigned short  pwOutGlyphs2[256];
129     unsigned short  pwLogClust[256];
130     SCRIPT_VISATTR  psva[256];
131     int             pcGlyphs;
132     int             piAdvance[256];
133     GOFFSET         pGoffset[256];
134     ABC             pABC[256];
135     int             cnt;
136
137     /* Start testing usp10 functions                                                         */
138     /* This test determines that the pointer returned by ScriptGetProperties is valid
139      * by checking a known value in the table                                                */
140     hr = ScriptGetProperties(&ppSp, &iMaxProps);
141     trace("number of script properties %d\n", iMaxProps);
142     ok (iMaxProps > 0, "Number of scripts returned should not be 0\n"); 
143     if  (iMaxProps > 0)
144          ok( ppSp[5]->langid == 9, "Langid[5] not = to 9\n"); /* Check a known value to ensure   */
145                                                               /* ptrs work                       */
146
147
148     /* This set of tests are to check that the various edits in ScriptIemize work           */
149     cInChars = 5;                                        /* Length of test without NULL     */
150     cMaxItems = 1;                                       /* Check threshold value           */
151     hr = ScriptItemize(TestItem1, cInChars, cMaxItems, NULL, NULL, pItem, &pcItems);
152     ok (hr == E_INVALIDARG, "ScriptItemize should return E_INVALIDARG if cMaxItems < 2.  Was %d\n",
153         cMaxItems);
154     cInChars = 5;
155     cMaxItems = 255;
156     hr = ScriptItemize(NULL, cInChars, cMaxItems, NULL, NULL, pItem, &pcItems);
157     ok (hr == E_INVALIDARG, "ScriptItemize should return E_INVALIDARG if pwcInChars is NULL\n");
158
159     cInChars = 5;
160     cMaxItems = 255;
161     hr = ScriptItemize(TestItem1, 0, cMaxItems, NULL, NULL, pItem, &pcItems);
162     ok (hr == E_INVALIDARG, "ScriptItemize should return E_INVALIDARG if cInChars is 0\n");
163
164     cInChars = 5;
165     cMaxItems = 255;
166     hr = ScriptItemize(TestItem1, cInChars, cMaxItems, NULL, NULL, NULL, &pcItems);
167     ok (hr == E_INVALIDARG, "ScriptItemize should return E_INVALIDARG if pItems is NULL\n");
168
169     /* This is a valid test that will cause parsing to take place                             */
170     cInChars = 5;
171     cMaxItems = 255;
172     hr = ScriptItemize(TestItem1, cInChars, cMaxItems, NULL, NULL, pItem, &pcItems);
173     ok (hr == 0, "ScriptItemize should return 0, returned %08x\n", hr);
174     /*  This test is for the interim operation of ScriptItemize where only one SCRIPT_ITEM is *
175      *  returned.                                                                             */
176     ok (pcItems > 0, "The number of SCRIPT_ITEMS should be greater than 0\n");
177     if (pcItems > 0)
178         ok (pItem[0].iCharPos == 0 && pItem[1].iCharPos == cInChars,
179             "Start pos not = 0 (%d) or end pos not = %d (%d)\n",
180             pItem[0].iCharPos, cInChars, pItem[1].iCharPos);
181
182     /* It would appear that we have a valid SCRIPT_ANALYSIS and can continue
183      * ie. ScriptItemize has succeeded and that pItem has been set                            */
184     cInChars = 5;
185     cMaxItems = 255;
186     if (hr == 0) {
187         psc = NULL;                                   /* must be null on first call           */
188         cChars = cInChars;
189         cMaxGlyphs = cInChars;
190         hr = ScriptShape(NULL, &psc, TestItem1, cChars,
191                          cMaxGlyphs, &pItem[0].a,
192                          pwOutGlyphs1, pwLogClust, psva, &pcGlyphs);
193         ok (hr == E_PENDING, "If psc is NULL (%08x) the E_PENDING should be returned\n", hr);
194         cMaxGlyphs = 4;
195         hr = ScriptShape(hdc, &psc, TestItem1, cChars,
196                          cMaxGlyphs, &pItem[0].a,
197                          pwOutGlyphs1, pwLogClust, psva, &pcGlyphs);
198         ok (hr == E_OUTOFMEMORY, "If not enough output area cChars (%d) is > than CMaxGlyphs "
199                                  "(%d) but not E_OUTOFMEMORY\n",
200                                  cChars, cMaxGlyphs);
201         cMaxGlyphs = 256;
202         hr = ScriptShape(hdc, &psc, TestItem1, cChars,
203                          cMaxGlyphs, &pItem[0].a,
204                          pwOutGlyphs1, pwLogClust, psva, &pcGlyphs);
205         ok (hr == 0, "ScriptShape should return 0 not (%08x)\n", hr);
206         ok (psc != NULL, "psc should not be null and have SCRIPT_CACHE buffer address\n");
207         ok (pcGlyphs == cChars, "Chars in (%d) should equal Glyphs out (%d)\n", cChars, pcGlyphs);
208         if (hr ==0) {
209             hr = ScriptPlace(hdc, &psc, pwOutGlyphs1, pcGlyphs, psva, &pItem[0].a, piAdvance,
210                              pGoffset, pABC);
211             ok (hr == 0, "ScriptPlace should return 0 not (%08x)\n", hr);
212             hr = ScriptPlace(NULL, &psc, pwOutGlyphs1, pcGlyphs, psva, &pItem[0].a, piAdvance,
213                              pGoffset, pABC);
214             ok (hr == 0, "ScriptPlace should return 0 not (%08x)\n", hr);
215             for (cnt=0; cnt < pcGlyphs; cnt++)
216                 pwOutGlyphs[cnt] = pwOutGlyphs1[cnt];                 /* Send to next function */
217         }
218
219         /* This test will check to make sure that SCRIPT_CACHE is reused and that not translation   *
220          * takes place if fNoGlyphIndex is set.                                                     */
221
222         cInChars = 5;
223         cMaxItems = 255;
224         hr = ScriptItemize(TestItem2, cInChars, cMaxItems, NULL, NULL, pItem, &pcItems);
225         ok (hr == 0, "ScriptItemize should return 0, returned %08x\n", hr);
226         /*  This test is for the interim operation of ScriptItemize where only one SCRIPT_ITEM is *
227          *  returned.                                                                               */
228         ok (pItem[0].iCharPos == 0 && pItem[1].iCharPos == cInChars,
229                             "Start pos not = 0 (%d) or end pos not = %d (%d)\n",
230                              pItem[0].iCharPos, cInChars, pItem[1].iCharPos);
231         /* It would appear that we have a valid SCRIPT_ANALYSIS and can continue                    */
232         if (hr == 0) {
233              cChars = cInChars;
234              cMaxGlyphs = 256;
235              pItem[0].a.fNoGlyphIndex = 1;                /* say no translate                     */
236              hr = ScriptShape(NULL, &psc, TestItem2, cChars,
237                               cMaxGlyphs, &pItem[0].a,
238                               pwOutGlyphs2, pwLogClust, psva, &pcGlyphs);
239              ok (hr != E_PENDING, "If psc should not be NULL (%08x) and the E_PENDING should be returned\n", hr);
240              ok (hr == 0, "ScriptShape should return 0 not (%08x)\n", hr);
241              ok (psc != NULL, "psc should not be null and have SCRIPT_CACHE buffer address\n");
242              ok (pcGlyphs == cChars, "Chars in (%d) should equal Glyphs out (%d)\n", cChars, pcGlyphs);
243              for (cnt=0; cnt < cChars && TestItem2[cnt] == pwOutGlyphs2[cnt]; cnt++) {}
244              ok (cnt == cChars, "Translation to place when told not to. WCHAR %d - %04x != %04x\n",
245                            cnt, TestItem2[cnt], pwOutGlyphs2[cnt]);
246              if (hr ==0) {
247                  hr = ScriptPlace(hdc, &psc, pwOutGlyphs2, pcGlyphs, psva, &pItem[0].a, piAdvance,
248                                   pGoffset, pABC);
249                  ok (hr == 0, "ScriptPlace should return 0 not (%08x)\n", hr);
250              }
251         }
252         hr = ScriptFreeCache( &psc);
253         ok (!psc, "psc is not null after ScriptFreeCache\n");
254
255     }
256
257     /* This is a valid test that will cause parsing to take place and create 3 script_items   */
258     cInChars = (sizeof(TestItem3)/2)-1;
259     cMaxItems = 255;
260     hr = ScriptItemize(TestItem3, cInChars, cMaxItems, NULL, NULL, pItem, &pcItems);
261     ok (hr == 0, "ScriptItemize should return 0, returned %08x\n", hr);
262     if  (hr == 0)
263         {
264         ok (pcItems == 3, "The number of SCRIPT_ITEMS should be 3 not %d\n", pcItems);
265         if (pcItems > 2)
266         {
267             ok (pItem[0].iCharPos == 0 && pItem[1].iCharPos == 6,
268                 "Start pos [0] not = 0 (%d) or end pos [1] not = %d\n",
269                 pItem[0].iCharPos, pItem[1].iCharPos);
270             ok (pItem[1].iCharPos == 6 && pItem[2].iCharPos == 11,
271                 "Start pos [1] not = 6 (%d) or end pos [2] not = 11 (%d)\n",
272                 pItem[1].iCharPos, pItem[2].iCharPos);
273             ok (pItem[2].iCharPos == 11 && pItem[3].iCharPos == cInChars,
274                 "Start pos [2] not = 11 (%d) or end [3] pos not = 14 (%d), cInChars = %d\n",
275                 pItem[2].iCharPos, pItem[3].iCharPos, cInChars);
276         }
277         hr = ScriptFreeCache( &psc);
278         ok (!psc, "psc is not null after ScriptFreeCache\n");
279     }
280
281     /* This is a valid test that will cause parsing to take place and create 3 script_items   */
282     cInChars = (sizeof(TestItem4)/2)-1;
283     cMaxItems = 255;
284     hr = ScriptItemize(TestItem4, cInChars, cMaxItems, NULL, NULL, pItem, &pcItems);
285     ok (hr == 0, "ScriptItemize should return 0, returned %08x\n", hr);
286     if  (hr == 0)
287         {
288         ok (pcItems == 3, "The number of SCRIPT_ITEMS should be 3 not %d\n", pcItems);
289         if (pcItems > 2)
290         {
291             ok (pItem[0].iCharPos == 0 && pItem[1].iCharPos == 6,
292                 "Start pos [0] not = 0 (%d) or end pos [1] not = %d\n",
293                 pItem[0].iCharPos, pItem[1].iCharPos);
294             ok (pItem[1].iCharPos == 6 && pItem[2].iCharPos == 11,
295                 "Start pos [1] not = 6 (%d) or end pos [2] not = 11 (%d)\n",
296                 pItem[1].iCharPos, pItem[2].iCharPos);
297             ok (pItem[2].iCharPos == 11 && pItem[3].iCharPos == cInChars,
298                 "Start pos [2] not = 11 (%d) or end [3] pos not = 14 (%d), cInChars = %d\n",
299                 pItem[2].iCharPos, pItem[3].iCharPos, cInChars);
300         }
301         hr = ScriptFreeCache( &psc);
302         ok (!psc, "psc is not null after ScriptFreeCache\n");
303     }
304
305     /*
306      * This test is for when the first unicode character requires bidi support
307      */ 
308     cInChars = (sizeof(TestItem5)-1)/sizeof(WCHAR);
309     hr = ScriptItemize(TestItem5, cInChars, cMaxItems, NULL, NULL, pItem, &pcItems);
310     ok (hr == 0, "ScriptItemize should return 0, returned %08x\n", hr);
311     ok (pcItems == 4, "There should have been 4 items, found %d\n", pcItems);
312     ok (pItem[0].a.s.uBidiLevel == 1, "The first character should have been bidi=1 not %d\n", 
313                                        pItem[0].a.s.uBidiLevel);
314 }
315
316 static void test_ScriptGetCMap(HDC hdc, unsigned short pwOutGlyphs[256])
317 {
318     HRESULT         hr;
319     SCRIPT_CACHE    psc = NULL;
320     int             cInChars;
321     int             cChars;
322     unsigned short  pwOutGlyphs3[256];
323     WCHAR           TestItem1[] = {'T', 'e', 's', 't', 'a', 0}; 
324     DWORD           dwFlags;
325     int             cnt;
326
327     /*  Check to make sure that SCRIPT_CACHE gets allocated ok                     */
328     dwFlags = 0;
329     cInChars = cChars = 5;
330     /* Some sanity checks for ScriptGetCMap */
331
332     hr = ScriptGetCMap(NULL, NULL, NULL, 0, 0, NULL);
333     ok( hr == E_INVALIDARG, "(NULL,NULL,NULL,0,0,NULL), "
334                             "expected E_INVALIDARG, got %08x\n", hr);
335
336     hr = ScriptGetCMap(NULL, NULL, TestItem1, cInChars, dwFlags, pwOutGlyphs3);
337     ok( hr == E_INVALIDARG, "(NULL,NULL,TestItem1, cInChars, dwFlags, pwOutGlyphs3), "
338                             "expected E_INVALIDARG, got %08x\n", hr);
339
340     /* Set psc to NULL, to be able to check if a pointer is returned in psc */
341     psc = NULL;
342     hr = ScriptGetCMap(NULL, &psc, NULL, 0, 0, NULL);
343     ok( hr == E_PENDING, "(NULL,&psc,NULL,0,0NULL), expected E_PENDING, "
344                          "got %08x\n", hr);
345     ok( psc == NULL, "Expected psc to be NULL, got %p\n", psc);
346
347     /* Set psc to NULL but add hdc, to be able to check if a pointer is returned in psc */
348     psc = NULL;
349     hr = ScriptGetCMap(hdc, &psc, NULL, 0, 0, NULL);
350     ok( hr == S_OK, "ScriptGetCMap(NULL,&psc,NULL,0,0,NULL), expected S_OK, "
351                     "got %08x\n", hr);
352     ok( psc != NULL, "ScritpGetCMap expected psc to be not NULL\n");
353     ScriptFreeCache( &psc);
354
355     /* Set psc to NULL, to be able to check if a pointer is returned in psc */
356     psc = NULL;
357     hr = ScriptGetCMap(NULL, &psc, TestItem1, cInChars, dwFlags, pwOutGlyphs3);
358     ok( hr == E_PENDING, "(NULL,&psc,), expected E_PENDING, got %08x\n", hr);
359     ok( psc == NULL, "Expected psc to be NULL, got %p\n", psc);
360     /*  Check to see if the results are the same as those returned by ScriptShape  */
361     hr = ScriptGetCMap(hdc, &psc, TestItem1, cInChars, dwFlags, pwOutGlyphs3);
362     ok (hr == 0, "ScriptGetCMap should return 0 not (%08x)\n", hr);
363     ok (psc != NULL, "psc should not be null and have SCRIPT_CACHE buffer address\n");
364     for (cnt=0; cnt < cChars && pwOutGlyphs[cnt] == pwOutGlyphs3[cnt]; cnt++) {}
365     ok (cnt == cInChars, "Translation not correct. WCHAR %d - %04x != %04x\n",
366                          cnt, pwOutGlyphs[cnt], pwOutGlyphs3[cnt]);
367         
368     hr = ScriptFreeCache( &psc);
369     ok (!psc, "psc is not null after ScriptFreeCache\n");
370
371 }
372
373 static void test_ScriptGetFontProperties(HDC hdc)
374 {
375     HRESULT         hr;
376     SCRIPT_CACHE    psc,old_psc;
377     SCRIPT_FONTPROPERTIES sfp;
378
379     /* Some sanity checks for ScriptGetFontProperties */
380
381     hr = ScriptGetFontProperties(NULL,NULL,NULL);
382     ok( hr == E_INVALIDARG, "(NULL,NULL,NULL), expected E_INVALIDARG, got %08x\n", hr);
383
384     hr = ScriptGetFontProperties(NULL,NULL,&sfp);
385     ok( hr == E_INVALIDARG, "(NULL,NULL,&sfp), expected E_INVALIDARG, got %08x\n", hr);
386
387     /* Set psc to NULL, to be able to check if a pointer is returned in psc */
388     psc = NULL;
389     hr = ScriptGetFontProperties(NULL,&psc,NULL);
390     ok( hr == E_INVALIDARG, "(NULL,&psc,NULL), expected E_INVALIDARG, got %08x\n", hr);
391     ok( psc == NULL, "Expected psc to be NULL, got %p\n", psc);
392
393     /* Set psc to NULL, to be able to check if a pointer is returned in psc */
394     psc = NULL;
395     hr = ScriptGetFontProperties(NULL,&psc,&sfp);
396     ok( hr == E_PENDING, "(NULL,&psc,&sfp), expected E_PENDING, got %08x\n", hr);
397     ok( psc == NULL, "Expected psc to be NULL, got %p\n", psc);
398
399     hr = ScriptGetFontProperties(hdc,NULL,NULL);
400     ok( hr == E_INVALIDARG, "(hdc,NULL,NULL), expected E_INVALIDARG, got %08x\n", hr);
401
402     hr = ScriptGetFontProperties(hdc,NULL,&sfp);
403     ok( hr == E_INVALIDARG, "(hdc,NULL,&sfp), expected E_INVALIDARG, got %08x\n", hr);
404
405     /* Set psc to NULL, to be able to check if a pointer is returned in psc */
406     psc = NULL;
407     hr = ScriptGetFontProperties(hdc,&psc,NULL);
408     ok( hr == E_INVALIDARG, "(hdc,&psc,NULL), expected E_INVALIDARG, got %08x\n", hr);
409     ok( psc == NULL, "Expected psc to be NULL, got %p\n", psc);
410
411     /* Pass an invalid sfp */
412     psc = NULL;
413     sfp.cBytes = sizeof(SCRIPT_FONTPROPERTIES) - 1;
414     hr = ScriptGetFontProperties(hdc,&psc,&sfp);
415     ok( hr == E_INVALIDARG, "(hdc,&psc,&sfp) invalid, expected E_INVALIDARG, got %08x\n", hr);
416     ok( psc != NULL, "Expected a pointer in psc, got NULL\n");
417     ScriptFreeCache(&psc);
418     ok( psc == NULL, "Expected psc to be NULL, got %p\n", psc);
419
420     /* Give it the correct cBytes, we don't care about what's coming back */
421     sfp.cBytes = sizeof(SCRIPT_FONTPROPERTIES);
422     psc = NULL;
423     hr = ScriptGetFontProperties(hdc,&psc,&sfp);
424     ok( hr == S_OK, "(hdc,&psc,&sfp) partly initialized, expected S_OK, got %08x\n", hr);
425     ok( psc != NULL, "Expected a pointer in psc, got NULL\n");
426
427     /* Save the psc pointer */
428     old_psc = psc;
429     /* Now a NULL hdc again */
430     hr = ScriptGetFontProperties(NULL,&psc,&sfp);
431     ok( hr == S_OK, "(NULL,&psc,&sfp), expected S_OK, got %08x\n", hr);
432     ok( psc == old_psc, "Expected psc not to be changed, was %p is now %p\n", old_psc, psc);
433     ScriptFreeCache(&psc);
434     ok( psc == NULL, "Expected psc to be NULL, got %p\n", psc);
435 }
436
437 static void test_ScriptTextOut(HDC hdc)
438 {
439     HRESULT         hr;
440
441     int             cInChars;
442     int             cMaxItems;
443     SCRIPT_ITEM     pItem[255];
444     int             pcItems;
445     WCHAR           TestItem1[] = {'T', 'e', 's', 't', 'a', 0}; 
446
447     SCRIPT_CACHE    psc;
448     int             cChars;
449     int             cMaxGlyphs;
450     unsigned short  pwOutGlyphs1[256];
451     WORD            pwLogClust[256];
452     SCRIPT_VISATTR  psva[256];
453     int             pcGlyphs;
454     int             piAdvance[256];
455     GOFFSET         pGoffset[256];
456     ABC             pABC[256];
457     RECT            rect;
458     int             piX;
459     int             iCP = 1;
460     BOOL            fTrailing = FALSE;
461     SCRIPT_LOGATTR  *psla;
462     SCRIPT_LOGATTR  sla[256];
463
464     /* This is a valid test that will cause parsing to take place                             */
465     cInChars = 5;
466     cMaxItems = 255;
467     hr = ScriptItemize(TestItem1, cInChars, cMaxItems, NULL, NULL, pItem, &pcItems);
468     ok (hr == 0, "ScriptItemize should return 0, returned %08x\n", hr);
469     /*  This test is for the interim operation of ScriptItemize where only one SCRIPT_ITEM is *
470      *  returned.                                                                             */
471     ok (pcItems > 0, "The number of SCRIPT_ITEMS should be greater than 0\n");
472     if (pcItems > 0)
473         ok (pItem[0].iCharPos == 0 && pItem[1].iCharPos == cInChars,
474             "Start pos not = 0 (%d) or end pos not = %d (%d)\n",
475             pItem[0].iCharPos, cInChars, pItem[1].iCharPos);
476
477     /* It would appear that we have a valid SCRIPT_ANALYSIS and can continue
478      * ie. ScriptItemize has succeeded and that pItem has been set                            */
479     cInChars = 5;
480     cMaxItems = 255;
481     if (hr == 0) {
482         psc = NULL;                                   /* must be null on first call           */
483         cChars = cInChars;
484         cMaxGlyphs = cInChars;
485         cMaxGlyphs = 256;
486         hr = ScriptShape(hdc, &psc, TestItem1, cChars,
487                          cMaxGlyphs, &pItem[0].a,
488                          pwOutGlyphs1, pwLogClust, psva, &pcGlyphs);
489         ok (hr == 0, "ScriptShape should return 0 not (%08x)\n", hr);
490         ok (psc != NULL, "psc should not be null and have SCRIPT_CACHE buffer address\n");
491         ok (pcGlyphs == cChars, "Chars in (%d) should equal Glyphs out (%d)\n", cChars, pcGlyphs);
492         if (hr ==0) {
493             /* Note hdc is needed as glyph info is not yet in psc                  */
494             hr = ScriptPlace(hdc, &psc, pwOutGlyphs1, pcGlyphs, psva, &pItem[0].a, piAdvance,
495                              pGoffset, pABC);
496             ok (hr == 0, "Should return 0 not (%08x)\n", hr);
497             ScriptFreeCache(&psc);              /* Get rid of psc for next test set */
498             ok( psc == NULL, "Expected psc to be NULL, got %p\n", psc);
499
500             hr = ScriptTextOut(NULL, NULL, 0, 0, 0, NULL, NULL, NULL, 0, NULL, 0, NULL, NULL, NULL);
501             ok (hr == E_INVALIDARG, "Should return 0 not (%08x)\n", hr);
502
503             hr = ScriptTextOut(NULL, NULL, 0, 0, 0, NULL, &pItem[0].a, NULL, 0, pwOutGlyphs1, pcGlyphs,
504                                piAdvance, NULL, pGoffset);
505             ok( hr == E_INVALIDARG, "(NULL,NULL,TestItem1, cInChars, dwFlags, pwOutGlyphs3), "
506                                     "expected E_INVALIDARG, got %08x\n", hr);
507
508             /* Set psc to NULL, to be able to check if a pointer is returned in psc */
509             psc = NULL;
510             hr = ScriptTextOut(NULL, &psc, 0, 0, 0, NULL, NULL, NULL, 0, NULL, 0,
511                                NULL, NULL, NULL);
512             ok( hr == E_INVALIDARG, "(NULL,&psc,NULL,0,0,0,NULL,), expected E_INVALIDARG, "
513                                     "got %08x\n", hr);
514             ok( psc == NULL, "Expected psc to be NULL, got %p\n", psc);
515
516             /* hdc is required for this one rather than the usual optional          */
517             psc = NULL;
518             hr = ScriptTextOut(NULL, &psc, 0, 0, 0, NULL, &pItem[0].a, NULL, 0, pwOutGlyphs1, pcGlyphs,
519                                piAdvance, NULL, pGoffset);
520             ok( hr == E_INVALIDARG, "(NULL,&psc,), expected E_INVALIDARG, got %08x\n", hr);
521             ok( psc == NULL, "Expected psc to be NULL, got %p\n", psc);
522
523             /* Set that it returns 0 status */
524             hr = ScriptTextOut(hdc, &psc, 0, 0, 0, NULL, &pItem[0].a, NULL, 0, pwOutGlyphs1, pcGlyphs,
525                                piAdvance, NULL, pGoffset);
526             ok (hr == 0, "ScriptTextOut should return 0 not (%08x)\n", hr);
527
528             /* Test Rect Rgn is acceptable */
529             rect.top = 10;
530             rect.bottom = 20;
531             rect.left = 10;
532             rect.right = 40;
533             hr = ScriptTextOut(hdc, &psc, 0, 0, 0, &rect, &pItem[0].a, NULL, 0, pwOutGlyphs1, pcGlyphs,
534                                piAdvance, NULL, pGoffset);
535             ok (hr == 0, "ScriptTextOut should return 0 not (%08x)\n", hr);
536
537             iCP = 1;
538             hr = ScriptCPtoX(iCP, fTrailing, cChars, pcGlyphs, (const WORD *) &pwLogClust,
539                             (const SCRIPT_VISATTR *) &psva, (const int *)&piAdvance, &pItem[0].a, &piX);
540             ok(hr == S_OK, "ScriptCPtoX Stub should return S_OK not %08x\n", hr);
541
542             psla = (SCRIPT_LOGATTR *)&sla;
543             hr = ScriptBreak(TestItem1, cChars, &pItem[0].a, psla);
544             ok(hr == S_OK, "ScriptBreak Stub should return S_OK not %08x\n", hr);
545
546             /* Clean up and go   */
547             ScriptFreeCache(&psc);
548             ok( psc == NULL, "Expected psc to be NULL, got %p\n", psc);
549         }
550     }
551 }
552
553 static void test_ScriptTextOut2(HDC hdc)
554 {
555 /*  Intent is to validate that the HDC passed into ScriptTextOut is
556  *  used instead of the (possibly) invalid cached one
557  */
558     HRESULT         hr;
559
560     HDC             hdc1, hdc2;
561     int             cInChars;
562     int             cMaxItems;
563     SCRIPT_ITEM     pItem[255];
564     int             pcItems;
565     WCHAR           TestItem1[] = {'T', 'e', 's', 't', 'a', 0};
566
567     SCRIPT_CACHE    psc;
568     int             cChars;
569     int             cMaxGlyphs;
570     unsigned short  pwOutGlyphs1[256];
571     WORD            pwLogClust[256];
572     SCRIPT_VISATTR  psva[256];
573     int             pcGlyphs;
574     int             piAdvance[256];
575     GOFFSET         pGoffset[256];
576     ABC             pABC[256];
577
578     /* Create an extra DC that will be used until the ScriptTextOut */
579     hdc1 = CreateCompatibleDC(hdc);
580     ok (hdc1 != 0, "CreateCompatibleDC failed to create a DC\n");
581     hdc2 = CreateCompatibleDC(hdc);
582     ok (hdc2 != 0, "CreateCompatibleDC failed to create a DC\n");
583
584     /* This is a valid test that will cause parsing to take place                             */
585     cInChars = 5;
586     cMaxItems = 255;
587     hr = ScriptItemize(TestItem1, cInChars, cMaxItems, NULL, NULL, pItem, &pcItems);
588     ok (hr == 0, "ScriptItemize should return 0, returned %08x\n", hr);
589     /*  This test is for the interim operation of ScriptItemize where only one SCRIPT_ITEM is *
590      *  returned.                                                                             */
591     ok (pcItems > 0, "The number of SCRIPT_ITEMS should be greater than 0\n");
592     if (pcItems > 0)
593         ok (pItem[0].iCharPos == 0 && pItem[1].iCharPos == cInChars,
594             "Start pos not = 0 (%d) or end pos not = %d (%d)\n",
595             pItem[0].iCharPos, cInChars, pItem[1].iCharPos);
596
597     /* It would appear that we have a valid SCRIPT_ANALYSIS and can continue
598      * ie. ScriptItemize has succeeded and that pItem has been set                            */
599     cInChars = 5;
600     cMaxItems = 255;
601     if (hr == 0) {
602         psc = NULL;                                   /* must be null on first call           */
603         cChars = cInChars;
604         cMaxGlyphs = cInChars;
605         cMaxGlyphs = 256;
606         hr = ScriptShape(hdc2, &psc, TestItem1, cChars,
607                          cMaxGlyphs, &pItem[0].a,
608                          pwOutGlyphs1, pwLogClust, psva, &pcGlyphs);
609         ok (hr == 0, "ScriptShape should return 0 not (%08x)\n", hr);
610         ok (psc != NULL, "psc should not be null and have SCRIPT_CACHE buffer address\n");
611         ok (pcGlyphs == cChars, "Chars in (%d) should equal Glyphs out (%d)\n", cChars, pcGlyphs);
612         if (hr ==0) {
613             /* Note hdc is needed as glyph info is not yet in psc                  */
614             hr = ScriptPlace(hdc2, &psc, pwOutGlyphs1, pcGlyphs, psva, &pItem[0].a, piAdvance,
615                              pGoffset, pABC);
616             ok (hr == 0, "Should return 0 not (%08x)\n", hr);
617
618             /*   key part!!!   cached dc is being deleted  */
619             hr = DeleteDC(hdc2);
620             ok(hr == 1, "DeleteDC should return 1 not %08x\n", hr);
621
622             /* At this point the cached hdc (hdc2) has been destroyed,
623              * however, we are passing in a *real* hdc (the original hdc).
624              * The text should be written to that DC
625              */
626             hr = ScriptTextOut(hdc1, &psc, 0, 0, 0, NULL, &pItem[0].a, NULL, 0, pwOutGlyphs1, pcGlyphs,
627                                piAdvance, NULL, pGoffset);
628             ok (hr == 0, "ScriptTextOut should return 0 not (%08x)\n", hr);
629             ok (psc != NULL, "psc should not be null and have SCRIPT_CACHE buffer address\n");
630
631             DeleteDC(hdc1);
632
633             /* Clean up and go   */
634             ScriptFreeCache(&psc);
635             ok( psc == NULL, "Expected psc to be NULL, got %p\n", psc);
636         }
637     }
638 }
639
640 static void test_ScriptXtoX(void)
641 /****************************************************************************************
642  *  This routine tests the ScriptXtoCP and ScriptCPtoX functions using static variables *
643  ****************************************************************************************/
644 {
645     static const WCHAR test[] = {'t', 'e', 's', 't',0};
646     SCRIPT_ITEM items[2];
647     int iX, iCP;
648     int cChars;
649     int cGlyphs;
650     WORD pwLogClust[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
651     SCRIPT_VISATTR psva[10];
652     int piAdvance[10] = {200, 190, 210, 180, 170, 204, 189, 195, 212, 203};
653     int piCP, piX;
654     int piTrailing;
655     BOOL fTrailing;
656     HRESULT hr;
657
658     hr = ScriptItemize(test, lstrlenW(test), sizeof(items)/sizeof(items[0]), NULL, NULL, items, NULL);
659     ok(!hr, "ScriptItemize should return S_OK not %08x\n", hr);
660
661     iX = -1;
662     cChars = 10;
663     cGlyphs = 10;
664     hr = ScriptXtoCP(iX, cChars, cGlyphs, pwLogClust, psva, piAdvance, &items[0].a, &piCP, &piTrailing);
665     ok(hr == S_OK, "ScriptXtoCP should return S_OK not %08x\n", hr);
666     if (piTrailing)
667         ok(piCP == -1, "Negative iX should return piCP=-1 not %d\n", piCP);
668     else /* win2k3 */
669         ok(piCP == 10, "Negative iX should return piCP=10 not %d\n", piCP);
670
671     iX = 1954;
672     cChars = 10;
673     cGlyphs = 10;
674     hr = ScriptXtoCP(iX, cChars, cGlyphs, pwLogClust, psva, piAdvance, &items[0].a, &piCP, &piTrailing);
675     ok(hr == S_OK, "ScriptXtoCP should return S_OK not %08x\n", hr);
676     if (piTrailing) /* win2k3 */
677         ok(piCP == -1, "Negative iX should return piCP=-1 not %d\n", piCP);
678     else
679         ok(piCP == 10, "Negative iX should return piCP=10 not %d\n", piCP);
680
681     iX = 779;
682     cChars = 10;
683     cGlyphs = 10;
684     hr = ScriptXtoCP(iX, cChars, cGlyphs, pwLogClust, psva, piAdvance, &items[0].a, &piCP, &piTrailing);
685     ok(hr == S_OK, "ScriptXtoCP should return S_OK not %08x\n", hr);
686     ok(piCP == 3 ||
687        piCP == -1, /* win2k3 */
688        "iX=%d should return piCP=3 or piCP=-1 not %d\n", iX, piCP);
689     ok(piTrailing == 1, "iX=%d should return piTrailing=1 not %d\n", iX, piTrailing);
690
691     iX = 780;
692     cChars = 10;
693     cGlyphs = 10;
694     hr = ScriptXtoCP(iX, cChars, cGlyphs, pwLogClust, psva, piAdvance, &items[0].a, &piCP, &piTrailing);
695     ok(hr == S_OK, "ScriptXtoCP should return S_OK not %08x\n", hr);
696     ok(piCP == 3 ||
697        piCP == -1, /* win2k3 */
698        "iX=%d should return piCP=3 or piCP=-1 not %d\n", iX, piCP);
699     ok(piTrailing == 1, "iX=%d should return piTrailing=1 not %d\n", iX, piTrailing);
700
701     iX = 868;
702     cChars = 10;
703     cGlyphs = 10;
704     hr = ScriptXtoCP(iX, cChars, cGlyphs, pwLogClust, psva, piAdvance, &items[0].a, &piCP, &piTrailing);
705     ok(hr == S_OK, "ScriptXtoCP should return S_OK not %08x\n", hr);
706     ok(piCP == 4 ||
707        piCP == -1, /* win2k3 */
708        "iX=%d should return piCP=4 or piCP=-1 not %d\n", iX, piCP);
709
710     iX = 0;
711     cChars = 10;
712     cGlyphs = 10;
713     hr = ScriptXtoCP(iX, cChars, cGlyphs, pwLogClust, psva, piAdvance, &items[0].a, &piCP, &piTrailing);
714     ok(hr == S_OK, "ScriptXtoCP should return S_OK not %08x\n", hr);
715     ok(piCP == 0 ||
716        piCP == 10, /* win2k3 */
717        "iX=%d should return piCP=0 piCP=10 not %d\n", iX, piCP);
718
719     iX = 195;
720     cChars = 10;
721     cGlyphs = 10;
722     hr = ScriptXtoCP(iX, cChars, cGlyphs, pwLogClust, psva, piAdvance, &items[0].a, &piCP, &piTrailing);
723     ok(hr == S_OK, "ScriptXtoCP should return S_OK not %08x\n", hr);
724     ok(piCP == 0, "iX=%d should return piCP=0 not %d\n", iX, piCP);
725
726     iX = 196;
727     cChars = 10;
728     cGlyphs = 10;
729     hr = ScriptXtoCP(iX, cChars, cGlyphs, pwLogClust, psva, piAdvance, &items[0].a, &piCP, &piTrailing);
730     ok(hr == S_OK, "ScriptXtoCP should return S_OK not %08x\n", hr);
731     ok(piCP == 1 ||
732        piCP == 0, /* win2k3 */
733        "iX=%d should return piCP=1 or piCP=0 not %d\n", iX, piCP);
734
735     iCP=5;
736     fTrailing = FALSE;
737     cChars = 10;
738     cGlyphs = 10;
739     hr = ScriptCPtoX(iCP, fTrailing, cChars, cGlyphs, pwLogClust, psva, piAdvance, &items[0].a, &piX);
740     ok(hr == S_OK, "ScriptCPtoX should return S_OK not %08x\n", hr);
741     ok(piX == 976 ||
742        piX == 100, /* win2k3 */
743        "iCP=%d should return piX=976 or piX=100 not %d\n", iCP, piX);
744
745     iCP=5;
746     fTrailing = TRUE;
747     cChars = 10;
748     cGlyphs = 10;
749     hr = ScriptCPtoX(iCP, fTrailing, cChars, cGlyphs, pwLogClust, psva, piAdvance, &items[0].a, &piX);
750     ok(hr == S_OK, "ScriptCPtoX should return S_OK not %08x\n", hr);
751     ok(piX == 1171 ||
752        piX == 80, /* win2k3 */
753        "iCP=%d should return piX=1171 or piX=80 not %d\n", iCP, piX);
754
755     iCP=6;
756     fTrailing = FALSE;
757     cChars = 10;
758     cGlyphs = 10;
759     hr = ScriptCPtoX(iCP, fTrailing, cChars, cGlyphs, pwLogClust, psva, piAdvance, &items[0].a, &piX);
760     ok(hr == S_OK, "ScriptCPtoX should return S_OK not %08x\n", hr);
761     ok(piX == 1171 ||
762        piX == 80, /* win2k3 */
763        "iCP=%d should return piX=1171 or piX=80 not %d\n", iCP, piX);
764
765     iCP=11;
766     fTrailing = FALSE;
767     cChars = 10;
768     cGlyphs = 10;
769     hr = ScriptCPtoX(iCP, fTrailing, cChars, cGlyphs, pwLogClust, psva, piAdvance, &items[0].a, &piX);
770     ok(hr == S_OK, "ScriptCPtoX should return S_OK not %08x\n", hr);
771     ok(piX == 1953 ||
772        piX == 0, /* win2k3 */
773        "iCP=%d should return piX=1953 or piX=0 not %d\n", iCP, piX);
774
775     iCP=11;
776     fTrailing = TRUE;
777     cChars = 10;
778     cGlyphs = 10;
779     hr = ScriptCPtoX(iCP, fTrailing, cChars, cGlyphs, pwLogClust, psva, piAdvance, &items[0].a, &piX);
780     ok(hr == S_OK, "ScriptCPtoX should return S_OK not %08x\n", hr);
781     ok(piX == 1953 ||
782        piX == 0, /* win2k3 */
783        "iCP=%d should return piX=1953 or piX=0 not %d\n", iCP, piX);
784 }
785
786 static void test_ScriptString(HDC hdc)
787 {
788 /*******************************************************************************************
789  *
790  * This set of tests are for the string functions of uniscribe.  The ScriptStringAnalyse
791  * function allocates memory pointed to by the SCRIPT_STRING_ANALYSIS ssa pointer.  This
792  * memory if freed by ScriptStringFree.  There needs to be a valid hdc for this as
793  * ScriptStringAnalyse calls ScriptSItemize, ScriptShape and ScriptPlace which require it.
794  *
795  */
796
797     HRESULT         hr;
798     WCHAR           teststr[] = {'T','e','s','t','1',' ','a','2','b','3', '\0'};
799     int             len = (sizeof(teststr) / sizeof(WCHAR)) - 1;
800     int             Glyphs = len * 2 + 16;
801     int             Charset;
802     DWORD           Flags = SSA_GLYPHS;
803     int             ReqWidth = 100;
804     SCRIPT_CONTROL  Control;
805     SCRIPT_STATE    State;
806     const int       Dx[5] = {10, 10, 10, 10, 10};
807     SCRIPT_TABDEF   Tabdef;
808     const BYTE      InClass = 0;
809     SCRIPT_STRING_ANALYSIS ssa = NULL;
810
811     int             X = 10; 
812     int             Y = 100;
813     UINT            Options = 0; 
814     const RECT      rc = {0, 50, 100, 100}; 
815     int             MinSel = 0;
816     int             MaxSel = 0;
817     BOOL            Disabled = FALSE;
818     const int      *clip_len;
819     int            i;
820     UINT           *order;
821
822
823     Charset = -1;     /* this flag indicates unicode input */
824     /* Test without hdc to get E_PENDING */
825     hr = ScriptStringAnalyse( NULL, teststr, len, Glyphs, Charset, Flags,
826                              ReqWidth, &Control, &State, Dx, &Tabdef,
827                              &InClass, &ssa);
828     ok(hr == E_PENDING, "ScriptStringAnalyse Stub should return E_PENDING not %08x\n", hr);
829
830     /* test with hdc, this should be a valid test  */
831     hr = ScriptStringAnalyse( hdc, teststr, len, Glyphs, Charset, Flags,
832                               ReqWidth, &Control, &State, Dx, &Tabdef,
833                               &InClass, &ssa);
834     ok(hr == S_OK, "ScriptStringAnalyse should return S_OK not %08x\n", hr);
835     ScriptStringFree(&ssa);
836
837     /* test makes sure that a call with a valid pssa still works */
838     hr = ScriptStringAnalyse( hdc, teststr, len, Glyphs, Charset, Flags,
839                               ReqWidth, &Control, &State, Dx, &Tabdef,
840                               &InClass, &ssa);
841     ok(hr == S_OK, "ScriptStringAnalyse should return S_OK not %08x\n", hr);
842     ok(ssa != NULL, "ScriptStringAnalyse pssa should not be NULL\n");
843
844     if (hr == S_OK)
845     {
846         hr = ScriptStringOut(ssa, X, Y, Options, &rc, MinSel, MaxSel, Disabled);
847         ok(hr == S_OK, "ScriptStringOut should return S_OK not %08x\n", hr);
848     }
849
850      clip_len = ScriptString_pcOutChars(ssa);
851      ok(*clip_len == len, "ScriptString_pcOutChars failed, got %d, expected %d\n", *clip_len, len);
852
853      order = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *clip_len * sizeof(UINT));
854      hr = ScriptStringGetOrder(ssa, order);
855      ok(hr == S_OK, "ScriptStringGetOrder failed, got %08x, expected S_OK\n", hr);
856
857      for (i = 0; i < *clip_len; i++) ok(order[i] == i, "%d: got %d expected %d\n", i, order[i], i);
858      HeapFree(GetProcessHeap(), 0, order);
859
860      hr = ScriptStringFree(&ssa);
861      ok(hr == S_OK, "ScriptStringFree should return S_OK not %08x\n", hr);
862 }
863
864 static void test_ScriptStringXtoCP_CPtoX(HDC hdc)
865 {
866 /*****************************************************************************************
867  *
868  * This test is for the ScriptStringXtoCP and ScriptStringXtoCP functions.  Due to the
869  * nature of the fonts between Windows and Wine, the test is implemented by generating
870  * values using one one function then checking the output of the second.  In this way
871  * the validity of the functions is established using Windows as a base and confirming
872  * similar behaviour in wine.
873  */
874
875     HRESULT         hr;
876     WCHAR           teststr1[] = {'T', 'e', 's', 't', 'e', '1', '2', ' ', 'a', '\0'};
877     void            *String = (WCHAR *) &teststr1;      /* ScriptStringAnalysis needs void */
878     int             String_len = (sizeof(teststr1)/sizeof(WCHAR))-1;
879     int             Glyphs = String_len * 2 + 16;       /* size of buffer as recommended  */
880     int             Charset = -1;                       /* unicode                        */
881     DWORD           Flags = SSA_GLYPHS;
882     int             ReqWidth = 100;
883     SCRIPT_CONTROL  Control;
884     SCRIPT_STATE    State;
885     SCRIPT_TABDEF   Tabdef;
886     const BYTE      InClass = 0;
887     SCRIPT_STRING_ANALYSIS ssa = NULL;
888
889     int             Ch;                                  /* Character position in string */
890     int             iTrailing;
891     int             Cp;                                  /* Character position in string */
892     int             X;
893     BOOL            fTrailing;
894
895     /* Test with hdc, this should be a valid test
896      * Here we generate an SCRIPT_STRING_ANALYSIS that will be used as input to the
897      * following character positions to X and X to character position functions.
898      */
899     hr = ScriptStringAnalyse( hdc, String, String_len, Glyphs, Charset, Flags,
900                               ReqWidth, &Control, &State, NULL, &Tabdef,
901                               &InClass, &ssa);
902     ok(hr == S_OK ||
903        hr == E_INVALIDARG, /* NT */
904        "ScriptStringAnalyse should return S_OK or E_INVALIDARG not %08x\n", hr);
905
906     if  (hr == S_OK)
907     {
908         ok(ssa != NULL, "ScriptStringAnalyse ssa should not be NULL\n");
909
910         /*
911          * Loop to generate character positions to provide starting positions for the
912          * ScriptStringCPtoX and ScriptStringXtoCP functions
913          */
914         for (Cp = 0; Cp < String_len; Cp++)
915         {
916             /* The fTrailing flag is used to indicate whether the X being returned is at
917              * the beginning or the end of the character. What happens here is that if
918              * fTrailing indicates the end of the character, ie. FALSE, then ScriptStringXtoCP
919              * returns the beginning of the next character and iTrailing is FALSE.  So for this
920              * loop iTrailing will be FALSE in both cases.
921              */
922             fTrailing = FALSE;
923             hr = ScriptStringCPtoX(ssa, Cp, fTrailing, &X);
924             ok(hr == S_OK, "ScriptStringCPtoX should return S_OK not %08x\n", hr);
925             hr = ScriptStringXtoCP(ssa, X, &Ch, &iTrailing);
926             ok(hr == S_OK, "ScriptStringXtoCP should return S_OK not %08x\n", hr);
927             ok(Cp == Ch, "ScriptStringXtoCP should return Ch = %d not %d for X = %d\n", Cp, Ch, X);
928             ok(iTrailing == FALSE, "ScriptStringXtoCP should return iTrailing = 0 not %d for X = %d\n", 
929                                   iTrailing, X);
930             fTrailing = TRUE;
931             hr = ScriptStringCPtoX(ssa, Cp, fTrailing, &X);
932             ok(hr == S_OK, "ScriptStringCPtoX should return S_OK not %08x\n", hr);
933             hr = ScriptStringXtoCP(ssa, X, &Ch, &iTrailing);
934             ok(hr == S_OK, "ScriptStringXtoCP should return S_OK not %08x\n", hr);
935
936             /*
937              * Check that character position returned by ScriptStringXtoCP in Ch matches the
938              * one input to ScriptStringCPtoX.  This means that the Cp to X position and back
939              * again works
940              */
941             ok(Cp + 1 == Ch, "ScriptStringXtoCP should return Ch = %d not %d for X = %d\n", Cp + 1, Ch, X);
942             ok(iTrailing == FALSE, "ScriptStringXtoCP should return iTrailing = 0 not %d for X = %d\n", 
943                                    iTrailing, X);
944         }
945         /*
946          * This test is to check that if the X position is just inside the trailing edge of the
947          * character then iTrailing will indicate the trailing edge, ie. TRUE
948          */
949         fTrailing = TRUE;
950         Cp = 3;
951         hr = ScriptStringCPtoX(ssa, Cp, fTrailing, &X);
952         ok(hr == S_OK, "ScriptStringCPtoX should return S_OK not %08x\n", hr);
953         X--;                                /* put X just inside the trailing edge */
954         hr = ScriptStringXtoCP(ssa, X, &Ch, &iTrailing);
955         ok(hr == S_OK, "ScriptStringXtoCP should return S_OK not %08x\n", hr);
956         ok(Cp == Ch, "ScriptStringXtoCP should return Ch = %d not %d for X = %d\n", Cp, Ch, X);
957         ok(iTrailing == TRUE, "ScriptStringXtoCP should return iTrailing = 1 not %d for X = %d\n", 
958                                   iTrailing, X);
959
960         /*
961          * This test is to check that if the X position is just outside the trailing edge of the
962          * character then iTrailing will indicate the leading edge, ie. FALSE, and Ch will indicate
963          * the next character, ie. Cp + 1 
964          */
965         fTrailing = TRUE;
966         Cp = 3;
967         hr = ScriptStringCPtoX(ssa, Cp, fTrailing, &X);
968         ok(hr == S_OK, "ScriptStringCPtoX should return S_OK not %08x\n", hr);
969         X++;                                /* put X just outside the trailing edge */
970         hr = ScriptStringXtoCP(ssa, X, &Ch, &iTrailing);
971         ok(hr == S_OK, "ScriptStringXtoCP should return S_OK not %08x\n", hr);
972         ok(Cp + 1 == Ch, "ScriptStringXtoCP should return Ch = %d not %d for X = %d\n", Cp + 1, Ch, X);
973         ok(iTrailing == FALSE, "ScriptStringXtoCP should return iTrailing = 0 not %d for X = %d\n", 
974                                   iTrailing, X);
975
976         /*
977          * This test is to check that if the X position is just outside the leading edge of the
978          * character then iTrailing will indicate the trailing edge, ie. TRUE, and Ch will indicate
979          * the next character down , ie. Cp - 1 
980          */
981         fTrailing = FALSE;
982         Cp = 3;
983         hr = ScriptStringCPtoX(ssa, Cp, fTrailing, &X);
984         ok(hr == S_OK, "ScriptStringCPtoX should return S_OK not %08x\n", hr);
985         X--;                                /* put X just outside the leading edge */
986         hr = ScriptStringXtoCP(ssa, X, &Ch, &iTrailing);
987         ok(hr == S_OK, "ScriptStringXtoCP should return S_OK not %08x\n", hr);
988         ok(Cp - 1 == Ch, "ScriptStringXtoCP should return Ch = %d not %d for X = %d\n", Cp - 1, Ch, X);
989         ok(iTrailing == TRUE, "ScriptStringXtoCP should return iTrailing = 1 not %d for X = %d\n", 
990                                   iTrailing, X);
991
992         /*
993          * Cleanup the SSA for the next round of tests
994          */
995         hr = ScriptStringFree(&ssa);
996         ok(hr == S_OK, "ScriptStringFree should return S_OK not %08x\n", hr);
997
998         /*
999          * Test to see that exceeding the number of chars returns E_INVALIDARG.  First
1000          * generate an SSA for the subsequent tests.
1001          */
1002         hr = ScriptStringAnalyse( hdc, String, String_len, Glyphs, Charset, Flags,
1003                                   ReqWidth, &Control, &State, NULL, &Tabdef,
1004                                   &InClass, &ssa);
1005         ok(hr == S_OK, "ScriptStringAnalyse should return S_OK not %08x\n", hr);
1006
1007         /*
1008          * When ScriptStringCPtoX is called with a character position Cp that exceeds the
1009          * string length, return E_INVALIDARG.  This also invalidates the ssa so a 
1010          * ScriptStringFree should also fail.
1011          */
1012         fTrailing = FALSE;
1013         Cp = String_len + 1; 
1014         hr = ScriptStringCPtoX(ssa, Cp, fTrailing, &X);
1015         ok(hr == E_INVALIDARG, "ScriptStringCPtoX should return E_INVALIDARG not %08x\n", hr);
1016
1017         hr = ScriptStringFree(&ssa);
1018         /*
1019          * ScriptStringCPtoX should free ssa, hence ScriptStringFree should fail
1020          */
1021         ok(hr == E_INVALIDARG ||
1022            hr == E_FAIL, /* win2k3 */
1023            "ScriptStringFree should return E_INVALIDARG or E_FAIL not %08x\n", hr);
1024     }
1025 }
1026
1027 static void test_ScriptCacheGetHeight(HDC hdc)
1028 {
1029     HRESULT hr;
1030     SCRIPT_CACHE sc = NULL;
1031     LONG height;
1032
1033     hr = ScriptCacheGetHeight(NULL, NULL, NULL);
1034     ok(hr == E_INVALIDARG, "expected E_INVALIDARG, got 0x%08x\n", hr);
1035
1036     hr = ScriptCacheGetHeight(NULL, &sc, NULL);
1037     ok(hr == E_INVALIDARG, "expected E_INVALIDARG, got 0x%08x\n", hr);
1038
1039     hr = ScriptCacheGetHeight(NULL, &sc, &height);
1040     ok(hr == E_PENDING, "expected E_PENDING, got 0x%08x\n", hr);
1041
1042     height = 0;
1043
1044     hr = ScriptCacheGetHeight(hdc, &sc, &height);
1045     ok(hr == S_OK, "expected S_OK, got 0x%08x\n", hr);
1046     ok(height > 0, "expected height > 0\n");
1047
1048     ScriptFreeCache(&sc);
1049 }
1050
1051 static void test_ScriptGetGlyphABCWidth(HDC hdc)
1052 {
1053     HRESULT hr;
1054     SCRIPT_CACHE sc = NULL;
1055     ABC abc;
1056
1057     hr = ScriptGetGlyphABCWidth(NULL, NULL, 'a', NULL);
1058     ok(hr == E_INVALIDARG, "expected E_INVALIDARG, got 0x%08x\n", hr);
1059
1060     hr = ScriptGetGlyphABCWidth(NULL, &sc, 'a', NULL);
1061     ok(hr == E_PENDING, "expected E_PENDING, got 0x%08x\n", hr);
1062
1063     if (0) {    /* crashes on WinXP */
1064     hr = ScriptGetGlyphABCWidth(hdc, &sc, 'a', NULL);
1065     ok(hr == E_INVALIDARG, "expected E_INVALIDARG, got 0x%08x\n", hr);
1066     }
1067
1068     hr = ScriptGetGlyphABCWidth(hdc, &sc, 'a', &abc);
1069     ok(hr == S_OK, "expected S_OK, got 0x%08x\n", hr);
1070
1071     ScriptFreeCache(&sc);
1072 }
1073
1074 static void test_ScriptLayout(void)
1075 {
1076     HRESULT hr;
1077     static const BYTE levels[][5] =
1078     {
1079         { 0, 0, 0, 0, 0 },
1080         { 1, 1, 1, 1, 1 },
1081         { 2, 2, 2, 2, 2 },
1082         { 3, 3, 3, 3, 3 },
1083     };
1084     static const int expect[][5] =
1085     {
1086         { 0, 1, 2, 3, 4 },
1087         { 4, 3, 2, 1, 0 },
1088         { 0, 1, 2, 3, 4 },
1089         { 4, 3, 2, 1, 0 }
1090     };
1091     int i, j, vistolog[sizeof(levels[0])], logtovis[sizeof(levels[0])];
1092
1093     hr = ScriptLayout(sizeof(levels[0]), NULL, vistolog, logtovis);
1094     ok(hr == E_INVALIDARG, "expected E_INVALIDARG, got 0x%08x\n", hr);
1095
1096     hr = ScriptLayout(sizeof(levels[0]), levels[0], NULL, NULL);
1097     ok(hr == E_INVALIDARG, "expected E_INVALIDARG, got 0x%08x\n", hr);
1098
1099     for (i = 0; i < sizeof(levels)/sizeof(levels[0]); i++)
1100     {
1101         hr = ScriptLayout(sizeof(levels[0]), levels[i], vistolog, logtovis);
1102         ok(hr == S_OK, "expected S_OK, got 0x%08x\n", hr);
1103
1104         for (j = 0; j < sizeof(levels[i]); j++)
1105         {
1106             ok(expect[i][j] == vistolog[j],
1107                "failure: levels[%d][%d] = %d, vistolog[%d] = %d\n",
1108                i, j, levels[i][j], j, vistolog[j] );
1109         }
1110
1111         for (j = 0; j < sizeof(levels[i]); j++)
1112         {
1113             ok(expect[i][j] == logtovis[j],
1114                "failure: levels[%d][%d] = %d, logtovis[%d] = %d\n",
1115                i, j, levels[i][j], j, logtovis[j] );
1116         }
1117     }
1118 }
1119
1120 static BOOL CALLBACK enum_proc(LGRPID group, LCID lcid, LPSTR locale, LONG_PTR lparam)
1121 {
1122     HRESULT hr;
1123     SCRIPT_DIGITSUBSTITUTE sds;
1124     SCRIPT_CONTROL sc;
1125     SCRIPT_STATE ss;
1126     LCID lcid_old;
1127
1128     if (!IsValidLocale(lcid, LCID_INSTALLED)) return TRUE;
1129
1130     memset(&sds, 0, sizeof(sds));
1131     memset(&sc, 0, sizeof(sc));
1132     memset(&ss, 0, sizeof(ss));
1133
1134     lcid_old = GetThreadLocale();
1135     if (!SetThreadLocale(lcid)) return TRUE;
1136
1137     hr = ScriptRecordDigitSubstitution(lcid, &sds);
1138     ok(hr == S_OK, "ScriptRecordDigitSubstitution failed: 0x%08x\n", hr);
1139
1140     hr = ScriptApplyDigitSubstitution(&sds, &sc, &ss);
1141     ok(hr == S_OK, "ScriptApplyDigitSubstitution failed: 0x%08x\n", hr);
1142
1143     SetThreadLocale(lcid_old);
1144     return TRUE;
1145 }
1146
1147 static void test_digit_substitution(void)
1148 {
1149     BOOL ret;
1150     unsigned int i;
1151     static const LGRPID groups[] =
1152     {
1153         LGRPID_WESTERN_EUROPE,
1154         LGRPID_CENTRAL_EUROPE,
1155         LGRPID_BALTIC,
1156         LGRPID_GREEK,
1157         LGRPID_CYRILLIC,
1158         LGRPID_TURKISH,
1159         LGRPID_JAPANESE,
1160         LGRPID_KOREAN,
1161         LGRPID_TRADITIONAL_CHINESE,
1162         LGRPID_SIMPLIFIED_CHINESE,
1163         LGRPID_THAI,
1164         LGRPID_HEBREW,
1165         LGRPID_ARABIC,
1166         LGRPID_VIETNAMESE,
1167         LGRPID_INDIC,
1168         LGRPID_GEORGIAN,
1169         LGRPID_ARMENIAN
1170     };
1171     HMODULE hKernel32;
1172     static BOOL (WINAPI * pEnumLanguageGroupLocalesA)(LANGGROUPLOCALE_ENUMPROC,LGRPID,DWORD,LONG_PTR);
1173
1174     hKernel32 = GetModuleHandleA("kernel32.dll");
1175     pEnumLanguageGroupLocalesA = (void*)GetProcAddress(hKernel32, "EnumLanguageGroupLocalesA");
1176
1177     if (!pEnumLanguageGroupLocalesA)
1178     {
1179         win_skip("EnumLanguageGroupLocalesA not available on this platform\n");
1180         return;
1181     }
1182
1183     for (i = 0; i < sizeof(groups)/sizeof(groups[0]); i++)
1184     {
1185         ret = pEnumLanguageGroupLocalesA(enum_proc, groups[i], 0, 0);
1186         if (!ret && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
1187         {
1188             win_skip("EnumLanguageGroupLocalesA not implemented on this platform\n");
1189             break;
1190         }
1191         
1192         ok(ret, "EnumLanguageGroupLocalesA failed unexpectedly: %u\n", GetLastError());
1193     }
1194 }
1195
1196 static void test_ScriptGetProperties(void)
1197 {
1198     const SCRIPT_PROPERTIES **props;
1199     HRESULT hr;
1200     int num;
1201
1202     hr = ScriptGetProperties(NULL, NULL);
1203     ok(hr == E_INVALIDARG, "ScriptGetProperties succeeded\n");
1204
1205     hr = ScriptGetProperties(NULL, &num);
1206     ok(hr == S_OK, "ScriptGetProperties failed: 0x%08x\n", hr);
1207
1208     hr = ScriptGetProperties(&props, NULL);
1209     ok(hr == S_OK, "ScriptGetProperties failed: 0x%08x\n", hr);
1210
1211     hr = ScriptGetProperties(&props, &num);
1212     ok(hr == S_OK, "ScriptGetProperties failed: 0x%08x\n", hr);
1213 }
1214
1215 static void test_ScriptBreak(void)
1216 {
1217     static const WCHAR test[] = {' ','\r','\n',0};
1218     SCRIPT_ITEM items[4];
1219     SCRIPT_LOGATTR la;
1220     HRESULT hr;
1221
1222     hr = ScriptItemize(test, 3, 4, NULL, NULL, items, NULL);
1223     ok(!hr, "ScriptItemize should return S_OK not %08x\n", hr);
1224
1225     memset(&la, 0, sizeof(la));
1226     hr = ScriptBreak(test, 1, &items[0].a, &la);
1227     ok(!hr, "ScriptBreak should return S_OK not %08x\n", hr);
1228
1229     ok(!la.fSoftBreak, "fSoftBreak set\n");
1230     ok(la.fWhiteSpace, "fWhiteSpace not set\n");
1231     ok(la.fCharStop, "fCharStop not set\n");
1232     ok(!la.fWordStop, "fWordStop set\n");
1233     ok(!la.fInvalid, "fInvalid set\n");
1234     ok(!la.fReserved, "fReserved set\n");
1235
1236     memset(&la, 0, sizeof(la));
1237     hr = ScriptBreak(test + 1, 1, &items[1].a, &la);
1238     ok(!hr, "ScriptBreak should return S_OK not %08x\n", hr);
1239
1240     ok(!la.fSoftBreak, "fSoftBreak set\n");
1241     ok(!la.fWhiteSpace, "fWhiteSpace set\n");
1242     ok(la.fCharStop, "fCharStop not set\n");
1243     ok(!la.fWordStop, "fWordStop set\n");
1244     ok(!la.fInvalid, "fInvalid set\n");
1245     ok(!la.fReserved, "fReserved set\n");
1246
1247     memset(&la, 0, sizeof(la));
1248     hr = ScriptBreak(test + 2, 1, &items[2].a, &la);
1249     ok(!hr, "ScriptBreak should return S_OK not %08x\n", hr);
1250
1251     ok(!la.fSoftBreak, "fSoftBreak set\n");
1252     ok(!la.fWhiteSpace, "fWhiteSpace set\n");
1253     ok(la.fCharStop, "fCharStop not set\n");
1254     ok(!la.fWordStop, "fWordStop set\n");
1255     ok(!la.fInvalid, "fInvalid set\n");
1256     ok(!la.fReserved, "fReserved set\n");
1257 }
1258
1259 static void test_newlines(void)
1260 {
1261     static const WCHAR test1[] = {'t','e','x','t','\r','t','e','x','t',0};
1262     static const WCHAR test2[] = {'t','e','x','t','\n','t','e','x','t',0};
1263     static const WCHAR test3[] = {'t','e','x','t','\r','\n','t','e','x','t',0};
1264     static const WCHAR test4[] = {'t','e','x','t','\n','\r','t','e','x','t',0};
1265     static const WCHAR test5[] = {'1','2','3','4','\n','\r','1','2','3','4',0};
1266     SCRIPT_ITEM items[5];
1267     HRESULT hr;
1268     int count;
1269
1270     count = 0;
1271     hr = ScriptItemize(test1, lstrlenW(test1), 5, NULL, NULL, items, &count);
1272     ok(hr == S_OK, "ScriptItemize failed: 0x%08x\n", hr);
1273     ok(count == 3, "got %d expected 3\n", count);
1274
1275     count = 0;
1276     hr = ScriptItemize(test2, lstrlenW(test2), 5, NULL, NULL, items, &count);
1277     ok(hr == S_OK, "ScriptItemize failed: 0x%08x\n", hr);
1278     ok(count == 3, "got %d expected 3\n", count);
1279
1280     count = 0;
1281     hr = ScriptItemize(test3, lstrlenW(test3), 5, NULL, NULL, items, &count);
1282     ok(hr == S_OK, "ScriptItemize failed: 0x%08x\n", hr);
1283     ok(count == 4, "got %d expected 4\n", count);
1284
1285     count = 0;
1286     hr = ScriptItemize(test4, lstrlenW(test4), 5, NULL, NULL, items, &count);
1287     ok(hr == S_OK, "ScriptItemize failed: 0x%08x\n", hr);
1288     ok(count == 4, "got %d expected 4\n", count);
1289
1290     count = 0;
1291     hr = ScriptItemize(test5, lstrlenW(test5), 5, NULL, NULL, items, &count);
1292     ok(hr == S_OK, "ScriptItemize failed: 0x%08x\n", hr);
1293     ok(count == 4, "got %d expected 4\n", count);
1294 }
1295
1296 START_TEST(usp10)
1297 {
1298     HWND            hwnd;
1299     HDC             hdc;
1300     LOGFONTA        lf;
1301     HFONT           hfont;
1302
1303     unsigned short  pwOutGlyphs[256];
1304
1305     /* We need a valid HDC to drive a lot of Script functions which requires the following    *
1306      * to set up for the tests.                                                               */
1307     hwnd = CreateWindowExA(0, "static", "", WS_POPUP, 0,0,100,100,
1308                            0, 0, 0, NULL);
1309     assert(hwnd != 0);
1310     ShowWindow(hwnd, SW_SHOW);
1311     UpdateWindow(hwnd);
1312
1313     hdc = GetDC(hwnd);                                      /* We now have a hdc             */
1314     ok( hdc != NULL, "HDC failed to be created %p\n", hdc);
1315
1316     memset(&lf, 0, sizeof(LOGFONTA));
1317     lstrcpyA(lf.lfFaceName, "Tahoma");
1318     lf.lfHeight = 10;
1319     lf.lfWeight = 3;
1320     lf.lfWidth = 10;
1321
1322     hfont = SelectObject(hdc, CreateFontIndirectA(&lf));
1323
1324     test_ScriptItemIzeShapePlace(hdc,pwOutGlyphs);
1325     test_ScriptGetCMap(hdc, pwOutGlyphs);
1326     test_ScriptCacheGetHeight(hdc);
1327     test_ScriptGetGlyphABCWidth(hdc);
1328     test_ScriptShape(hdc);
1329
1330     test_ScriptGetFontProperties(hdc);
1331     test_ScriptTextOut(hdc);
1332     test_ScriptTextOut2(hdc);
1333     test_ScriptXtoX();
1334     test_ScriptString(hdc);
1335     test_ScriptStringXtoCP_CPtoX(hdc);
1336
1337     test_ScriptLayout();
1338     test_digit_substitution();
1339     test_ScriptGetProperties();
1340     test_ScriptBreak();
1341     test_newlines();
1342
1343     ReleaseDC(hwnd, hdc);
1344     DestroyWindow(hwnd);
1345 }