1 /* 2 * ReactOS kernel 3 * Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 18 */ 19 /* 20 * PROJECT: ReactOS user32.dll 21 * FILE: win32ss/user/user32/windows/font.c 22 * PURPOSE: Draw Text 23 * PROGRAMMER: Casper S. Hornstrup (chorns@users.sourceforge.net) 24 * UPDATE HISTORY: 25 * 09-05-2001 CSH Created 26 */ 27 28 /* INCLUDES ******************************************************************/ 29 30 #include <user32.h> 31 32 DWORD WINAPI GdiGetCodePage(HDC hdc); 33 34 INT WINAPI DrawTextExWorker( HDC hdc, LPWSTR str, INT i_count, 35 LPRECT rect, UINT flags, LPDRAWTEXTPARAMS dtp ); 36 37 38 /* FUNCTIONS *****************************************************************/ 39 40 41 /*********************************************************************** 42 * TEXT_TabbedTextOut 43 * 44 * Helper function for TabbedTextOut() and GetTabbedTextExtent(). 45 * Note: this doesn't work too well for text-alignment modes other 46 * than TA_LEFT|TA_TOP. But we want bug-for-bug compatibility :-) 47 */ 48 /* WINE synced 22-May-2006 */ 49 LONG TEXT_TabbedTextOut( HDC hdc, 50 INT x, 51 INT y, 52 LPCWSTR lpstr, 53 INT count, 54 INT cTabStops, 55 const INT *lpTabPos, 56 INT nTabOrg, 57 BOOL fDisplayText ) 58 { 59 INT defWidth; 60 SIZE extent; 61 int i, j; 62 int start = x; 63 TEXTMETRICW tm; 64 65 if (!lpTabPos) 66 cTabStops=0; 67 68 GetTextMetricsW( hdc, &tm ); 69 70 if (cTabStops == 1) 71 { 72 defWidth = *lpTabPos; 73 cTabStops = 0; 74 } 75 else 76 { 77 defWidth = 8 * tm.tmAveCharWidth; 78 } 79 80 while (count > 0) 81 { 82 RECT r; 83 INT x0; 84 x0 = x; 85 r.left = x0; 86 /* chop the string into substrings of 0 or more <tabs> 87 * possibly followed by 1 or more normal characters */ 88 for (i = 0; i < count; i++) 89 if (lpstr[i] != '\t') break; 90 for (j = i; j < count; j++) 91 if (lpstr[j] == '\t') break; 92 /* get the extent of the normal character part */ 93 GetTextExtentPointW( hdc, lpstr + i, j - i , &extent ); 94 /* and if there is a <tab>, calculate its position */ 95 if( i) { 96 /* get x coordinate for the drawing of this string */ 97 for (; cTabStops > i; lpTabPos++, cTabStops--) 98 { 99 if( nTabOrg + abs( *lpTabPos) > x) { 100 if( lpTabPos[ i - 1] >= 0) { 101 /* a left aligned tab */ 102 x = nTabOrg + lpTabPos[ i-1] + extent.cx; 103 break; 104 } 105 else 106 { 107 /* if tab pos is negative then text is right-aligned 108 * to tab stop meaning that the string extends to the 109 * left, so we must subtract the width of the string */ 110 if (nTabOrg - lpTabPos[ i - 1] - extent.cx > x) 111 { 112 x = nTabOrg - lpTabPos[ i - 1]; 113 x0 = x - extent.cx; 114 break; 115 } 116 } 117 } 118 } 119 /* if we have run out of tab stops and we have a valid default tab 120 * stop width then round x up to that width */ 121 if ((cTabStops <= i) && (defWidth > 0)) { 122 x0 = nTabOrg + ((x - nTabOrg) / defWidth + i) * defWidth; 123 x = x0 + extent.cx; 124 } else if ((cTabStops <= i) && (defWidth < 0)) { 125 x = nTabOrg + ((x - nTabOrg + extent.cx) / -defWidth + i) 126 * -defWidth; 127 x0 = x - extent.cx; 128 } 129 } else 130 x += extent.cx; 131 132 if (fDisplayText) 133 { 134 r.top = y; 135 r.right = x; 136 r.bottom = y + extent.cy; 137 138 ExtTextOutW( hdc, x0, y, GetBkMode(hdc) == OPAQUE ? ETO_OPAQUE : 0, 139 &r, lpstr + i, j - i, NULL ); 140 } 141 count -= j; 142 lpstr += j; 143 } 144 145 if(!extent.cy) 146 extent.cy = tm.tmHeight; 147 148 return MAKELONG(x - start, extent.cy); 149 } 150 151 /* 152 * @implemented 153 */ 154 LONG 155 WINAPI 156 TabbedTextOutA( 157 HDC hDC, 158 int X, 159 int Y, 160 LPCSTR lpString, 161 int nCount, 162 int nTabPositions, 163 CONST INT *lpnTabStopPositions, 164 int nTabOrigin) 165 { 166 LONG ret; 167 DWORD len; 168 LPWSTR strW; 169 UINT cp = GdiGetCodePage( hDC ); // CP_ACP 170 171 len = MultiByteToWideChar(cp, 0, lpString, nCount, NULL, 0); 172 if (!len) return 0; 173 strW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); 174 if (!strW) return 0; 175 MultiByteToWideChar(cp, 0, lpString, nCount, strW, len); 176 ret = TabbedTextOutW(hDC, X, Y, strW, len, nTabPositions, lpnTabStopPositions, nTabOrigin); 177 HeapFree(GetProcessHeap(), 0, strW); 178 return ret; 179 } 180 181 /* 182 * @implemented 183 */ 184 LONG 185 WINAPI 186 TabbedTextOutW( 187 HDC hDC, 188 int X, 189 int Y, 190 LPCWSTR lpString, 191 int nCount, 192 int nTabPositions, 193 CONST INT *lpnTabStopPositions, 194 int nTabOrigin) 195 { 196 return TEXT_TabbedTextOut(hDC, X, Y, lpString, nCount, nTabPositions, lpnTabStopPositions, nTabOrigin, TRUE); 197 } 198 199 /* WINE synced 22-May-2006 */ 200 /* 201 * @implemented 202 */ 203 DWORD 204 WINAPI 205 GetTabbedTextExtentA( 206 HDC hDC, 207 LPCSTR lpString, 208 int nCount, 209 int nTabPositions, 210 CONST INT *lpnTabStopPositions) 211 { 212 LONG ret; 213 UINT cp = GdiGetCodePage( hDC ); // CP_ACP 214 DWORD len; 215 LPWSTR strW; 216 217 len = MultiByteToWideChar(cp, 0, lpString, nCount, NULL, 0); 218 if (!len) return 0; 219 strW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); 220 if (!strW) return 0; 221 MultiByteToWideChar(cp, 0, lpString, nCount, strW, len); 222 ret = GetTabbedTextExtentW(hDC, strW, len, nTabPositions, lpnTabStopPositions); 223 HeapFree(GetProcessHeap(), 0, strW); 224 return ret; 225 } 226 227 /* 228 * @implemented 229 */ 230 DWORD 231 WINAPI 232 GetTabbedTextExtentW( 233 HDC hDC, 234 LPCWSTR lpString, 235 int nCount, 236 int nTabPositions, 237 CONST INT *lpnTabStopPositions) 238 { 239 return TEXT_TabbedTextOut(hDC, 0, 0, lpString, nCount, nTabPositions, lpnTabStopPositions, 0, FALSE); 240 } 241 242 243 /*********************************************************************** 244 * DrawTextExW (USER32.@) 245 * 246 * The documentation on the extra space required for DT_MODIFYSTRING at MSDN 247 * is not quite complete, especially with regard to \0. We will assume that 248 * the returned string could have a length of up to i_count+3 and also have 249 * a trailing \0 (which would be 4 more than a not-null-terminated string but 250 * 3 more than a null-terminated string). If this is not so then increase 251 * the allowance in DrawTextExA. 252 */ 253 #define MAX_BUFFER 1024 254 /* 255 * @implemented 256 * 257 * Synced with Wine Staging 1.7.37 258 */ 259 INT WINAPI DrawTextExW( HDC hdc, LPWSTR str, INT i_count, 260 LPRECT rect, UINT flags, LPDRAWTEXTPARAMS dtp ) 261 { 262 return DrawTextExWorker( hdc, str, i_count, rect, flags, dtp ); 263 } 264 265 /*********************************************************************** 266 * DrawTextExA (USER32.@) 267 * 268 * If DT_MODIFYSTRING is specified then there must be room for up to 269 * 4 extra characters. We take great care about just how much modified 270 * string we return. 271 * 272 * @implemented 273 * 274 * Synced with Wine Staging 1.7.37 275 */ 276 INT WINAPI DrawTextExA( HDC hdc, LPSTR str, INT count, 277 LPRECT rect, UINT flags, LPDRAWTEXTPARAMS dtp ) 278 { 279 WCHAR *wstr; 280 WCHAR *p; 281 INT ret = 0; 282 int i; 283 DWORD wcount; 284 DWORD wmax; 285 DWORD amax; 286 UINT cp; 287 288 if (!count) return 0; 289 if (!str && count > 0) return 0; 290 if( !str || ((count == -1) && !(count = strlen(str)))) 291 { 292 int lh; 293 TEXTMETRICA tm; 294 295 if (dtp && dtp->cbSize != sizeof(DRAWTEXTPARAMS)) 296 return 0; 297 298 GetTextMetricsA(hdc, &tm); 299 if (flags & DT_EXTERNALLEADING) 300 lh = tm.tmHeight + tm.tmExternalLeading; 301 else 302 lh = tm.tmHeight; 303 304 if( flags & DT_CALCRECT) 305 { 306 rect->right = rect->left; 307 if( flags & DT_SINGLELINE) 308 rect->bottom = rect->top + lh; 309 else 310 rect->bottom = rect->top; 311 } 312 return lh; 313 } 314 cp = GdiGetCodePage( hdc ); 315 wcount = MultiByteToWideChar( cp, 0, str, count, NULL, 0 ); 316 wmax = wcount; 317 amax = count; 318 if (flags & DT_MODIFYSTRING) 319 { 320 wmax += 4; 321 amax += 4; 322 } 323 wstr = HeapAlloc(GetProcessHeap(), 0, wmax * sizeof(WCHAR)); 324 if (wstr) 325 { 326 MultiByteToWideChar( cp, 0, str, count, wstr, wcount ); 327 if (flags & DT_MODIFYSTRING) 328 for (i=4, p=wstr+wcount; i--; p++) *p=0xFFFE; 329 /* Initialise the extra characters so that we can see which ones 330 * change. U+FFFE is guaranteed to be not a unicode character and 331 * so will not be generated by DrawTextEx itself. 332 */ 333 ret = DrawTextExW( hdc, wstr, wcount, rect, flags, dtp ); 334 if (flags & DT_MODIFYSTRING) 335 { 336 /* Unfortunately the returned string may contain multiple \0s 337 * and so we need to measure it ourselves. 338 */ 339 for (i=4, p=wstr+wcount; i-- && *p != 0xFFFE; p++) wcount++; 340 WideCharToMultiByte( cp, 0, wstr, wcount, str, amax, NULL, NULL ); 341 } 342 HeapFree(GetProcessHeap(), 0, wstr); 343 } 344 return ret; 345 } 346 347 /*********************************************************************** 348 * DrawTextW (USER32.@) 349 * 350 * @implemented 351 * Synced with Wine Staging 1.7.37 352 */ 353 INT WINAPI DrawTextW( HDC hdc, LPCWSTR str, INT count, LPRECT rect, UINT flags ) 354 { 355 DRAWTEXTPARAMS dtp; 356 357 memset (&dtp, 0, sizeof(dtp)); 358 dtp.cbSize = sizeof(dtp); 359 if (flags & DT_TABSTOP) 360 { 361 dtp.iTabLength = (flags >> 8) & 0xff; 362 flags &= 0xffff00ff; 363 } 364 return DrawTextExW(hdc, (LPWSTR)str, count, rect, flags, &dtp); 365 } 366 367 /*********************************************************************** 368 * DrawTextA (USER32.@) 369 * 370 * @implemented 371 * Synced with Wine Staging 1.7.37 372 */ 373 INT WINAPI DrawTextA( HDC hdc, LPCSTR str, INT count, LPRECT rect, UINT flags ) 374 { 375 DRAWTEXTPARAMS dtp; 376 377 memset (&dtp, 0, sizeof(dtp)); 378 dtp.cbSize = sizeof(dtp); 379 if (flags & DT_TABSTOP) 380 { 381 dtp.iTabLength = (flags >> 8) & 0xff; 382 flags &= 0xffff00ff; 383 } 384 return DrawTextExA( hdc, (LPSTR)str, count, rect, flags, &dtp ); 385 } 386 387 /*************************************************************************** 388 * UserLpkPSMTextOut 389 * 390 * Stripped down version of DrawText, can only draw single line text and 391 * Prefix underscore (only on the last found amperstand) 392 * only flags to be found to be of use in testing: 393 * 394 * DT_NOPREFIX - Draw the string as is it without any changes 395 * DT_HIDEPREFIX - Draw the string without underscore 396 * DT_PREFIXONLY - Draw only the underscore 397 * 398 * without any of these flags the behavior is the string being drawn without the amperstands and 399 * with the underscore. 400 * 401 * lpk has an equivalent function - LpkPSMTextOut 402 * Notes by testing: 403 * This function in windows doesn't check if lpString is NULL, which results a crash, 404 * returns seemingly random values without any logic, and ignores the DT_NOPREFIX value. 405 * All of these issues don't exist in the LPK version. 406 * 407 * Note: lpString does not need to be null terminated 408 */ 409 #define PREFIX 38 410 #define ALPHA_PREFIX 30 /* Win16: Alphabet prefix */ 411 #define KANA_PREFIX 31 /* Win16: Katakana prefix */ 412 413 INT WINAPI UserLpkPSMTextOut(HDC hdc, int x, int y, LPCWSTR lpString, int cString, DWORD dwFlags) 414 { 415 SIZE size; 416 TEXTMETRICW tm; 417 int len, i = 0, j = 0; 418 int prefix_count = 0, prefix_offset = -1; 419 LPWSTR display_str = NULL; 420 int prefix_x, prefix_end; 421 HPEN hpen; 422 HPEN oldPen; 423 424 if (!lpString || cString <= 0) 425 return 0; 426 427 if (dwFlags & DT_NOPREFIX) /* Windows ignores this */ 428 { 429 TextOutW(hdc, x, y, lpString, cString); 430 GetTextExtentPointW(hdc, lpString, cString, &size); 431 return size.cx; 432 } 433 434 display_str = HeapAlloc(GetProcessHeap(), 0, (cString + 1) * sizeof(WCHAR)); 435 436 if (!display_str) 437 return 0; 438 439 while (i < cString) 440 { 441 if (lpString[i] == PREFIX || (iswspace(lpString[i]) && lpString[i] != L' ')) 442 { 443 if (i < cString - 1 && lpString[i + 1] == PREFIX) 444 display_str[j++] = lpString[i++]; 445 else 446 i++; 447 } 448 else 449 { 450 display_str[j++] = lpString[i++]; 451 } 452 } 453 454 display_str[j] = L'\0'; 455 len = wcslen(display_str); 456 457 if (!(dwFlags & DT_PREFIXONLY)) 458 TextOutW(hdc, x, y, display_str, len); 459 460 if (!(dwFlags & DT_HIDEPREFIX)) 461 { 462 463 for (i = 0; i < cString - 1; i++) 464 { 465 if (lpString[i] == PREFIX && lpString[i + 1] != PREFIX) 466 { 467 prefix_offset = i - prefix_count; 468 prefix_count++; 469 } 470 else if (lpString[i] == PREFIX && lpString[i + 1] == PREFIX) 471 { 472 i++; 473 } 474 } 475 476 GetTextMetricsW(hdc, &tm); 477 478 if (prefix_offset != -1) 479 { 480 GetTextExtentPointW(hdc, display_str, prefix_offset, &size); 481 prefix_x = x + size.cx; 482 GetTextExtentPointW(hdc, display_str, prefix_offset + 1, &size); 483 prefix_end = x + size.cx - 1; 484 hpen = CreatePen(PS_SOLID, 1, GetTextColor(hdc)); 485 oldPen = SelectObject(hdc, hpen); 486 MoveToEx(hdc, prefix_x, y + tm.tmAscent + 1, NULL); 487 LineTo(hdc, prefix_end, y + tm.tmAscent + 1); 488 SelectObject(hdc, oldPen); 489 DeleteObject(hpen); 490 } 491 } 492 493 GetTextExtentPointW(hdc, display_str, len + 1, &size); 494 HeapFree(GetProcessHeap(), 0, display_str); 495 496 return size.cx; 497 } 498