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