Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

682 lines
20 KiB

  1. /****************************** Module Header ******************************\
  2. * Module Name: drawtext.c
  3. *
  4. * Copyright (c) 1985 - 1999, Microsoft Corporation
  5. *
  6. * This module contains common text drawing functions.
  7. *
  8. * History:
  9. * 02-12-92 mikeke Moved Drawtext to the client side
  10. \***************************************************************************/
  11. #include "precomp.h"
  12. #pragma hdrstop
  13. #define CR 13
  14. #define LF 10
  15. #define DT_HFMTMASK 0x03
  16. /***************************************************************************\
  17. * IsMetaFile
  18. *
  19. * History:
  20. * 30-Nov-1992 mikeke Created
  21. \***************************************************************************/
  22. BOOL IsMetaFile(
  23. HDC hdc)
  24. {
  25. DWORD dwType = GetObjectType(hdc);
  26. return (dwType == OBJ_METAFILE ||
  27. dwType == OBJ_METADC ||
  28. dwType == OBJ_ENHMETAFILE ||
  29. dwType == OBJ_ENHMETADC);
  30. }
  31. /***************************************************************************\
  32. * DrawTextA (API)
  33. *
  34. * History:
  35. * 30-11-92 mikeke Created
  36. \***************************************************************************/
  37. CONST WCHAR gwszNullStr[] = L"";
  38. FUNCLOG6(LOG_GENERAL, int, DUMMYCALLINGTYPE, DrawTextExA, HDC, hdc, LPSTR, lpchText, int, cchText, LPRECT, lprc, UINT, format, LPDRAWTEXTPARAMS, lpdtp)
  39. int DrawTextExA(
  40. HDC hdc,
  41. LPSTR lpchText,
  42. int cchText,
  43. LPRECT lprc,
  44. UINT format,
  45. LPDRAWTEXTPARAMS lpdtp)
  46. {
  47. LPWSTR lpwstr;
  48. int iRet;
  49. int iUniString;
  50. WORD wCodePage = (WORD)GdiGetCodePage(hdc);
  51. if (cchText == -1) {
  52. // USER_AWCONV_COUNTSTRINGSZ does not count/convert trailing \0.
  53. cchText = USER_AWCONV_COUNTSTRINGSZ;
  54. } else if (cchText < -1) {
  55. return 0;
  56. }
  57. if ((iUniString = MBToWCSEx(wCodePage, lpchText, cchText, &lpwstr, -1, TRUE)) == 0) {
  58. if (cchText == USER_AWCONV_COUNTSTRINGSZ) {
  59. lpwstr = (LPWSTR)gwszNullStr;
  60. format &= ~DT_MODIFYSTRING;
  61. } else {
  62. return 0;
  63. }
  64. }
  65. /*
  66. * Grow the buffer to accomodate the ellipsis (see AddEllipsisAndDrawLine)
  67. */
  68. if (format & DT_MODIFYSTRING) {
  69. int iNewLen = (iUniString + CCHELLIPSIS + 1) * sizeof(*lpwstr);
  70. LPWSTR lpwstrNew = UserLocalReAlloc(lpwstr, iNewLen, HEAP_ZERO_MEMORY);
  71. if (lpwstrNew == NULL) {
  72. UserLocalFree((HANDLE)lpwstr);
  73. return FALSE;
  74. }
  75. lpwstr = lpwstrNew;
  76. }
  77. iRet = DrawTextExWorker(hdc, lpwstr, iUniString, lprc, format, lpdtp, GetTextCharset(hdc));
  78. if (format & DT_MODIFYSTRING) {
  79. /*
  80. * Note that if the buffer grew and the caller provided the string size,
  81. * then we won't return the additional characters... fixing this
  82. * might break some apps so let's leave it alone until some one complains
  83. */
  84. if (cchText < 0) {
  85. UserAssert(cchText == USER_AWCONV_COUNTSTRINGSZ);
  86. // Guess how many bytes we can put in the buffer...
  87. // We can safely assume the maximum bytes available.
  88. // At worst, even for DBCS, the buffer size required
  89. // will be smaller than or equal to the orignal size,
  90. // because some DBCS characters would be substituted
  91. // to SBC ".", which is one byte each.
  92. // On the other hand, the number of characters converted
  93. // is limited by both iUniString and cchText.
  94. //
  95. if (IS_DBCS_ENABLED()) {
  96. cchText = iUniString * DBCS_CHARSIZE;
  97. } else {
  98. cchText = iUniString * sizeof(CHAR);
  99. }
  100. }
  101. WCSToMBEx(wCodePage, lpwstr, iUniString, &lpchText, cchText, FALSE);
  102. }
  103. if (lpwstr != gwszNullStr) {
  104. UserLocalFree((HANDLE)lpwstr);
  105. }
  106. return iRet;
  107. }
  108. /***************************************************************************\
  109. * DrawTextW (API)
  110. *
  111. * History:
  112. * 30-11-92 mikeke Created
  113. \***************************************************************************/
  114. FUNCLOG5(LOG_GENERAL, int, DUMMYCALLINGTYPE, DrawTextW, HDC, hdc, LPCWSTR, lpchText, int, cchText, LPRECT, lprc, UINT, format)
  115. int DrawTextW(
  116. HDC hdc,
  117. LPCWSTR lpchText,
  118. int cchText,
  119. LPRECT lprc,
  120. UINT format)
  121. {
  122. DRAWTEXTPARAMS DTparams;
  123. LPDRAWTEXTPARAMS lpDTparams = NULL;
  124. /* v-ronaar: fix bug #24985
  125. * Disallow negative string lengths, except -1 (which has special meaning).
  126. */
  127. if (cchText < -1)
  128. return(0);
  129. if (format & DT_TABSTOP)
  130. {
  131. DTparams.cbSize = sizeof(DRAWTEXTPARAMS);
  132. DTparams.iLeftMargin = DTparams.iRightMargin = 0;
  133. DTparams.iTabLength = (format & 0xff00) >> 8;
  134. lpDTparams = &DTparams;
  135. format &= 0xffff00ff;
  136. }
  137. return DrawTextExW(hdc, (LPWSTR)lpchText, cchText, lprc, format, lpDTparams);
  138. }
  139. /***************************************************************************\
  140. * DrawTextA (API)
  141. *
  142. * History:
  143. * 30-11-92 mikeke Created
  144. \***************************************************************************/
  145. FUNCLOG5(LOG_GENERAL, int, DUMMYCALLINGTYPE, DrawTextA, HDC, hdc, LPCSTR, lpchText, int, cchText, LPRECT, lprc, UINT, format)
  146. int DrawTextA(
  147. HDC hdc,
  148. LPCSTR lpchText,
  149. int cchText,
  150. LPRECT lprc,
  151. UINT format)
  152. {
  153. DRAWTEXTPARAMS DTparams;
  154. LPDRAWTEXTPARAMS lpDTparams = NULL;
  155. /* v-ronaar: fix bug #24985
  156. * Disallow negative string lengths, except -1 (which has special meaning).
  157. */
  158. if (cchText < -1)
  159. return(0);
  160. if (format & DT_TABSTOP) {
  161. DTparams.cbSize = sizeof(DRAWTEXTPARAMS);
  162. DTparams.iLeftMargin = DTparams.iRightMargin = 0;
  163. DTparams.iTabLength = (format & 0xff00) >> 8;
  164. lpDTparams = &DTparams;
  165. format &= 0xffff00ff;
  166. }
  167. return DrawTextExA(hdc, (LPSTR)lpchText, cchText, lprc, format, lpDTparams);
  168. }
  169. /***************************************************************************\
  170. * ClientTabTheTextOutForWimps
  171. *
  172. * effects: Outputs the tabbed text if fDrawTheText is TRUE and returns the
  173. * textextent of the tabbed text.
  174. *
  175. * nCount Count of bytes in string
  176. * nTabPositions Count of tabstops in tabstop array
  177. * lpintTabStopPositions Tab stop positions in pixels
  178. * iTabOrigin Tab stops are with respect to this
  179. *
  180. * History:
  181. * 19-Jan-1993 mikeke Client side
  182. * 13-Sep-1996 GregoryW This routine now calls the LPK(s) to handle text out.
  183. * If no LPKs are installed, this defaults to calling
  184. * UserLpkTabbedTextOut (identical behavior to what we
  185. * had before supporting LPKs).
  186. \***************************************************************************/
  187. LONG TabTextOut(
  188. HDC hdc,
  189. int x,
  190. int y,
  191. LPCWSTR lpstring,
  192. int nCount,
  193. int nTabPositions,
  194. CONST INT *lpTabPositions,
  195. int iTabOrigin,
  196. BOOL fDrawTheText,
  197. int iCharset)
  198. {
  199. int cxCharWidth;
  200. int cyCharHeight = 0;
  201. if (nCount == -1 && lpstring) {
  202. nCount = wcslen(lpstring);
  203. }
  204. if (!lpstring || nCount < 0 || nTabPositions < 0)
  205. return 0;
  206. // Check if it is SysFont AND the mapping mode is MM_TEXT;
  207. // Fix made in connection with Bug #8717 --02-01-90 --SANKAR--
  208. if (IsSysFontAndDefaultMode(hdc))
  209. {
  210. cxCharWidth = gpsi->cxSysFontChar;
  211. cyCharHeight = gpsi->cySysFontChar;
  212. } else {
  213. cxCharWidth = GdiGetCharDimensions(hdc, NULL, &cyCharHeight);
  214. if (cxCharWidth == 0) {
  215. RIPMSG0(RIP_WARNING, "TabTextOut: GdiGetCharDimensions failed");
  216. return 0;
  217. }
  218. }
  219. return (*fpLpkTabbedTextOut)(hdc, x, y, lpstring, nCount, nTabPositions,
  220. lpTabPositions, iTabOrigin, fDrawTheText,
  221. cxCharWidth, cyCharHeight, iCharset);
  222. }
  223. LONG UserLpkTabbedTextOut(
  224. HDC hdc,
  225. int x,
  226. int y,
  227. LPCWSTR lpstring,
  228. int nCount,
  229. int nTabPositions,
  230. CONST INT *lpTabPositions,
  231. int iTabOrigin,
  232. BOOL fDrawTheText,
  233. int cxCharWidth,
  234. int cyCharHeight,
  235. int iCharset)
  236. {
  237. SIZE textextent, viewextent, windowextent;
  238. int initialx = x;
  239. int cch;
  240. LPCWSTR lp;
  241. int iOneTab = 0;
  242. RECT rc;
  243. UINT uOpaque = (GetBkMode(hdc) == OPAQUE) ? ETO_OPAQUE : 0;
  244. BOOL fStrStart = TRUE;
  245. int ySign = 1; //Assume y increases in down direction.
  246. UNREFERENCED_PARAMETER(iCharset); //Needed by lpk, but not us
  247. /*
  248. * If no tabstop positions are specified, then use a default of 8 system
  249. * font ave char widths or use the single fixed tab stop.
  250. */
  251. if (!lpTabPositions) {
  252. // no tab stops specified -- default to a tab stop every 8 characters
  253. iOneTab = 8 * cxCharWidth;
  254. } else if (nTabPositions == 1) {
  255. // one tab stop specified -- treat value as the tab increment, one
  256. // tab stop every increment
  257. iOneTab = lpTabPositions[0];
  258. if (!iOneTab)
  259. iOneTab = 1;
  260. }
  261. // Calculate if the y increases or decreases in the down direction using
  262. // the ViewPortExtent and WindowExtents.
  263. // If this call fails, hdc must be invalid
  264. if (!GetViewportExtEx(hdc, &viewextent))
  265. return 0;
  266. GetWindowExtEx(hdc, &windowextent);
  267. if ((viewextent.cy ^ windowextent.cy) & 0x80000000)
  268. ySign = -1;
  269. rc.left = initialx;
  270. rc.top = y;
  271. rc.bottom = rc.top + (ySign * cyCharHeight);
  272. while (TRUE) {
  273. // count the number of characters until the next tab character
  274. // this set of characters (substring) will be the working set for
  275. // each iteration of this loop
  276. for (cch = nCount, lp = lpstring; cch && (*lp != TEXT('\t')); lp++, cch--)
  277. {
  278. }
  279. // Compute the number of characters to be drawn with textout.
  280. cch = nCount - cch;
  281. // Compute the number of characters remaining.
  282. nCount -= cch + 1;
  283. // get height and width of substring
  284. if (cch == 0) {
  285. textextent.cx = 0;
  286. textextent.cy = cyCharHeight;
  287. } else
  288. GetTextExtentPointW(hdc, lpstring, cch, &textextent);
  289. if (fStrStart)
  290. // first iteration should just spit out the first substring
  291. // no tabbing occurs until the first tab character is encountered
  292. fStrStart = FALSE;
  293. else
  294. {
  295. // not the first iteration -- tab accordingly
  296. int xTab;
  297. int i;
  298. if (!iOneTab)
  299. {
  300. // look thru tab stop array for next tab stop after existing
  301. // text to put this substring
  302. for (i = 0; i < nTabPositions; i++)
  303. {
  304. xTab = lpTabPositions[i];
  305. if (xTab < 0)
  306. // calc length needed to use this right justified tab
  307. xTab = (iTabOrigin - xTab) - textextent.cx;
  308. else
  309. // calc length needed to use this left justified tab
  310. xTab = iTabOrigin + xTab;
  311. if (x < xTab)
  312. {
  313. // we found a tab with enough room -- let's use it
  314. x = xTab;
  315. break;
  316. }
  317. }
  318. if (i == nTabPositions)
  319. // we've exhausted all of the given tab positions
  320. // go back to default of a tab stop every 8 characters
  321. iOneTab = 8 * cxCharWidth;
  322. }
  323. // we have to recheck iOneTab here (instead of just saying "else")
  324. // because iOneTab will be set if we've run out of tab stops
  325. if (iOneTab)
  326. {
  327. if (iOneTab < 0)
  328. {
  329. // calc next available right justified tab stop
  330. xTab = x + textextent.cx - iTabOrigin;
  331. xTab = ((xTab / iOneTab) * iOneTab) - iOneTab - textextent.cx + iTabOrigin;
  332. }
  333. else
  334. {
  335. // calc next available left justified tab stop
  336. xTab = x - iTabOrigin;
  337. xTab = ((xTab / iOneTab) * iOneTab) + iOneTab + iTabOrigin;
  338. }
  339. x = xTab;
  340. }
  341. }
  342. if (fDrawTheText) {
  343. /*
  344. * Output all text up to the tab (or end of string) and get its
  345. * extent.
  346. */
  347. rc.right = x + textextent.cx;
  348. ExtTextOutW(
  349. hdc, x, y, uOpaque, &rc, (LPWSTR)lpstring,
  350. cch, NULL);
  351. rc.left = rc.right;
  352. }
  353. // Skip over the tab and the characters we just drew.
  354. x += textextent.cx;
  355. // Skip over the characters we just drew.
  356. lpstring += cch;
  357. // See if we have more to draw OR see if this string ends in
  358. // a tab character that needs to be drawn.
  359. if((nCount > 0) || ((nCount == 0) && (*lpstring == TEXT('\t'))))
  360. {
  361. lpstring++; // Skip over the tab
  362. continue;
  363. }
  364. else
  365. break; // Break from the loop.
  366. }
  367. return MAKELONG((x - initialx), (short)textextent.cy);
  368. }
  369. /***************************************************************************\
  370. * TabbedTextOutW
  371. *
  372. * effects: Outputs the tabbed text and returns the
  373. * textextent of the tabbed text.
  374. *
  375. * nCount Count of bytes in string
  376. * nTabPositions Count of tabstops in tabstop array
  377. * lpintTabStopPositions Tab stop positions in pixels
  378. * iTabOrigin Tab stops are with respect to this
  379. *
  380. * History:
  381. * 19-Jan-1993 mikeke Client side
  382. \***************************************************************************/
  383. LONG TabbedTextOutW(
  384. HDC hdc,
  385. int x,
  386. int y,
  387. LPCWSTR lpstring,
  388. int cchChars,
  389. int nTabPositions,
  390. CONST INT *lpintTabStopPositions,
  391. int iTabOrigin)
  392. {
  393. return TabTextOut(hdc, x, y, lpstring, cchChars,
  394. nTabPositions, lpintTabStopPositions, iTabOrigin, TRUE, -1);
  395. }
  396. /***************************************************************************\
  397. * TabbedTextOutA (API)
  398. *
  399. * History:
  400. * 30-11-92 mikeke Created
  401. \***************************************************************************/
  402. LONG TabbedTextOutA(
  403. HDC hdc,
  404. int x,
  405. int y,
  406. LPCSTR pString,
  407. int chCount,
  408. int nTabPositions,
  409. CONST INT *pnTabStopPositions,
  410. int nTabOrigin)
  411. {
  412. LPWSTR lpwstr;
  413. BOOL bRet;
  414. WORD wCodePage = (WORD)GdiGetCodePage(hdc);
  415. int iUniString;
  416. if (chCount == -1) {
  417. chCount = USER_AWCONV_COUNTSTRINGSZ;
  418. }
  419. if ((iUniString = MBToWCSEx(wCodePage, pString, chCount, &lpwstr, -1, TRUE)) == 0) {
  420. if (chCount == USER_AWCONV_COUNTSTRINGSZ) {
  421. lpwstr = (LPWSTR)gwszNullStr;
  422. } else {
  423. return FALSE;
  424. }
  425. }
  426. bRet = TabTextOut(
  427. hdc, x, y, lpwstr, iUniString, nTabPositions,
  428. pnTabStopPositions, nTabOrigin, TRUE, GetTextCharset(hdc));
  429. if (lpwstr != gwszNullStr) {
  430. UserLocalFree((HANDLE)lpwstr);
  431. }
  432. return bRet;
  433. }
  434. DWORD GetTabbedTextExtentW(
  435. HDC hdc,
  436. LPCWSTR pString,
  437. int chCount,
  438. int nTabPositions,
  439. CONST INT *pnTabStopPositions)
  440. {
  441. return TabTextOut(hdc, 0, 0, pString, chCount,
  442. nTabPositions, pnTabStopPositions, 0, FALSE, -1);
  443. }
  444. DWORD GetTabbedTextExtentA(
  445. HDC hdc,
  446. LPCSTR pString,
  447. int chCount,
  448. int nTabPositions,
  449. CONST INT *pnTabStopPositions)
  450. {
  451. LPWSTR lpwstr;
  452. BOOL bRet;
  453. WORD wCodePage = (WORD)GdiGetCodePage(hdc);
  454. int iUniString;
  455. if (chCount == -1) {
  456. chCount = USER_AWCONV_COUNTSTRINGSZ;
  457. }
  458. if ((iUniString = MBToWCSEx(wCodePage, pString, chCount, &lpwstr, -1, TRUE)) == 0) {
  459. if (chCount == USER_AWCONV_COUNTSTRINGSZ) {
  460. lpwstr = (LPWSTR)gwszNullStr;
  461. } else {
  462. return FALSE;
  463. }
  464. }
  465. bRet = TabTextOut(hdc, 0, 0, lpwstr, iUniString,
  466. nTabPositions, pnTabStopPositions, 0, FALSE, GetTextCharset(hdc));
  467. if (lpwstr != gwszNullStr) {
  468. UserLocalFree((HANDLE)lpwstr);
  469. }
  470. return bRet;
  471. }
  472. /***************************************************************************\
  473. * PSMTextOut
  474. *
  475. * Outputs the text and puts and _ below the character with an &
  476. * before it. Note that this routine isn't used for menus since menus
  477. * have their own special one so that it is specialized and faster...
  478. *
  479. * History:
  480. * 11-13-90 JimA Ported to NT.
  481. * 30-Nov-1992 mikeke Client side version
  482. * 7-Apr-1998 MCostea Added dwFlags
  483. \***************************************************************************/
  484. void PSMTextOut(
  485. HDC hdc,
  486. int xLeft,
  487. int yTop,
  488. LPWSTR lpsz,
  489. int cch,
  490. DWORD dwFlags)
  491. {
  492. /*
  493. * By default this is just a call to UserLpkPSMTextOut. If an
  494. * LPK is installed, this calls out to the LPK. The LPK calls
  495. * UserLpkPSMTextOut, if necessary.
  496. */
  497. (*fpLpkPSMTextOut)(hdc, xLeft, yTop, lpsz, cch, dwFlags);
  498. return;
  499. }
  500. /***************************************************************************\
  501. * UserLpkPSMTextOut
  502. *
  503. * NOTE: A very similar routine (xxxPSMTextOut) exists on the kernel
  504. * side in text.c. Any changes to this routine most likely need
  505. * to be made in xxxPSMTextOut as well.
  506. *
  507. \***************************************************************************/
  508. FUNCLOGVOID6(LOG_GENERAL, DUMMYCALLINGTYPE, UserLpkPSMTextOut, HDC, hdc, int, xLeft, int, yTop, LPWSTR, lpsz, int, cch, DWORD, dwFlags)
  509. void UserLpkPSMTextOut(
  510. HDC hdc,
  511. int xLeft,
  512. int yTop,
  513. LPWSTR lpsz,
  514. int cch,
  515. DWORD dwFlags)
  516. {
  517. int cx;
  518. LONG textsize, result;
  519. WCHAR achWorkBuffer[255];
  520. WCHAR *pchOut = achWorkBuffer;
  521. TEXTMETRICW textMetric;
  522. SIZE size;
  523. RECT rc;
  524. COLORREF color;
  525. if (cch > sizeof(achWorkBuffer)/sizeof(WCHAR)) {
  526. pchOut = (WCHAR*)UserLocalAlloc(HEAP_ZERO_MEMORY, (cch+1) * sizeof(WCHAR));
  527. if (pchOut == NULL)
  528. return;
  529. }
  530. result = GetPrefixCount(lpsz, cch, pchOut, cch);
  531. /*
  532. * DT_PREFIXONLY is a new 5.0 option used when switching from keyboard cues off
  533. * to on.
  534. */
  535. if (!(dwFlags & DT_PREFIXONLY)) {
  536. TextOutW(hdc, xLeft, yTop, pchOut, cch - HIWORD(result));
  537. }
  538. /*
  539. * Any true prefix characters to underline?
  540. */
  541. if (LOWORD(result) == 0xFFFF || dwFlags & DT_HIDEPREFIX) {
  542. if (pchOut != achWorkBuffer)
  543. UserLocalFree(pchOut);
  544. return;
  545. }
  546. if (!GetTextMetricsW(hdc, &textMetric)) {
  547. textMetric.tmOverhang = 0;
  548. textMetric.tmAscent = 0;
  549. }
  550. /*
  551. * For proportional fonts, find starting point of underline.
  552. */
  553. if (LOWORD(result) != 0) {
  554. /*
  555. * How far in does underline start (if not at 0th byte.).
  556. */
  557. GetTextExtentPointW(hdc, pchOut, LOWORD(result), &size);
  558. xLeft += size.cx;
  559. /*
  560. * Adjust starting point of underline if not at first char and there is
  561. * an overhang. (Italics or bold fonts.)
  562. */
  563. xLeft = xLeft - textMetric.tmOverhang;
  564. }
  565. /*
  566. * Adjust for proportional font when setting the length of the underline and
  567. * height of text.
  568. */
  569. GetTextExtentPointW(hdc, pchOut + LOWORD(result), 1, &size);
  570. textsize = size.cx;
  571. /*
  572. * Find the width of the underline character. Just subtract out the overhang
  573. * divided by two so that we look better with italic fonts. This is not
  574. * going to effect embolded fonts since their overhang is 1.
  575. */
  576. cx = LOWORD(textsize) - textMetric.tmOverhang / 2;
  577. /*
  578. * Get height of text so that underline is at bottom.
  579. */
  580. yTop += textMetric.tmAscent + 1;
  581. /*
  582. * Draw the underline using the foreground color.
  583. */
  584. SetRect(&rc, xLeft, yTop, xLeft+cx, yTop+1);
  585. color = SetBkColor(hdc, GetTextColor(hdc));
  586. ExtTextOutW(hdc, xLeft, yTop, ETO_OPAQUE, &rc, TEXT(""), 0, NULL);
  587. SetBkColor(hdc, color);
  588. if (pchOut != achWorkBuffer) {
  589. UserLocalFree(pchOut);
  590. }
  591. }