gdi32: Avoid signed-unsigned integer comparisons.
[wine] / dlls / gdi32 / bidi.c
1 /*
2  * GDI BiDirectional handling
3  *
4  * Copyright 2003 Shachar Shemesh
5  * Copyright 2007 Maarten Lankhorst
6  * Copyright 2010 CodeWeavers, Aric Stewart
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21  *
22  * Code derived from the modified reference implementation
23  * that was found in revision 17 of http://unicode.org/reports/tr9/
24  * "Unicode Standard Annex #9: THE BIDIRECTIONAL ALGORITHM"
25  *
26  * -- Copyright (C) 1999-2005, ASMUS, Inc.
27  *
28  * Permission is hereby granted, free of charge, to any person obtaining a
29  * copy of the Unicode data files and any associated documentation (the
30  * "Data Files") or Unicode software and any associated documentation (the
31  * "Software") to deal in the Data Files or Software without restriction,
32  * including without limitation the rights to use, copy, modify, merge,
33  * publish, distribute, and/or sell copies of the Data Files or Software,
34  * and to permit persons to whom the Data Files or Software are furnished
35  * to do so, provided that (a) the above copyright notice(s) and this
36  * permission notice appear with all copies of the Data Files or Software,
37  * (b) both the above copyright notice(s) and this permission notice appear
38  * in associated documentation, and (c) there is clear notice in each
39  * modified Data File or in the Software as well as in the documentation
40  * associated with the Data File(s) or Software that the data or software
41  * has been modified.
42  */
43
44 #include "config.h"
45
46 #include <stdarg.h>
47 #include "windef.h"
48 #include "winbase.h"
49 #include "wingdi.h"
50 #include "winnls.h"
51 #include "usp10.h"
52 #include "wine/unicode.h"
53 #include "wine/debug.h"
54 #include "gdi_private.h"
55
56 WINE_DEFAULT_DEBUG_CHANNEL(bidi);
57
58 /* HELPER FUNCTIONS AND DECLARATIONS */
59
60 #define odd(x) ((x) & 1)
61
62 /*------------------------------------------------------------------------
63     Bidirectional Character Types
64
65     as defined by the Unicode Bidirectional Algorithm Table 3-7.
66
67     Note:
68
69       The list of bidirectional character types here is not grouped the
70       same way as the table 3-7, since the numberic values for the types
71       are chosen to keep the state and action tables compact.
72 ------------------------------------------------------------------------*/
73 enum directions
74 {
75     /* input types */
76              /* ON MUST be zero, code relies on ON = N = 0 */
77     ON = 0,  /* Other Neutral */
78     L,       /* Left Letter */
79     R,       /* Right Letter */
80     AN,      /* Arabic Number */
81     EN,      /* European Number */
82     AL,      /* Arabic Letter (Right-to-left) */
83     NSM,     /* Non-spacing Mark */
84     CS,      /* Common Separator */
85     ES,      /* European Separator */
86     ET,      /* European Terminator (post/prefix e.g. $ and %) */
87
88     /* resolved types */
89     BN,      /* Boundary neutral (type of RLE etc after explicit levels) */
90
91     /* input types, */
92     S,       /* Segment Separator (TAB)        // used only in L1 */
93     WS,      /* White space                    // used only in L1 */
94     B,       /* Paragraph Separator (aka as PS) */
95
96     /* types for explicit controls */
97     RLO,     /* these are used only in X1-X9 */
98     RLE,
99     LRO,
100     LRE,
101     PDF,
102
103     /* resolved types, also resolved directions */
104     N = ON,  /* alias, where ON, WS and S are treated the same */
105 };
106
107 /* HELPER FUNCTIONS */
108
109 /* Convert the libwine information to the direction enum */
110 static void classify(LPCWSTR lpString, WORD *chartype, DWORD uCount)
111 {
112     static const enum directions dir_map[16] =
113     {
114         L,  /* unassigned defaults to L */
115         L,
116         R,
117         EN,
118         ES,
119         ET,
120         AN,
121         CS,
122         B,
123         S,
124         WS,
125         ON,
126         AL,
127         NSM,
128         BN,
129         PDF  /* also LRE, LRO, RLE, RLO */
130     };
131
132     unsigned i;
133
134     for (i = 0; i < uCount; ++i)
135     {
136         chartype[i] = dir_map[get_char_typeW(lpString[i]) >> 12];
137         if (chartype[i] == PDF)
138         {
139             switch (lpString[i])
140             {
141             case 0x202A: chartype[i] = LRE; break;
142             case 0x202B: chartype[i] = RLE; break;
143             case 0x202C: chartype[i] = PDF; break;
144             case 0x202D: chartype[i] = LRO; break;
145             case 0x202E: chartype[i] = RLO; break;
146             }
147         }
148     }
149 }
150
151 /* Set a run of cval values at locations all prior to, but not including */
152 /* iStart, to the new value nval. */
153 static void SetDeferredRun(BYTE *pval, int cval, int iStart, int nval)
154 {
155     int i = iStart - 1;
156     for (; i >= iStart - cval; i--)
157     {
158         pval[i] = nval;
159     }
160 }
161
162 /* THE PARAGRAPH LEVEL */
163
164 /*------------------------------------------------------------------------
165     Function: resolveParagraphs
166
167     Resolves the input strings into blocks over which the algorithm
168     is then applied.
169
170     Implements Rule P1 of the Unicode Bidi Algorithm
171
172     Input: Text string
173            Character count
174
175     Output: revised character count
176
177     Note:    This is a very simplistic function. In effect it restricts
178             the action of the algorithm to the first paragraph in the input
179             where a paragraph ends at the end of the first block separator
180             or at the end of the input text.
181
182 ------------------------------------------------------------------------*/
183
184 static int resolveParagraphs(WORD *types, int cch)
185 {
186     /* skip characters not of type B */
187     int ich = 0;
188     for(; ich < cch && types[ich] != B; ich++);
189     /* stop after first B, make it a BN for use in the next steps */
190     if (ich < cch && types[ich] == B)
191         types[ich++] = BN;
192     return ich;
193 }
194
195 /* REORDER */
196 /*------------------------------------------------------------------------
197     Function: resolveLines
198
199     Breaks a paragraph into lines
200
201     Input:  Array of line break flags
202             Character count
203     In/Out: Array of characters
204
205     Returns the count of characters on the first line
206
207     Note: This function only breaks lines at hard line breaks. Other
208     line breaks can be passed in. If pbrk[n] is TRUE, then a break
209     occurs after the character in pszInput[n]. Breaks before the first
210     character are not allowed.
211 ------------------------------------------------------------------------*/
212 static int resolveLines(LPCWSTR pszInput, const BOOL * pbrk, int cch)
213 {
214     /* skip characters not of type LS */
215     int ich = 0;
216     for(; ich < cch; ich++)
217     {
218         if (pszInput[ich] == (WCHAR)'\n' || (pbrk && pbrk[ich]))
219         {
220             ich++;
221             break;
222         }
223     }
224
225     return ich;
226 }
227
228 /*------------------------------------------------------------------------
229     Function: resolveWhiteSpace
230
231     Resolves levels for WS and S
232     Implements rule L1 of the Unicode bidi Algorithm.
233
234     Input:  Base embedding level
235             Character count
236             Array of direction classes (for one line of text)
237
238     In/Out: Array of embedding levels (for one line of text)
239
240     Note: this should be applied a line at a time. The default driver
241           code supplied in this file assumes a single line of text; for
242           a real implementation, cch and the initial pointer values
243           would have to be adjusted.
244 ------------------------------------------------------------------------*/
245 static void resolveWhitespace(int baselevel, const WORD *pcls, BYTE *plevel, int cch)
246 {
247     int cchrun = 0;
248     BYTE oldlevel = baselevel;
249
250     int ich = 0;
251     for (; ich < cch; ich++)
252     {
253         switch(pcls[ich])
254         {
255         default:
256             cchrun = 0; /* any other character breaks the run */
257             break;
258         case WS:
259             cchrun++;
260             break;
261
262         case RLE:
263         case LRE:
264         case LRO:
265         case RLO:
266         case PDF:
267         case BN:
268             plevel[ich] = oldlevel;
269             cchrun++;
270             break;
271
272         case S:
273         case B:
274             /* reset levels for WS before eot */
275             SetDeferredRun(plevel, cchrun, ich, baselevel);
276             cchrun = 0;
277             plevel[ich] = baselevel;
278             break;
279         }
280         oldlevel = plevel[ich];
281     }
282     /* reset level before eot */
283     SetDeferredRun(plevel, cchrun, ich, baselevel);
284 }
285
286 /*------------------------------------------------------------------------
287     Function: BidiLines
288
289     Implements the Line-by-Line phases of the Unicode Bidi Algorithm
290
291       Input:     Count of characters
292                  Array of character directions
293
294     Inp/Out: Input text
295              Array of levels
296
297 ------------------------------------------------------------------------*/
298 static void BidiLines(int baselevel, LPWSTR pszOutLine, LPCWSTR pszLine, const WORD * pclsLine,
299                       BYTE * plevelLine, int cchPara, const BOOL * pbrk)
300 {
301     int cchLine = 0;
302     int done = 0;
303     int *run;
304
305     run = HeapAlloc(GetProcessHeap(), 0, cchPara * sizeof(int));
306     if (!run)
307     {
308         WARN("Out of memory\n");
309         return;
310     }
311
312     do
313     {
314         /* break lines at LS */
315         cchLine = resolveLines(pszLine, pbrk, cchPara);
316
317         /* resolve whitespace */
318         resolveWhitespace(baselevel, pclsLine, plevelLine, cchLine);
319
320         if (pszOutLine)
321         {
322             int i;
323             /* reorder each line in place */
324             ScriptLayout(cchLine, plevelLine, NULL, run);
325             for (i = 0; i < cchLine; i++)
326                 pszOutLine[done+run[i]] = pszLine[i];
327         }
328
329         pszLine += cchLine;
330         plevelLine += cchLine;
331         pbrk += pbrk ? cchLine : 0;
332         pclsLine += cchLine;
333         cchPara -= cchLine;
334         done += cchLine;
335
336     } while (cchPara);
337
338     HeapFree(GetProcessHeap(), 0, run);
339 }
340
341 /*************************************************************
342  *    BIDI_Reorder
343  *
344  *     Returns TRUE if reordering was required and done.
345  */
346 BOOL BIDI_Reorder(
347                 HDC hDC,        /*[in] Display DC */
348                 LPCWSTR lpString,       /* [in] The string for which information is to be returned */
349                 INT uCount,     /* [in] Number of WCHARs in string. */
350                 DWORD dwFlags,  /* [in] GetCharacterPlacement compatible flags specifying how to process the string */
351                 DWORD dwWineGCP_Flags,       /* [in] Wine internal flags - Force paragraph direction */
352                 LPWSTR lpOutString, /* [out] Reordered string */
353                 INT uCountOut,  /* [in] Size of output buffer */
354                 UINT *lpOrder, /* [out] Logical -> Visual order map */
355                 WORD **lpGlyphs, /* [out] reordered, mirrored, shaped glyphs to display */
356                 INT *cGlyphs /* [out] number of glyphs generated */
357     )
358 {
359     WORD *chartype;
360     BYTE *levels;
361     INT i, done;
362     unsigned glyph_i;
363     BOOL is_complex;
364
365     int maxItems;
366     int nItems;
367     SCRIPT_CONTROL Control;
368     SCRIPT_STATE State;
369     SCRIPT_ITEM *pItems;
370     HRESULT res;
371     SCRIPT_CACHE psc = NULL;
372     WORD *run_glyphs = NULL;
373     WORD *pwLogClust = NULL;
374     SCRIPT_VISATTR *psva = NULL;
375     DWORD cMaxGlyphs = 0;
376     BOOL  doGlyphs = TRUE;
377
378     TRACE("%s, %d, 0x%08x lpOutString=%p, lpOrder=%p\n",
379           debugstr_wn(lpString, uCount), uCount, dwFlags,
380           lpOutString, lpOrder);
381
382     memset(&Control, 0, sizeof(Control));
383     memset(&State, 0, sizeof(State));
384     if (lpGlyphs)
385         *lpGlyphs = NULL;
386
387     if (!(dwFlags & GCP_REORDER))
388     {
389         FIXME("Asked to reorder without reorder flag set\n");
390         return FALSE;
391     }
392
393     if (lpOutString && uCountOut < uCount)
394     {
395         FIXME("lpOutString too small\n");
396         return FALSE;
397     }
398
399     chartype = HeapAlloc(GetProcessHeap(), 0, uCount * sizeof(WORD));
400     if (!chartype)
401     {
402         WARN("Out of memory\n");
403         return FALSE;
404     }
405
406     if (lpOutString)
407         memcpy(lpOutString, lpString, uCount * sizeof(WCHAR));
408
409     is_complex = FALSE;
410     for (i = 0; i < uCount && !is_complex; i++)
411     {
412         if ((lpString[i] >= 0x900 && lpString[i] <= 0xfff) ||
413             (lpString[i] >= 0x1cd0 && lpString[i] <= 0x1cff) ||
414             (lpString[i] >= 0xa840 && lpString[i] <= 0xa8ff))
415             is_complex = TRUE;
416     }
417
418     /* Verify reordering will be required */
419     if ((WINE_GCPW_FORCE_RTL == (dwWineGCP_Flags&WINE_GCPW_DIR_MASK)) ||
420         ((dwWineGCP_Flags&WINE_GCPW_DIR_MASK) == WINE_GCPW_LOOSE_RTL))
421         State.uBidiLevel = 1;
422     else if (!is_complex)
423     {
424         done = 1;
425         classify(lpString, chartype, uCount);
426         for (i = 0; i < uCount; i++)
427             switch (chartype[i])
428             {
429                 case R:
430                 case AL:
431                 case RLE:
432                 case RLO:
433                     done = 0;
434                     break;
435             }
436         if (done)
437         {
438             HeapFree(GetProcessHeap(), 0, chartype);
439             if (lpOrder)
440             {
441                 for (i = 0; i < uCount; i++)
442                     lpOrder[i] = i;
443             }
444             return TRUE;
445         }
446     }
447
448     levels = HeapAlloc(GetProcessHeap(), 0, uCount * sizeof(BYTE));
449     if (!levels)
450     {
451         WARN("Out of memory\n");
452         HeapFree(GetProcessHeap(), 0, chartype);
453         return FALSE;
454     }
455
456     maxItems = 5;
457     pItems = HeapAlloc(GetProcessHeap(),0, maxItems * sizeof(SCRIPT_ITEM));
458     if (!pItems)
459     {
460         WARN("Out of memory\n");
461         HeapFree(GetProcessHeap(), 0, chartype);
462         HeapFree(GetProcessHeap(), 0, levels);
463         return FALSE;
464     }
465
466     if (lpGlyphs)
467     {
468         cMaxGlyphs = 1.5 * uCount + 16;
469         run_glyphs = HeapAlloc(GetProcessHeap(),0,sizeof(WORD) * cMaxGlyphs);
470         if (!run_glyphs)
471         {
472             WARN("Out of memory\n");
473             HeapFree(GetProcessHeap(), 0, chartype);
474             HeapFree(GetProcessHeap(), 0, levels);
475             HeapFree(GetProcessHeap(), 0, pItems);
476             return FALSE;
477         }
478         pwLogClust = HeapAlloc(GetProcessHeap(),0,sizeof(WORD) * uCount);
479         if (!pwLogClust)
480         {
481             WARN("Out of memory\n");
482             HeapFree(GetProcessHeap(), 0, chartype);
483             HeapFree(GetProcessHeap(), 0, levels);
484             HeapFree(GetProcessHeap(), 0, pItems);
485             HeapFree(GetProcessHeap(), 0, run_glyphs);
486             return FALSE;
487         }
488         psva = HeapAlloc(GetProcessHeap(),0,sizeof(SCRIPT_VISATTR) * uCount);
489         if (!psva)
490         {
491             WARN("Out of memory\n");
492             HeapFree(GetProcessHeap(), 0, chartype);
493             HeapFree(GetProcessHeap(), 0, levels);
494             HeapFree(GetProcessHeap(), 0, pItems);
495             HeapFree(GetProcessHeap(), 0, run_glyphs);
496             HeapFree(GetProcessHeap(), 0, pwLogClust);
497             return FALSE;
498         }
499     }
500
501     done = 0;
502     glyph_i = 0;
503     while (done < uCount)
504     {
505         INT j;
506         classify(lpString + done, chartype, uCount - done);
507         /* limit text to first block */
508         i = resolveParagraphs(chartype, uCount - done);
509         for (j = 0; j < i; ++j)
510             switch(chartype[j])
511             {
512                 case B:
513                 case S:
514                 case WS:
515                 case ON: chartype[j] = N;
516                 default: continue;
517             }
518
519         if ((dwWineGCP_Flags&WINE_GCPW_DIR_MASK) == WINE_GCPW_LOOSE_RTL)
520             State.uBidiLevel = 1;
521         else if ((dwWineGCP_Flags&WINE_GCPW_DIR_MASK) == WINE_GCPW_LOOSE_LTR)
522             State.uBidiLevel = 0;
523
524         if (dwWineGCP_Flags & WINE_GCPW_LOOSE_MASK)
525         {
526             for (j = 0; j < i; ++j)
527                 if (chartype[j] == L)
528                 {
529                     State.uBidiLevel = 0;
530                     break;
531                 }
532                 else if (chartype[j] == R || chartype[j] == AL)
533                 {
534                     State.uBidiLevel = 1;
535                     break;
536                 }
537         }
538
539         res = ScriptItemize(lpString + done, i, maxItems, &Control, &State, pItems, &nItems);
540         while (res == E_OUTOFMEMORY)
541         {
542             maxItems = maxItems * 2;
543             pItems = HeapReAlloc(GetProcessHeap(), 0, pItems, sizeof(SCRIPT_ITEM) * maxItems);
544             if (!pItems)
545             {
546                 WARN("Out of memory\n");
547                 HeapFree(GetProcessHeap(), 0, chartype);
548                 HeapFree(GetProcessHeap(), 0, levels);
549                 HeapFree(GetProcessHeap(), 0, run_glyphs);
550                 HeapFree(GetProcessHeap(), 0, pwLogClust);
551                 HeapFree(GetProcessHeap(), 0, psva);
552                 return FALSE;
553             }
554             res = ScriptItemize(lpString + done, i, maxItems, &Control, &State, pItems, &nItems);
555         }
556
557         if (lpOutString || lpOrder)
558             for (j = 0; j < nItems; j++)
559             {
560                 int k;
561                 for (k = pItems[j].iCharPos; k < pItems[j+1].iCharPos; k++)
562                     levels[k] = pItems[j].a.s.uBidiLevel;
563             }
564
565         if (lpOutString)
566         {
567             /* assign directional types again, but for WS, S this time */
568             classify(lpString + done, chartype, i);
569
570             BidiLines(State.uBidiLevel, lpOutString + done, lpString + done,
571                         chartype, levels, i, 0);
572         }
573
574         if (lpOrder)
575         {
576             int k, lastgood;
577             for (j = lastgood = 0; j < i; ++j)
578                 if (levels[j] != levels[lastgood])
579                 {
580                     --j;
581                     if (odd(levels[lastgood]))
582                         for (k = j; k >= lastgood; --k)
583                             lpOrder[done + k] = done + j - k;
584                     else
585                         for (k = lastgood; k <= j; ++k)
586                             lpOrder[done + k] = done + k;
587                     lastgood = ++j;
588                 }
589             if (odd(levels[lastgood]))
590                 for (k = j - 1; k >= lastgood; --k)
591                     lpOrder[done + k] = done + j - 1 - k;
592             else
593                 for (k = lastgood; k < j; ++k)
594                     lpOrder[done + k] = done + k;
595         }
596
597         if (lpGlyphs && doGlyphs)
598         {
599             BYTE runOrder[maxItems];
600             int visOrder[maxItems];
601             SCRIPT_ITEM *curItem;
602
603             for (j = 0; j < nItems; j++)
604                 runOrder[j] = pItems[j].a.s.uBidiLevel;
605
606             ScriptLayout(nItems, runOrder, visOrder, NULL);
607
608             for (j = 0; j < nItems; j++)
609             {
610                 int k;
611                 int cChars,cOutGlyphs;
612                 curItem = &pItems[visOrder[j]];
613
614                 cChars = pItems[visOrder[j]+1].iCharPos - curItem->iCharPos;
615
616                 res = ScriptShape(hDC, &psc, lpString + done + curItem->iCharPos, cChars, cMaxGlyphs, &curItem->a, run_glyphs, pwLogClust, psva, &cOutGlyphs);
617                 while (res == E_OUTOFMEMORY)
618                 {
619                     cMaxGlyphs *= 2;
620                     run_glyphs = HeapReAlloc(GetProcessHeap(), 0, run_glyphs, sizeof(WORD) * cMaxGlyphs);
621                     if (!run_glyphs)
622                     {
623                         WARN("Out of memory\n");
624                         HeapFree(GetProcessHeap(), 0, chartype);
625                         HeapFree(GetProcessHeap(), 0, levels);
626                         HeapFree(GetProcessHeap(), 0, pItems);
627                         HeapFree(GetProcessHeap(), 0, psva);
628                         HeapFree(GetProcessHeap(), 0, pwLogClust);
629                         HeapFree(GetProcessHeap(), 0, *lpGlyphs);
630                         ScriptFreeCache(&psc);
631                         *lpGlyphs = NULL;
632                         return FALSE;
633                     }
634                     res = ScriptShape(hDC, &psc, lpString + done + curItem->iCharPos, cChars, cMaxGlyphs, &curItem->a, run_glyphs, pwLogClust, psva, &cOutGlyphs);
635                 }
636                 if (res)
637                 {
638                     if (res == USP_E_SCRIPT_NOT_IN_FONT)
639                         TRACE("Unable to shape with currently selected font\n");
640                     else
641                         FIXME("Unable to shape string (%x)\n",res);
642                     j = nItems;
643                     doGlyphs = FALSE;
644                     HeapFree(GetProcessHeap(), 0, *lpGlyphs);
645                     *lpGlyphs = NULL;
646                 }
647                 else
648                 {
649                     if (*lpGlyphs)
650                         *lpGlyphs = HeapReAlloc(GetProcessHeap(), 0, *lpGlyphs, sizeof(WORD) * (glyph_i + cOutGlyphs));
651                    else
652                         *lpGlyphs = HeapAlloc(GetProcessHeap(), 0, sizeof(WORD) * (glyph_i + cOutGlyphs));
653                     for (k = 0; k < cOutGlyphs; k++)
654                         (*lpGlyphs)[glyph_i+k] = run_glyphs[k];
655                     glyph_i += cOutGlyphs;
656                 }
657             }
658         }
659
660         done += i;
661     }
662     if (cGlyphs)
663         *cGlyphs = glyph_i;
664
665     HeapFree(GetProcessHeap(), 0, chartype);
666     HeapFree(GetProcessHeap(), 0, levels);
667     HeapFree(GetProcessHeap(), 0, pItems);
668     HeapFree(GetProcessHeap(), 0, run_glyphs);
669     HeapFree(GetProcessHeap(), 0, pwLogClust);
670     HeapFree(GetProcessHeap(), 0, psva);
671     ScriptFreeCache(&psc);
672     return TRUE;
673 }