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