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