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