xref: /reactos/win32ss/user/rtl/text.c (revision 23373acb)
1 /*
2  *  ReactOS kernel
3  *  Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19 /*
20  * PROJECT:         ReactOS user32.dll
21  * FILE:            win32ss/user/rtl/text.c
22  * PURPOSE:         Draw Text
23  * PROGRAMMER:      Casper S. Hornstrup (chorns@users.sourceforge.net)
24  * UPDATE HISTORY:
25  *      09-05-2001  CSH  Created
26  */
27 
28 /* INCLUDES ******************************************************************/
29 
30 #ifdef _WIN32K_
31 #include <win32k.h>
32 DBG_DEFAULT_CHANNEL(UserMenu);
33 #else
34 #include <user32.h>
35 #include <wine/debug.h>
36 WINE_DEFAULT_DEBUG_CHANNEL(text);
37 #endif
38 
39 /* FUNCTIONS *****************************************************************/
40 
41 #ifndef NDEBUG
42 
43 #ifdef assert
44 #undef assert
45 #endif
46 
47 #define assert(e) ((e) ? (void)0 : _font_assert(#e, __FILE__, __LINE__))
48 
49 #else
50 #include <assert.h>
51 
52 #endif
53 
54 void _font_assert(const char *msg, const char *file, int line)
55 {
56   /* Assertion failed at foo.c line 45: x<y */
57   DbgPrint("Assertion failed at %s line %d: %s\n", file, line, msg);
58 #ifdef _WIN32K_
59   ASSERT(FALSE);
60 #else
61   ExitProcess(3);
62   for(;;); /* eliminate warning by mingw */
63 #endif
64 }
65 
66 /*********************************************************************
67  *
68  *            DrawText functions
69  *
70  * Copied from Wine.
71  *   Copyright 1993, 1994 Alexandre Julliard
72  *   Copyright 2002 Bill Medland
73  *
74  * Design issues
75  *   How many buffers to use
76  *     While processing in DrawText there are potentially three different forms
77  *     of the text that need to be held.  How are they best held?
78  *     1. The original text is needed, of course, to see what to display.
79  *     2. The text that will be returned to the user if the DT_MODIFYSTRING is
80  *        in effect.
81  *     3. The buffered text that is about to be displayed e.g. the current line.
82  *        Typically this will exclude the ampersands used for prefixing etc.
83  *
84  *     Complications.
85  *     a. If the buffered text to be displayed includes the ampersands then
86  *        we will need special measurement and draw functions that will ignore
87  *        the ampersands (e.g. by copying to a buffer without the prefix and
88  *        then using the normal forms).  This may involve less space but may
89  *        require more processing.  e.g. since a line containing tabs may
90  *        contain several underlined characters either we need to carry around
91  *        a list of prefix locations or we may need to locate them several
92  *        times.
93  *     b. If we actually directly modify the "original text" as we go then we
94  *        will need some special "caching" to handle the fact that when we
95  *        ellipsify the text the ellipsis may modify the next line of text,
96  *        which we have not yet processed.  (e.g. ellipsification of a W at the
97  *        end of a line will overwrite the W, the \n and the first character of
98  *        the next line, and a \0 will overwrite the second.  Try it!!)
99  *
100  *     Option 1.  Three separate storages. (To be implemented)
101  *       If DT_MODIFYSTRING is in effect then allocate an extra buffer to hold
102  *       the edited string in some form, either as the string itself or as some
103  *       sort of "edit list" to be applied just before returning.
104  *       Use a buffer that holds the ellipsified current line sans ampersands
105  *       and accept the need occasionally to recalculate the prefixes (if
106  *       DT_EXPANDTABS and not DT_NOPREFIX and not DT_HIDEPREFIX)
107  */
108 
109 #define TAB     9
110 #define LF     10
111 #define CR     13
112 #define SPACE  32
113 #define PREFIX 38
114 #define ALPHA_PREFIX 30 /* Win16: Alphabet prefix */
115 #define KANA_PREFIX  31 /* Win16: Katakana prefix */
116 
117 #define FORWARD_SLASH '/'
118 #define BACK_SLASH '\\'
119 
120 static const WCHAR ELLIPSISW[] = {'.','.','.', 0};
121 
122 typedef struct tag_ellipsis_data
123 {
124     int before;
125     int len;
126     int under;
127     int after;
128 } ellipsis_data;
129 
130 /*********************************************************************
131  *                      TEXT_Ellipsify (static)
132  *
133  *  Add an ellipsis to the end of the given string whilst ensuring it fits.
134  *
135  * If the ellipsis alone doesn't fit then it will be returned anyway.
136  *
137  * See Also TEXT_PathEllipsify
138  *
139  * Arguments
140  *   hdc        [in] The handle to the DC that defines the font.
141  *   str        [in/out] The string that needs to be modified.
142  *   max_str    [in] The dimension of str (number of WCHAR).
143  *   len_str    [in/out] The number of characters in str
144  *   width      [in] The maximum width permitted (in logical coordinates)
145  *   size       [out] The dimensions of the text
146  *   modstr     [out] The modified form of the string, to be returned to the
147  *                    calling program.  It is assumed that the caller has
148  *                    made sufficient space available so we don't need to
149  *                    know the size of the space.  This pointer may be NULL if
150  *                    the modified string is not required.
151  *   len_before [out] The number of characters before the ellipsis.
152  *   len_ellip  [out] The number of characters in the ellipsis.
153  *
154  * See for example Microsoft article Q249678.
155  *
156  * For now we will simply use three dots rather than worrying about whether
157  * the font contains an explicit ellipsis character.
158  */
159 static void TEXT_Ellipsify (HDC hdc, WCHAR *str, unsigned int max_len,
160                             unsigned int *len_str, int width, SIZE *size,
161                             WCHAR *modstr,
162                             int *len_before, int *len_ellip)
163 {
164     unsigned int len_ellipsis;
165     unsigned int lo, mid, hi;
166     len_ellipsis = strlenW (ELLIPSISW);
167     if (len_ellipsis > max_len) len_ellipsis = max_len;
168     if (*len_str > max_len - len_ellipsis)
169         *len_str = max_len - len_ellipsis;
170 
171     /* First do a quick binary search to get an upper bound for *len_str. */
172     if (*len_str > 0 &&
173 #ifdef _WIN32K_
174         GreGetTextExtentExW(hdc, str, *len_str, width, NULL, NULL, size, 0) &&
175 #else
176         GetTextExtentExPointW(hdc, str, *len_str, width, NULL, NULL, size) &&
177 #endif
178         size->cx > width)
179     {
180         for (lo = 0, hi = *len_str; lo < hi; )
181         {
182             mid = (lo + hi) / 2;
183 #ifdef _WIN32K_
184             if (!GreGetTextExtentExW(hdc, str, mid, width, NULL, NULL, size, 0))
185 #else
186             if (!GetTextExtentExPointW(hdc, str, mid, width, NULL, NULL, size))
187 #endif
188                 break;
189             if (size->cx > width)
190                 hi = mid;
191             else
192                 lo = mid + 1;
193         }
194         *len_str = hi;
195     }
196     /* Now this should take only a couple iterations at most. */
197     for ( ; ; )
198     {
199         memcpy(str + *len_str, ELLIPSISW, len_ellipsis*sizeof(WCHAR));
200 #ifdef _WIN32K_
201         if (!GreGetTextExtentExW (hdc, str, *len_str + len_ellipsis, width,
202                                     NULL, NULL, size, 0)) break;
203 #else
204         if (!GetTextExtentExPointW (hdc, str, *len_str + len_ellipsis, width,
205                                     NULL, NULL, size)) break;
206 #endif
207         if (!*len_str || size->cx <= width) break;
208 
209         (*len_str)--;
210     }
211     *len_ellip = len_ellipsis;
212     *len_before = *len_str;
213     *len_str += len_ellipsis;
214 
215     if (modstr)
216     {
217         memcpy (modstr, str, *len_str * sizeof(WCHAR));
218         modstr[*len_str] = '\0';
219     }
220 }
221 
222 /*********************************************************************
223  *                      TEXT_PathEllipsify (static)
224  *
225  * Add an ellipsis to the provided string in order to make it fit within
226  * the width.  The ellipsis is added as specified for the DT_PATH_ELLIPSIS
227  * flag.
228  *
229  * See Also TEXT_Ellipsify
230  *
231  * Arguments
232  *   hdc        [in] The handle to the DC that defines the font.
233  *   str        [in/out] The string that needs to be modified
234  *   max_str    [in] The dimension of str (number of WCHAR).
235  *   len_str    [in/out] The number of characters in str
236  *   width      [in] The maximum width permitted (in logical coordinates)
237  *   size       [out] The dimensions of the text
238  *   modstr     [out] The modified form of the string, to be returned to the
239  *                    calling program.  It is assumed that the caller has
240  *                    made sufficient space available so we don't need to
241  *                    know the size of the space.  This pointer may be NULL if
242  *                    the modified string is not required.
243  *   pellip     [out] The ellipsification results
244  *
245  * For now we will simply use three dots rather than worrying about whether
246  * the font contains an explicit ellipsis character.
247  *
248  * The following applies, I think to Win95.  We will need to extend it for
249  * Win98 which can have both path and end ellipsis at the same time (e.g.
250  *  C:\MyLongFileName.Txt becomes ...\MyLongFileN...)
251  *
252  * The resulting string consists of as much as possible of the following:
253  * 1. The ellipsis itself
254  * 2. The last \ or / of the string (if any)
255  * 3. Everything after the last \ or / of the string (if any) or the whole
256  *    string if there is no / or \.  I believe that under Win95 this would
257  *    include everything even though some might be clipped off the end whereas
258  *    under Win98 that might be ellipsified too.
259  *    Yet to be investigated is whether this would include wordbreaking if the
260  *    filename is more than 1 word and splitting if DT_EDITCONTROL was in
261  *    effect.  (If DT_EDITCONTROL is in effect then on occasions text will be
262  *    broken within words).
263  * 4. All the stuff before the / or \, which is placed before the ellipsis.
264  */
265 static void TEXT_PathEllipsify (HDC hdc, WCHAR *str, unsigned int max_len,
266                                 unsigned int *len_str, int width, SIZE *size,
267                                 WCHAR *modstr, ellipsis_data *pellip)
268 {
269     int len_ellipsis;
270     int len_trailing;
271     int len_under;
272     WCHAR *lastBkSlash, *lastFwdSlash, *lastSlash;
273     len_ellipsis = strlenW (ELLIPSISW);
274     if (!max_len) return;
275     if (len_ellipsis >= max_len) len_ellipsis = max_len - 1;
276     if (*len_str + len_ellipsis >= max_len)
277         *len_str = max_len - len_ellipsis-1;
278         /* Hopefully this will never happen, otherwise it would probably lose
279          * the wrong character
280          */
281     str[*len_str] = '\0'; /* to simplify things */
282 #ifdef _WIN32K_
283     lastBkSlash  = wcsrchr (str, BACK_SLASH);
284     lastFwdSlash = wcsrchr (str, FORWARD_SLASH);
285 #else
286     lastBkSlash  = strrchrW (str, BACK_SLASH);
287     lastFwdSlash = strrchrW (str, FORWARD_SLASH);
288 #endif
289     lastSlash = lastBkSlash > lastFwdSlash ? lastBkSlash : lastFwdSlash;
290     if (!lastSlash) lastSlash = str;
291     len_trailing = *len_str - (lastSlash - str);
292 
293     /* overlap-safe movement to the right */
294     memmove (lastSlash+len_ellipsis, lastSlash, len_trailing * sizeof(WCHAR));
295     memcpy (lastSlash, ELLIPSISW, len_ellipsis*sizeof(WCHAR));
296     len_trailing += len_ellipsis;
297     /* From this point on lastSlash actually points to the ellipsis in front
298      * of the last slash and len_trailing includes the ellipsis
299      */
300 
301     len_under = 0;
302     for ( ; ; )
303     {
304 #ifdef _WIN32K_
305         if (!GreGetTextExtentExW (hdc, str, *len_str + len_ellipsis, width,
306                                     NULL, NULL, size, 0)) break;
307 #else
308         if (!GetTextExtentExPointW (hdc, str, *len_str + len_ellipsis, width,
309                                     NULL, NULL, size)) break;
310 #endif
311         if (lastSlash == str || size->cx <= width) break;
312 
313         /* overlap-safe movement to the left */
314         memmove (lastSlash-1, lastSlash, len_trailing * sizeof(WCHAR));
315         lastSlash--;
316         len_under++;
317 
318         assert (*len_str);
319         (*len_str)--;
320     }
321     pellip->before = lastSlash-str;
322     pellip->len = len_ellipsis;
323     pellip->under = len_under;
324     pellip->after = len_trailing - len_ellipsis;
325     *len_str += len_ellipsis;
326 
327     if (modstr)
328     {
329         memcpy(modstr, str, *len_str * sizeof(WCHAR));
330         modstr[*len_str] = '\0';
331     }
332 }
333 
334 /* Check the character is Chinese, Japanese, Korean and/or Thai */
335 inline BOOL IsCJKT(WCHAR wch)
336 {
337     if (0x0E00 <= wch && wch <= 0x0E7F)
338         return TRUE;    /* Thai */
339 
340     if (0x3000 <= wch && wch <= 0x9FFF)
341         return TRUE;    /* CJK */
342 
343     if (0xAC00 <= wch && wch <= 0xD7FF)
344         return TRUE;    /* Korean */
345 
346     if (0xFF00 <= wch && wch <= 0xFFEF)
347         return TRUE;    /* CJK */
348 
349     return FALSE;
350 }
351 
352 /* See http://en.wikipedia.org/wiki/Kinsoku_shori */
353 static const WCHAR KinsokuClassA[] =
354 {
355     0x2010, 0x2013, 0x2019, 0x201D, 0x203C, 0x2047, 0x2048, 0x2049, 0x3001,
356     0x3002, 0x3005, 0x3009, 0x300B, 0x300D, 0x300F, 0x3011, 0x3015, 0x3017,
357     0x3019, 0x301C, 0x301F, 0x303B, 0x3041, 0x3043, 0x3045, 0x3047, 0x3049,
358     0x3063, 0x3083, 0x3085, 0x3087, 0x308E, 0x3095, 0x3096, 0x30A0, 0x30A1,
359     0x30A3, 0x30A5, 0x30A7, 0x30A9, 0x30C3, 0x30E3, 0x30E5, 0x30E7, 0x30EE,
360     0x30F5, 0x30F6, 0x30FB, 0x30FC, 0x30FD, 0x30FE, 0x31F0, 0x31F1, 0x31F2,
361     0x31F3, 0x31F4, 0x31F5, 0x31F6, 0x31F7, 0x31F8, 0x31F9, 0x31FA, 0x31FB,
362     0x31FC, 0x31FD, 0x31FE, 0x31FF, 0xFF01, 0xFF09, 0xFF0C, 0xFF0E, 0xFF1A,
363     0xFF1B, 0xFF1F, 0xFF3D, 0xFF5D, 0xFF60, 0
364 };
365 
366 /*********************************************************************
367  *                      TEXT_WordBreak (static)
368  *
369  *  Perform wordbreak processing on the given string
370  *
371  * Assumes that DT_WORDBREAK has been specified and not all the characters
372  * fit.  Note that this function should even be called when the first character
373  * that doesn't fit is known to be a space or tab, so that it can swallow them.
374  *
375  * Note that the Windows processing has some strange properties.
376  * 1. If the text is left-justified and there is room for some of the spaces
377  *    that follow the last word on the line then those that fit are included on
378  *    the line.
379  * 2. If the text is centred or right-justified and there is room for some of
380  *    the spaces that follow the last word on the line then all but one of those
381  *    that fit are included on the line.
382  * 3. (Reasonable behaviour) If the word breaking causes a space to be the first
383  *    character of a new line it will be skipped.
384  *
385  * Arguments
386  *   hdc        [in] The handle to the DC that defines the font.
387  *   str        [in/out] The string that needs to be broken.
388  *   max_str    [in] The dimension of str (number of WCHAR).
389  *   len_str    [in/out] The number of characters in str
390  *   width      [in] The maximum width permitted
391  *   format     [in] The format flags in effect
392  *   chars_fit  [in] The maximum number of characters of str that are already
393  *              known to fit; chars_fit+1 is known not to fit.
394  *   chars_used [out] The number of characters of str that have been "used" and
395  *              do not need to be included in later text.  For example this will
396  *              include any spaces that have been discarded from the start of
397  *              the next line.
398  *   size       [out] The size of the returned text in logical coordinates
399  *
400  * Pedantic assumption - Assumes that the text length is monotonically
401  * increasing with number of characters (i.e. no weird kernings)
402  *
403  * Algorithm
404  *
405  * Work back from the last character that did fit to either a space or the last
406  * character of a word, whichever is met first.
407  * If there was one or the first character didn't fit then
408  *     If the text is centred or right justified and that one character was a
409  *     space then break the line before that character
410  *     Otherwise break the line after that character
411  *     and if the next character is a space then discard it.
412  * Suppose there was none (and the first character did fit).
413  *     If Break Within Word is permitted
414  *         break the word after the last character that fits (there must be
415  *         at least one; none is caught earlier).
416  *     Otherwise
417  *         discard any trailing space.
418  *         include the whole word; it may be ellipsified later
419  *
420  * Break Within Word is permitted under a set of circumstances that are not
421  * totally clear yet.  Currently our best guess is:
422  *     If DT_EDITCONTROL is in effect and neither DT_WORD_ELLIPSIS nor
423  *     DT_PATH_ELLIPSIS is
424  */
425 
426 static void TEXT_WordBreak (HDC hdc, WCHAR *str, unsigned int max_str,
427                             unsigned int *len_str,
428                             int width, int format, unsigned int chars_fit,
429                             unsigned int *chars_used, SIZE *size)
430 {
431     WCHAR *p;
432     int word_fits;
433     assert (format & DT_WORDBREAK);
434     assert (chars_fit < *len_str);
435 
436     /* Work back from the last character that did fit to either a space or the
437      * last character of a word, whichever is met first.
438      */
439     p = str + chars_fit; /* The character that doesn't fit */
440     word_fits = TRUE;
441     if (!chars_fit)
442         word_fits = FALSE;
443     else if (*p == SPACE) /* chars_fit < *len_str so this is valid */
444         p--; /* the word just fitted */
445     else
446     {
447         while (p > str && *(--p) != SPACE && (!IsCJKT(p[1]) ||
448                 p[1] == L'\0' || wcschr(KinsokuClassA, p[1]) != NULL))
449             ;
450         word_fits = (p != str || *p == SPACE || IsCJKT(p[1]));
451     }
452     /* If there was one. */
453     if (word_fits)
454     {
455         int next_is_space;
456         /* break the line before/after that character */
457         if (!(format & (DT_RIGHT | DT_CENTER)) || *p != SPACE)
458             p++;
459         next_is_space = (unsigned int) (p - str) < *len_str && *p == SPACE;
460         *len_str = p - str;
461         /* and if the next character is a space then discard it. */
462         *chars_used = *len_str;
463         if (next_is_space)
464             (*chars_used)++;
465     }
466     /* Suppose there was none. */
467     else
468     {
469         if ((format & (DT_EDITCONTROL | DT_WORD_ELLIPSIS | DT_PATH_ELLIPSIS)) ==
470             DT_EDITCONTROL)
471         {
472             /* break the word after the last character that fits (there must be
473              * at least one). */
474             if (!chars_fit)
475                 ++chars_fit;
476             *len_str = chars_fit;
477             *chars_used = chars_fit;
478 
479             /* FIXME - possible error.  Since the next character is now removed
480              * this could make the text longer so that it no longer fits, and
481              * so we need a loop to test and shrink.
482              */
483         }
484         /* Otherwise */
485         else
486         {
487             /* discard any trailing space. */
488             const WCHAR *e = str + *len_str;
489             p = str + chars_fit;
490             while (p < e && *p != SPACE)
491                 p++;
492             *chars_used = p - str;
493             if (p < e) /* i.e. loop failed because *p == SPACE */
494                 (*chars_used)++;
495 
496             /* include the whole word; it may be ellipsified later */
497             *len_str = p - str;
498             /* Possible optimisation; if DT_WORD_ELLIPSIS only use chars_fit+1
499              * so that it will be too long
500              */
501         }
502     }
503     /* Remeasure the string */
504 #ifdef _WIN32K_
505     GreGetTextExtentExW (hdc, str, *len_str, 0, NULL, NULL, size, 0);
506 #else
507     GetTextExtentExPointW (hdc, str, *len_str, 0, NULL, NULL, size);
508 #endif
509 }
510 
511 /*********************************************************************
512  *                      TEXT_SkipChars
513  *
514  *  Skip over the given number of characters, bearing in mind prefix
515  *  substitution and the fact that a character may take more than one
516  *  WCHAR (Unicode surrogates are two words long) (and there may have been
517  *  a trailing &)
518  *
519  * Parameters
520  *   new_count  [out] The updated count
521  *   new_str    [out] The updated pointer
522  *   start_count [in] The count of remaining characters corresponding to the
523  *                    start of the string
524  *   start_str  [in] The starting point of the string
525  *   max        [in] The number of characters actually in this segment of the
526  *                   string (the & counts)
527  *   n          [in] The number of characters to skip (if prefix then
528  *                   &c counts as one)
529  *   prefix     [in] Apply prefix substitution
530  *
531  * Return Values
532  *   none
533  *
534  * Remarks
535  *   There must be at least n characters in the string
536  *   We need max because the "line" may have ended with a & followed by a tab
537  *   or newline etc. which we don't want to swallow
538  */
539 
540 static void TEXT_SkipChars (int *new_count, const WCHAR **new_str,
541                             int start_count, const WCHAR *start_str,
542                             int max, int n, int prefix)
543 {
544     /* This is specific to wide characters, MSDN doesn't say anything much
545      * about Unicode surrogates yet and it isn't clear if _wcsinc will
546      * correctly handle them so we'll just do this the easy way for now
547      */
548 
549     if (prefix)
550     {
551         const WCHAR *str_on_entry = start_str;
552         assert (max >= n);
553         max -= n;
554         while (n--)
555         {
556             if ((*start_str == PREFIX || *start_str == ALPHA_PREFIX) && max--)
557                 start_str++;
558             start_str++;
559         }
560         start_count -= (start_str - str_on_entry);
561     }
562     else
563     {
564         start_str += n;
565         start_count -= n;
566     }
567     *new_str = start_str;
568     *new_count = start_count;
569 }
570 
571 /*********************************************************************
572  *                      TEXT_Reprefix
573  *
574  *  Reanalyse the text to find the prefixed character.  This is called when
575  *  wordbreaking or ellipsification has shortened the string such that the
576  *  previously noted prefixed character is no longer visible.
577  *
578  * Parameters
579  *   str        [in] The original string segment (including all characters)
580  *   ns         [in] The number of characters in str (including prefixes)
581  *   pe         [in] The ellipsification data
582  *
583  * Return Values
584  *   The prefix offset within the new string segment (the one that contains the
585  *   ellipses and does not contain the prefix characters) (-1 if none)
586  */
587 
588 static int TEXT_Reprefix (const WCHAR *str, unsigned int ns,
589                           const ellipsis_data *pe)
590 {
591     int result = -1;
592     unsigned int i;
593     unsigned int n = pe->before + pe->under + pe->after;
594     assert (n <= ns);
595     for (i = 0; i < n; i++, str++)
596     {
597         if (i == (unsigned int) pe->before)
598         {
599             /* Reached the path ellipsis; jump over it */
600             if (ns < (unsigned int) pe->under) break;
601             str += pe->under;
602             ns -= pe->under;
603             i += pe->under;
604             if (!pe->after) break; /* Nothing after the path ellipsis */
605         }
606         if (!ns) break;
607         ns--;
608         if (*str++ == PREFIX || *str == ALPHA_PREFIX)
609         {
610             str++;
611             if (!ns) break;
612             if (*str != PREFIX)
613                 result = (i < (unsigned int) pe->before || pe->under == 0) ? i : i - pe->under + pe->len;
614                 /* pe->len may be non-zero while pe_under is zero */
615             ns--;
616         }
617     }
618     return result;
619 }
620 
621 /*********************************************************************
622  *  Returns true if and only if the remainder of the line is a single
623  *  newline representation or nothing
624  */
625 
626 static int remainder_is_none_or_newline (int num_chars, const WCHAR *str)
627 {
628     if (!num_chars) return TRUE;
629     if (*str != LF && *str != CR) return FALSE;
630     if (!--num_chars) return TRUE;
631     if (*str == *(str+1)) return FALSE;
632     str++;
633     if (*str != CR && *str != LF) return FALSE;
634     if (--num_chars) return FALSE;
635     return TRUE;
636 }
637 
638 /*********************************************************************
639  *  Return next line of text from a string.
640  *
641  * hdc - handle to DC.
642  * str - string to parse into lines.
643  * count - length of str.
644  * dest - destination in which to return line.
645  * len - dest buffer size in chars on input, copied length into dest on output.
646  * width - maximum width of line in pixels.
647  * format - format type passed to DrawText.
648  * retsize - returned size of the line in pixels.
649  * last_line - TRUE if is the last line that will be processed
650  * p_retstr - If DT_MODIFYSTRING this points to a cursor in the buffer in which
651  *            the return string is built.
652  * tabwidth - The width of a tab in logical coordinates
653  * pprefix_offset - Here is where we return the offset within dest of the first
654  *                  prefixed (underlined) character.  -1 is returned if there
655  *                  are none.  Note that there may be more; the calling code
656  *                  will need to use TEXT_Reprefix to find any later ones.
657  * pellip - Here is where we return the information about any ellipsification
658  *          that was carried out.  Note that if tabs are being expanded then
659  *          this data will correspond to the last text segment actually
660  *          returned in dest; by definition there would not have been any
661  *          ellipsification in earlier text segments of the line.
662  *
663  * Returns pointer to next char in str after end of the line
664  * or NULL if end of str reached.
665  */
666 static const WCHAR *TEXT_NextLineW( HDC hdc, const WCHAR *str, int *count,
667                                  WCHAR *dest, int *len, int width, DWORD format,
668                                  SIZE *retsize, int last_line, WCHAR **p_retstr,
669                                  int tabwidth, int *pprefix_offset,
670                                  ellipsis_data *pellip)
671 {
672     int i = 0, j = 0;
673     int plen = 0;
674     SIZE size = {0, 0};
675     int maxl = *len;
676     int seg_i, seg_count, seg_j;
677     int max_seg_width;
678     int num_fit;
679     int word_broken;
680     int line_fits;
681     unsigned int j_in_seg;
682     int ellipsified;
683     *pprefix_offset = -1;
684 
685     /* For each text segment in the line */
686 
687     retsize->cy = 0;
688     while (*count)
689     {
690 
691         /* Skip any leading tabs */
692 
693         if (str[i] == TAB && (format & DT_EXPANDTABS))
694         {
695             plen = ((plen/tabwidth)+1)*tabwidth;
696             (*count)--; if (j < maxl) dest[j++] = str[i++]; else i++;
697             while (*count && str[i] == TAB)
698             {
699                 plen += tabwidth;
700                 (*count)--; if (j < maxl) dest[j++] = str[i++]; else i++;
701             }
702         }
703 
704 
705         /* Now copy as far as the next tab or cr/lf or eos */
706 
707         seg_i = i;
708         seg_count = *count;
709         seg_j = j;
710 
711         while (*count &&
712                (str[i] != TAB || !(format & DT_EXPANDTABS)) &&
713                ((str[i] != CR && str[i] != LF) || (format & DT_SINGLELINE)))
714         {
715             if ((format & DT_NOPREFIX) || *count <= 1)
716             {
717                 (*count)--; if (j < maxl) dest[j++] = str[i++]; else i++;
718                 continue;
719             }
720 
721 	    if (str[i] == PREFIX || str[i] == ALPHA_PREFIX) {
722                 (*count)--, i++; /* Throw away the prefix itself */
723                 if (str[i] == PREFIX)
724                 {
725                     /* Swallow it before we see it again */
726 		    (*count)--; if (j < maxl) dest[j++] = str[i++]; else i++;
727                 }
728 		else if (*pprefix_offset == -1 || *pprefix_offset >= seg_j)
729                 {
730                     *pprefix_offset = j;
731                 }
732                 /* else the previous prefix was in an earlier segment of the
733                  * line; we will leave it to the drawing code to catch this
734                  * one.
735                  */
736 	    }
737 	    else if (str[i] == KANA_PREFIX)
738             {
739                 /* Throw away katakana access keys */
740                 (*count)--, i++; /* skip the prefix */
741                 (*count)--, i++; /* skip the letter */
742             }
743             else
744             {
745                 (*count)--; if (j < maxl) dest[j++] = str[i++]; else i++;
746             }
747         }
748 
749 
750         /* Measure the whole text segment and possibly WordBreak and
751          * ellipsify it
752          */
753 
754         j_in_seg = j - seg_j;
755         max_seg_width = width - plen;
756 #ifdef _WIN32K_
757         GreGetTextExtentExW (hdc, dest + seg_j, j_in_seg, max_seg_width, (PULONG)&num_fit, NULL, &size, 0);
758 #else
759         GetTextExtentExPointW (hdc, dest + seg_j, j_in_seg, max_seg_width, &num_fit, NULL, &size);
760 #endif
761 
762         /* The Microsoft handling of various combinations of formats is weird.
763          * The following may very easily be incorrect if several formats are
764          * combined, and may differ between versions (to say nothing of the
765          * several bugs in the Microsoft versions).
766          */
767         word_broken = 0;
768         line_fits = (num_fit >= j_in_seg);
769         if (!line_fits && (format & DT_WORDBREAK))
770         {
771             const WCHAR *s;
772             unsigned int chars_used;
773             TEXT_WordBreak (hdc, dest+seg_j, maxl-seg_j, &j_in_seg,
774                             max_seg_width, format, num_fit, &chars_used, &size);
775             line_fits = (size.cx <= max_seg_width);
776             /* and correct the counts */
777             TEXT_SkipChars (count, &s, seg_count, str+seg_i, i-seg_i,
778                             chars_used, !(format & DT_NOPREFIX));
779             i = s - str;
780             word_broken = 1;
781         }
782         pellip->before = j_in_seg;
783         pellip->under = 0;
784         pellip->after = 0;
785         pellip->len = 0;
786         ellipsified = 0;
787         if (!line_fits && (format & DT_PATH_ELLIPSIS))
788         {
789             TEXT_PathEllipsify (hdc, dest + seg_j, maxl-seg_j, &j_in_seg,
790                                 max_seg_width, &size, *p_retstr, pellip);
791             line_fits = (size.cx <= max_seg_width);
792             ellipsified = 1;
793         }
794         /* NB we may end up ellipsifying a word-broken or path_ellipsified
795          * string */
796         if ((!line_fits && (format & DT_WORD_ELLIPSIS)) ||
797             ((format & DT_END_ELLIPSIS) &&
798               ((last_line && *count) ||
799                (remainder_is_none_or_newline (*count, &str[i]) && !line_fits))))
800         {
801             int before, len_ellipsis;
802             TEXT_Ellipsify (hdc, dest + seg_j, maxl-seg_j, &j_in_seg,
803                             max_seg_width, &size, *p_retstr, &before, &len_ellipsis);
804             if (before > pellip->before)
805             {
806                 /* We must have done a path ellipsis too */
807                 pellip->after = before - pellip->before - pellip->len;
808                 /* Leave the len as the length of the first ellipsis */
809             }
810             else
811             {
812                 /* If we are here after a path ellipsification it must be
813                  * because even the ellipsis itself didn't fit.
814                  */
815                 assert (pellip->under == 0 && pellip->after == 0);
816                 pellip->before = before;
817                 pellip->len = len_ellipsis;
818                 /* pellip->after remains as zero as does
819                  * pellip->under
820                  */
821             }
822             line_fits = (size.cx <= max_seg_width);
823             ellipsified = 1;
824         }
825         /* As an optimisation if we have ellipsified and we are expanding
826          * tabs and we haven't reached the end of the line we can skip to it
827          * now rather than going around the loop again.
828          */
829         if ((format & DT_EXPANDTABS) && ellipsified)
830         {
831             if (format & DT_SINGLELINE)
832                 *count = 0;
833             else
834             {
835                 while ((*count) && str[i] != CR && str[i] != LF)
836                 {
837                     (*count)--, i++;
838                 }
839             }
840         }
841 
842         j = seg_j + j_in_seg;
843         if (*pprefix_offset >= seg_j + pellip->before)
844         {
845             *pprefix_offset = TEXT_Reprefix (str + seg_i, i - seg_i, pellip);
846             if (*pprefix_offset != -1)
847                 *pprefix_offset += seg_j;
848         }
849 
850         plen += size.cx;
851         if (size.cy > retsize->cy)
852             retsize->cy = size.cy;
853 
854         if (word_broken)
855             break;
856         else if (!*count)
857             break;
858         else if (str[i] == CR || str[i] == LF)
859         {
860             (*count)--, i++;
861             if (*count && (str[i] == CR || str[i] == LF) && str[i] != str[i-1])
862             {
863                 (*count)--, i++;
864             }
865             break;
866         }
867         /* else it was a Tab and we go around again */
868     }
869 
870     retsize->cx = plen;
871     *len = j;
872     if (*count)
873         return (&str[i]);
874     else
875         return NULL;
876 }
877 
878 
879 /***********************************************************************
880  *                      TEXT_DrawUnderscore
881  *
882  *  Draw the underline under the prefixed character
883  *
884  * Parameters
885  *   hdc        [in] The handle of the DC for drawing
886  *   x          [in] The x location of the line segment (logical coordinates)
887  *   y          [in] The y location of where the underscore should appear
888  *                   (logical coordinates)
889  *   str        [in] The text of the line segment
890  *   offset     [in] The offset of the underscored character within str
891  *   rect       [in] Clipping rectangle (if not NULL)
892  */
893 /* Synced with wine 1.1.32 */
894 static void TEXT_DrawUnderscore (HDC hdc, int x, int y, const WCHAR *str, int offset, const RECT *rect)
895 {
896     int prefix_x;
897     int prefix_end;
898     SIZE size;
899     HPEN hpen;
900     HPEN oldPen;
901 #ifdef _WIN32K_
902     GreGetTextExtentW (hdc, str, offset, &size, 0);
903 #else
904     GetTextExtentPointW (hdc, str, offset, &size);
905 #endif
906     prefix_x = x + size.cx;
907 #ifdef _WIN32K_
908     GreGetTextExtentW (hdc, str, offset+1, &size, 0);
909 #else
910     GetTextExtentPointW (hdc, str, offset+1, &size);
911 #endif
912     prefix_end = x + size.cx - 1;
913     /* The above method may eventually be slightly wrong due to kerning etc. */
914 
915     /* Check for clipping */
916     if (rect)
917     {
918         if (prefix_x > rect->right || prefix_end < rect->left ||
919             y < rect->top || y > rect->bottom)
920             return; /* Completely outside */
921         /* Partially outside */
922         if (prefix_x   < rect->left ) prefix_x   = rect->left;
923         if (prefix_end > rect->right) prefix_end = rect->right;
924     }
925 #ifdef _WIN32K_
926     hpen = NtGdiCreatePen (PS_SOLID, 1, GreGetTextColor (hdc), NULL);
927     oldPen = NtGdiSelectPen (hdc, hpen);
928     GreMoveTo (hdc, prefix_x, y, NULL);
929     NtGdiLineTo (hdc, prefix_end, y);
930     NtGdiSelectPen (hdc, oldPen);
931     GreDeleteObject (hpen);
932 #else
933     hpen = CreatePen (PS_SOLID, 1, GetTextColor (hdc));
934     oldPen = SelectObject (hdc, hpen);
935     MoveToEx (hdc, prefix_x, y, NULL);
936     LineTo (hdc, prefix_end, y);
937     SelectObject (hdc, oldPen);
938     DeleteObject (hpen);
939 #endif
940 }
941 
942 #ifdef _WIN32K_
943 /***********************************************************************
944  *                      UserExtTextOutW
945  *
946  *  Callback to usermode to use ExtTextOut, which will apply complex
947  *  script processing if needed and then draw it
948  *
949  * Parameters
950  *   hdc        [in] The handle of the DC for drawing
951  *   x          [in] The x location of the string
952  *   y          [in] The y location of the string
953  *   flags      [in] ExtTextOut flags
954  *   lprc       [in] Clipping rectangle (if not NULL)
955  *   lpString   [in] String to be drawn
956  *   count      [in] String length
957  */
958 BOOL UserExtTextOutW(HDC hdc,
959                      INT x,
960                      INT y,
961                      UINT flags,
962                      PRECTL lprc,
963                      LPCWSTR lpString,
964                      UINT count)
965 {
966     PVOID ResultPointer;
967     ULONG ResultLength;
968     ULONG ArgumentLength;
969     ULONG_PTR pStringBuffer;
970     NTSTATUS Status;
971     PLPK_CALLBACK_ARGUMENTS Argument;
972     BOOL bResult;
973 
974     ArgumentLength = sizeof(LPK_CALLBACK_ARGUMENTS);
975 
976     pStringBuffer = ArgumentLength;
977     ArgumentLength += sizeof(WCHAR) * (count + 2);
978 
979     Argument = IntCbAllocateMemory(ArgumentLength);
980 
981     if (!Argument)
982     {
983         goto fallback;
984     }
985 
986     /* Initialize struct members */
987     Argument->hdc   = hdc;
988     Argument->x     = x;
989     Argument->y     = y;
990     Argument->flags = flags;
991     Argument->count = count;
992 
993     if (lprc)
994     {
995         Argument->rect = *lprc;
996         Argument->bRect = TRUE;
997     }
998     else
999     {
1000         RtlZeroMemory(&Argument->rect, sizeof(RECT));
1001         Argument->bRect = FALSE;
1002     }
1003 
1004     /* Align lpString
1005        mimicks code from co_IntClientLoadLibrary */
1006     Argument->lpString = (LPWSTR)pStringBuffer;
1007     pStringBuffer += (ULONG_PTR)Argument;
1008 
1009     Status = RtlStringCchCopyNW((LPWSTR)pStringBuffer, count + 1, lpString, count);
1010 
1011     if (!NT_SUCCESS(Status))
1012     {
1013         IntCbFreeMemory(Argument);
1014         goto fallback;
1015     }
1016 
1017     UserLeaveCo();
1018 
1019     Status = KeUserModeCallback(USER32_CALLBACK_LPK,
1020                                 Argument,
1021                                 ArgumentLength,
1022                                 &ResultPointer,
1023                                 &ResultLength);
1024 
1025     UserEnterCo();
1026 
1027     IntCbFreeMemory(Argument);
1028 
1029     if (NT_SUCCESS(Status))
1030     {
1031         _SEH2_TRY
1032         {
1033             ProbeForRead(ResultPointer, sizeof(BOOL), 1);
1034             bResult = *(LPBOOL)ResultPointer;
1035         }
1036         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1037         {
1038             ERR("Failed to copy result from user mode!\n");
1039             Status = _SEH2_GetExceptionCode();
1040         }
1041         _SEH2_END;
1042     }
1043 
1044     if (!NT_SUCCESS(Status))
1045     {
1046         goto fallback;
1047     }
1048 
1049     return bResult;
1050 
1051 fallback:
1052     return GreExtTextOutW(hdc, x, y, flags, lprc, lpString, count, NULL, 0);
1053 }
1054 #endif
1055 
1056 /***********************************************************************
1057  *           DrawTextExW    (USER32.@)
1058  *
1059  * The documentation on the extra space required for DT_MODIFYSTRING at MSDN
1060  * is not quite complete, especially with regard to \0.  We will assume that
1061  * the returned string could have a length of up to i_count+3 and also have
1062  * a trailing \0 (which would be 4 more than a not-null-terminated string but
1063  * 3 more than a null-terminated string).  If this is not so then increase
1064  * the allowance in DrawTextExA.
1065  */
1066 #define MAX_BUFFER 1024
1067 /*
1068  * DrawTextExW
1069  *
1070  * Synced with Wine Staging 1.7.37
1071  */
1072 INT WINAPI DrawTextExWorker( HDC hdc,
1073                              LPWSTR str,
1074                              INT i_count,
1075                              LPRECT rect,
1076                              UINT flags,
1077                              LPDRAWTEXTPARAMS dtp )
1078 {
1079     SIZE size;
1080     const WCHAR *strPtr;
1081     WCHAR *retstr, *p_retstr;
1082     size_t size_retstr;
1083     WCHAR line[MAX_BUFFER];
1084     int len, lh, count=i_count;
1085     TEXTMETRICW tm;
1086     int lmargin = 0, rmargin = 0;
1087     int x = rect->left, y = rect->top;
1088     int width = rect->right - rect->left;
1089     int max_width = 0;
1090     int last_line;
1091     int tabwidth /* to keep gcc happy */ = 0;
1092     int prefix_offset;
1093     ellipsis_data ellip;
1094     BOOL invert_y=FALSE;
1095 
1096     HRGN    hrgn = 0;
1097 
1098 #ifdef _WIN32K_
1099     TRACE("%S, %d, %08x\n", str, count, flags);
1100 #else
1101     TRACE("%s, %d, [%s] %08x\n", debugstr_wn (str, count), count,
1102         wine_dbgstr_rect(rect), flags);
1103 #endif
1104     if (dtp) TRACE("Params: iTabLength=%d, iLeftMargin=%d, iRightMargin=%d\n",
1105           dtp->iTabLength, dtp->iLeftMargin, dtp->iRightMargin);
1106 
1107     if (!str) return 0;
1108 
1109     strPtr = str;
1110 
1111     if (flags & DT_SINGLELINE)
1112         flags &= ~DT_WORDBREAK;
1113 #ifdef _WIN32K_
1114     GreGetTextMetricsW(hdc, &tm);
1115 #else
1116     GetTextMetricsW(hdc, &tm);
1117 #endif
1118     if (flags & DT_EXTERNALLEADING)
1119 	lh = tm.tmHeight + tm.tmExternalLeading;
1120     else
1121 	lh = tm.tmHeight;
1122 
1123     if (str[0] && count == 0)
1124         return lh;
1125 
1126     if (dtp && dtp->cbSize != sizeof(DRAWTEXTPARAMS))
1127         return 0;
1128 #ifdef _WIN32K_
1129     if (GreGetGraphicsMode(hdc) == GM_COMPATIBLE)
1130     {
1131         SIZE window_ext, viewport_ext;
1132         GreGetWindowExtEx(hdc, &window_ext);
1133         GreGetViewportExtEx(hdc, &viewport_ext);
1134         if ((window_ext.cy > 0) != (viewport_ext.cy > 0))
1135             invert_y = TRUE;
1136     }
1137 #else
1138     if (GetGraphicsMode(hdc) == GM_COMPATIBLE)
1139     {
1140         SIZE window_ext, viewport_ext;
1141         GetWindowExtEx(hdc, &window_ext);
1142         GetViewportExtEx(hdc, &viewport_ext);
1143         if ((window_ext.cy > 0) != (viewport_ext.cy > 0))
1144             invert_y = TRUE;
1145     }
1146 #endif
1147     if (count == -1)
1148     {
1149 #ifdef _WIN32K_
1150         count = wcslen(str);
1151 #else
1152         count = strlenW(str);
1153 #endif
1154         if (count == 0)
1155         {
1156             if( flags & DT_CALCRECT)
1157             {
1158                 rect->right = rect->left;
1159                 if( flags & DT_SINGLELINE)
1160                     rect->bottom = rect->top + (invert_y ? -lh : lh);
1161                 else
1162                     rect->bottom = rect->top;
1163             }
1164             return lh;
1165         }
1166     }
1167 
1168     if (dtp)
1169     {
1170         lmargin = dtp->iLeftMargin;
1171         rmargin = dtp->iRightMargin;
1172         if (!(flags & (DT_CENTER | DT_RIGHT)))
1173             x += lmargin;
1174         dtp->uiLengthDrawn = 0;     /* This param RECEIVES number of chars processed */
1175     }
1176 
1177     if (flags & DT_EXPANDTABS)
1178     {
1179         int tabstop = ((flags & DT_TABSTOP) && dtp && dtp->iTabLength) ? dtp->iTabLength : 8;
1180 	tabwidth = tm.tmAveCharWidth * tabstop;
1181     }
1182 
1183     if (flags & DT_CALCRECT) flags |= DT_NOCLIP;
1184 #ifndef _WIN32K_ ///// Fix CORE-2201.
1185     if (!(flags & DT_NOCLIP) )
1186     {
1187        int hasClip;
1188        hrgn = CreateRectRgn(0,0,0,0);
1189        if (hrgn)
1190        {
1191           hasClip = GetClipRgn(hdc, hrgn);
1192           // If the region to be retrieved is NULL, the return value is 0.
1193           if (hasClip != 1)
1194           {
1195              DeleteObject(hrgn);
1196              hrgn = NULL;
1197           }
1198           IntersectClipRect(hdc, rect->left, rect->top, rect->right, rect->bottom);
1199        }
1200     }
1201 #else
1202     if (!(flags & DT_NOCLIP) )
1203     {
1204        int hasClip;
1205        hrgn = NtGdiCreateRectRgn(0,0,0,0);
1206        if (hrgn)
1207        {
1208           hasClip = NtGdiGetRandomRgn(hdc, hrgn, CLIPRGN);
1209           if (hasClip != 1)
1210           {
1211              GreDeleteObject(hrgn);
1212              hrgn = NULL;
1213           }
1214           NtGdiIntersectClipRect(hdc, rect->left, rect->top, rect->right, rect->bottom);
1215        }
1216     }
1217 #endif /////
1218     if (flags & DT_MODIFYSTRING)
1219     {
1220         size_retstr = (count + 4) * sizeof (WCHAR);
1221 #ifdef _WIN32K_
1222         retstr = ExAllocatePoolWithTag(PagedPool, size_retstr, USERTAG_RTL);
1223 #else
1224         retstr = HeapAlloc(GetProcessHeap(), 0, size_retstr);
1225 #endif
1226         if (!retstr) return 0;
1227         memcpy (retstr, str, size_retstr);
1228     }
1229     else
1230     {
1231         size_retstr = 0;
1232         retstr = NULL;
1233     }
1234     p_retstr = retstr;
1235 
1236     do
1237     {
1238 	len = sizeof(line)/sizeof(line[0]);
1239 	if (invert_y)
1240             last_line = !(flags & DT_NOCLIP) && y - ((flags & DT_EDITCONTROL) ? 2*lh-1 : lh) < rect->bottom;
1241 	else
1242             last_line = !(flags & DT_NOCLIP) && y + ((flags & DT_EDITCONTROL) ? 2*lh-1 : lh) > rect->bottom;
1243 	strPtr = TEXT_NextLineW(hdc, strPtr, &count, line, &len, width, flags, &size, last_line, &p_retstr, tabwidth, &prefix_offset, &ellip);
1244 
1245 #ifdef __REACTOS__
1246     if (flags & DT_CENTER)
1247     {
1248         if (((rect->right - rect->left) < size.cx) && (flags & DT_CALCRECT))
1249         {
1250             x = rect->left + size.cx;
1251         }
1252         else
1253         {
1254             x = (rect->left + rect->right - size.cx) / 2;
1255         }
1256     }
1257 #else
1258 	if (flags & DT_CENTER) x = (rect->left + rect->right -
1259 				    size.cx) / 2;
1260 #endif
1261 	else if (flags & DT_RIGHT) x = rect->right - size.cx;
1262 
1263 	if (flags & DT_SINGLELINE)
1264 	{
1265 #ifdef __REACTOS__
1266         if (flags & DT_VCENTER)
1267         {
1268             if (flags & DT_CALCRECT)
1269             {
1270                 if (rect->bottom - rect->top < size.cy / 2)
1271                     y = rect->top + (invert_y ? size.cy : -size.cy) / 2;
1272             }
1273             else
1274             {
1275                 y = rect->top + (rect->bottom - rect->top + (invert_y ? size.cy : -size.cy)) / 2;
1276             }
1277         }
1278         else if (flags & DT_BOTTOM)
1279             y = rect->bottom + (invert_y ? 0 : -size.cy);
1280 #else
1281 	    if (flags & DT_VCENTER) y = rect->top +
1282 	    	(rect->bottom - rect->top) / 2 - size.cy / 2;
1283 	    else if (flags & DT_BOTTOM) y = rect->bottom - size.cy;
1284 #endif
1285     }
1286 
1287 	if (!(flags & DT_CALCRECT))
1288 	{
1289             const WCHAR *str = line;
1290             int xseg = x;
1291             while (len)
1292             {
1293                 int len_seg;
1294                 SIZE size;
1295                 if ((flags & DT_EXPANDTABS))
1296                 {
1297                     const WCHAR *p;
1298                     p = str; while (p < str+len && *p != TAB) p++;
1299                     len_seg = p - str;
1300                     if (len_seg != len &&
1301 #ifdef _WIN32K_
1302                         !GreGetTextExtentW(hdc, str, len_seg, &size, 0))
1303 #else
1304                         !GetTextExtentPointW(hdc, str, len_seg, &size))
1305 #endif
1306                     {
1307 #ifdef _WIN32K_
1308                         ExFreePoolWithTag(retstr, USERTAG_RTL);
1309 #else
1310                         HeapFree (GetProcessHeap(), 0, retstr);
1311 #endif
1312                         return 0;
1313                     }
1314                 }
1315                 else
1316                     len_seg = len;
1317 #ifdef _WIN32K_
1318                 if (!UserExtTextOutW( hdc, xseg, y,
1319                                      ((flags & DT_NOCLIP) ? 0 : ETO_CLIPPED) |
1320                                      ((flags & DT_RTLREADING) ? ETO_RTLREADING : 0),
1321                                      rect, str, len_seg))
1322 #else
1323                 if (!ExtTextOutW( hdc, xseg, y,
1324                                  ((flags & DT_NOCLIP) ? 0 : ETO_CLIPPED) |
1325                                  ((flags & DT_RTLREADING) ? ETO_RTLREADING : 0),
1326                                  rect, str, len_seg, NULL ))
1327 #endif
1328                 {
1329 #ifdef _WIN32K_
1330                     ExFreePoolWithTag(retstr, USERTAG_RTL);
1331 #else
1332                     HeapFree (GetProcessHeap(), 0, retstr);
1333 #endif
1334                     return 0;
1335                 }
1336                 if (prefix_offset != -1 && prefix_offset < len_seg)
1337                 {
1338                     TEXT_DrawUnderscore (hdc, xseg, y + tm.tmAscent + 1, str, prefix_offset, (flags & DT_NOCLIP) ? NULL : rect);
1339                 }
1340                 len -= len_seg;
1341                 str += len_seg;
1342                 if (len)
1343                 {
1344                     assert ((flags & DT_EXPANDTABS) && *str == TAB);
1345                     len--; str++;
1346                     xseg += ((size.cx/tabwidth)+1)*tabwidth;
1347                     if (prefix_offset != -1)
1348                     {
1349                         if (prefix_offset < len_seg)
1350                         {
1351                             /* We have just drawn an underscore; we ought to
1352                              * figure out where the next one is.  I am going
1353                              * to leave it for now until I have a better model
1354                              * for the line, which will make reprefixing easier.
1355                              * This is where ellip would be used.
1356                              */
1357                             prefix_offset = -1;
1358                         }
1359                         else
1360                             prefix_offset -= len_seg;
1361                     }
1362                 }
1363             }
1364 	}
1365 	else if (size.cx > max_width)
1366 	    max_width = size.cx;
1367 
1368         y += invert_y ? -lh : lh;
1369         if (dtp)
1370             dtp->uiLengthDrawn += len;
1371     }
1372     while (strPtr && !last_line);
1373 
1374 #ifndef _WIN32K_
1375     if (!(flags & DT_NOCLIP) )
1376     {
1377        SelectClipRgn(hdc, hrgn);
1378        if (hrgn)
1379        {
1380           DeleteObject(hrgn);
1381        }
1382     }
1383 #else
1384     if (!(flags & DT_NOCLIP) )
1385     {
1386        NtGdiExtSelectClipRgn(hdc, hrgn, RGN_COPY);
1387        if (hrgn)
1388        {
1389           GreDeleteObject(hrgn);
1390        }
1391     }
1392 #endif
1393 
1394     if (flags & DT_CALCRECT)
1395     {
1396 	rect->right = rect->left + max_width;
1397 	rect->bottom = y;
1398         if (dtp)
1399             rect->right += lmargin + rmargin;
1400     }
1401     if (retstr)
1402     {
1403         memcpy (str, retstr, size_retstr);
1404 #ifdef _WIN32K_
1405         ExFreePoolWithTag(retstr, USERTAG_RTL);
1406 #else
1407         HeapFree (GetProcessHeap(), 0, retstr);
1408 #endif
1409     }
1410     return y - rect->top;
1411 }
1412 
1413