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.

901 lines
32 KiB

  1. //
  2. // fontlnkv.cpp
  3. //
  4. //
  5. // Vertical version DrawTextW()
  6. //
  7. #include "private.h"
  8. #include "flshare.h"
  9. #include "fontlink.h"
  10. #include "xstring.h"
  11. #include "osver.h"
  12. #include "globals.h"
  13. typedef struct tagDRAWTEXTPARAMSVERT
  14. {
  15. UINT cbSize;
  16. int iTabLength;
  17. int iTopMargin;
  18. int iBottomMargin;
  19. UINT uiLengthDrawn;
  20. } DRAWTEXTPARAMSVERT, FAR *LPDRAWTEXTPARAMSVERT;
  21. // Outputs the text and puts and _ below the character with an &
  22. // before it. Note that this routine isn't used for menus since menus
  23. // have their own special one so that it is specialized and faster...
  24. void PSMTextOutVert(
  25. HDC hdc,
  26. int xRight,
  27. int yTop,
  28. LPWSTR lpsz,
  29. int cch,
  30. DWORD dwFlags)
  31. {
  32. int cy;
  33. LONG textsize, result;
  34. WCHAR achWorkBuffer[255];
  35. WCHAR *pchOut = achWorkBuffer;
  36. TEXTMETRIC textMetric;
  37. SIZE size;
  38. RECT rc;
  39. COLORREF color;
  40. if (dwFlags & DT_NOPREFIX)
  41. {
  42. FLTextOutW(hdc, xRight, yTop, lpsz, cch);
  43. return;
  44. }
  45. if (cch > sizeof(achWorkBuffer)/sizeof(WCHAR))
  46. {
  47. pchOut = (WCHAR*)LocalAlloc(LPTR, (cch+1) * sizeof(WCHAR));
  48. if (pchOut == NULL)
  49. return;
  50. }
  51. result = GetPrefixCount(lpsz, cch, pchOut, cch);
  52. // DT_PREFIXONLY is a new 5.0 option used when switching from keyboard cues off to on.
  53. if (!(dwFlags & DT_PREFIXONLY))
  54. FLTextOutW(hdc, xRight, yTop, pchOut, cch - HIWORD(result));
  55. // Any true prefix characters to underline?
  56. if (LOWORD(result) == 0xFFFF || dwFlags & DT_HIDEPREFIX)
  57. {
  58. if (pchOut != achWorkBuffer)
  59. LocalFree(pchOut);
  60. return;
  61. }
  62. if (!GetTextMetrics(hdc, &textMetric))
  63. {
  64. textMetric.tmOverhang = 0;
  65. textMetric.tmAscent = 0;
  66. }
  67. // For proportional fonts, find starting point of underline.
  68. if (LOWORD(result) != 0)
  69. {
  70. // How far in does underline start (if not at 0th byte.).
  71. FLGetTextExtentPoint32(hdc, pchOut, LOWORD(result), &size);
  72. xRight += size.cy;
  73. // Adjust starting point of underline if not at first char and there is
  74. // an overhang. (Italics or bold fonts.)
  75. yTop = yTop - textMetric.tmOverhang;
  76. }
  77. // Adjust for proportional font when setting the length of the underline and
  78. // height of text.
  79. FLGetTextExtentPoint32(hdc, pchOut + LOWORD(result), 1, &size);
  80. textsize = size.cx;
  81. // Find the width of the underline character. Just subtract out the overhang
  82. // divided by two so that we look better with italic fonts. This is not
  83. // going to effect embolded fonts since their overhang is 1.
  84. cy = LOWORD(textsize) - textMetric.tmOverhang / 2;
  85. // Get height of text so that underline is at bottom.
  86. xRight -= textMetric.tmAscent + 1;
  87. // Draw the underline using the foreground color.
  88. SetRect(&rc, xRight, yTop, xRight+1, yTop+cy);
  89. color = SetBkColor(hdc, GetTextColor(hdc));
  90. FLExtTextOutW(hdc, xRight, yTop, ETO_OPAQUE, &rc, L"", 0, NULL);
  91. SetBkColor(hdc, color);
  92. if (pchOut != achWorkBuffer)
  93. LocalFree(pchOut);
  94. }
  95. int DT_GetExtentMinusPrefixesVert(HDC hdc, LPCWSTR lpchStr, int cchCount, UINT wFormat, int iOverhang)
  96. {
  97. int iPrefixCount;
  98. int cxPrefixes = 0;
  99. WCHAR PrefixChar = CH_PREFIX;
  100. SIZE size;
  101. if (!(wFormat & DT_NOPREFIX) &&
  102. (iPrefixCount = HIWORD(GetPrefixCount(lpchStr, cchCount, NULL, 0))))
  103. {
  104. // Kanji Windows has three shortcut prefixes...
  105. if (IsOnDBCS())
  106. {
  107. // 16bit apps compatibility
  108. cxPrefixes = KKGetPrefixWidth(hdc, lpchStr, cchCount) - (iPrefixCount * iOverhang);
  109. }
  110. else
  111. {
  112. cxPrefixes = FLGetTextExtentPoint32(hdc, &PrefixChar, 1, &size);
  113. cxPrefixes = size.cx - iOverhang;
  114. cxPrefixes *= iPrefixCount;
  115. }
  116. }
  117. FLGetTextExtentPoint32(hdc, lpchStr, cchCount, &size);
  118. return (size.cx - cxPrefixes);
  119. }
  120. // This will draw the given string in the given location without worrying
  121. // about the left/right justification. Gets the extent and returns it.
  122. // If fDraw is TRUE and if NOT DT_CALCRECT, this draws the text.
  123. // NOTE: This returns the extent minus Overhang.
  124. int DT_DrawStrVert(HDC hdc, int xRight, int yTop, LPCWSTR lpchStr,
  125. int cchCount, BOOL fDraw, UINT wFormat,
  126. LPDRAWTEXTDATAVERT lpDrawInfo)
  127. {
  128. LPCWSTR lpch;
  129. int iLen;
  130. int cyExtent;
  131. int yOldLeft = yTop; // Save the xRight given to compute the extent later
  132. int yTabLength = lpDrawInfo->cyTabLength;
  133. int iTabOrigin = lpDrawInfo->rcFormat.left;
  134. // Check if the tabs need to be expanded
  135. if (wFormat & DT_EXPANDTABS)
  136. {
  137. while (cchCount)
  138. {
  139. // Look for a tab
  140. for (iLen = 0, lpch = lpchStr; iLen < cchCount; iLen++)
  141. if(*lpch++ == L'\t')
  142. break;
  143. // Draw text, if any, upto the tab
  144. if (iLen)
  145. {
  146. // Draw the substring taking care of the prefixes.
  147. if (fDraw && !(wFormat & DT_CALCRECT)) // Only if we need to draw text
  148. PSMTextOutVert(hdc, xRight, yTop, (LPWSTR)lpchStr, iLen, wFormat);
  149. // Get the extent of this sub string and add it to xRight.
  150. yTop += DT_GetExtentMinusPrefixesVert(hdc, lpchStr, iLen, wFormat, lpDrawInfo->cyOverhang) - lpDrawInfo->cyOverhang;
  151. }
  152. //if a TAB was found earlier, calculate the start of next sub-string.
  153. if (iLen < cchCount)
  154. {
  155. iLen++; // Skip the tab
  156. if (yTabLength) // Tab length could be zero
  157. yTop = (((yTop - iTabOrigin)/yTabLength) + 1)*yTabLength + iTabOrigin;
  158. }
  159. // Calculate the details of the string that remains to be drawn.
  160. cchCount -= iLen;
  161. lpchStr = lpch;
  162. }
  163. cyExtent = yTop - yOldLeft;
  164. }
  165. else
  166. {
  167. // If required, draw the text
  168. if (fDraw && !(wFormat & DT_CALCRECT))
  169. PSMTextOutVert(hdc, xRight, yTop, (LPWSTR)lpchStr, cchCount, wFormat);
  170. // Compute the extent of the text.
  171. cyExtent = DT_GetExtentMinusPrefixesVert(hdc, lpchStr, cchCount, wFormat, lpDrawInfo->cyOverhang) - lpDrawInfo->cyOverhang;
  172. }
  173. return cyExtent;
  174. }
  175. // This function draws one complete line with proper justification
  176. void DT_DrawJustifiedLineVert(HDC hdc, int xRight, LPCWSTR lpchLineSt, int cchCount, UINT wFormat, LPDRAWTEXTDATAVERT lpDrawInfo)
  177. {
  178. LPRECT lprc;
  179. int cyExtent;
  180. int yTop;
  181. lprc = &(lpDrawInfo->rcFormat);
  182. yTop = lprc->top;
  183. // Handle the special justifications (right or centered) properly.
  184. if (wFormat & (DT_CENTER | DT_RIGHT))
  185. {
  186. cyExtent = DT_DrawStrVert(hdc, xRight, yTop, lpchLineSt, cchCount, FALSE, wFormat, lpDrawInfo)
  187. + lpDrawInfo->cyOverhang;
  188. if(wFormat & DT_CENTER)
  189. yTop = lprc->top + (((lprc->bottom - lprc->top) - cyExtent) >> 1);
  190. else
  191. yTop = lprc->bottom - cyExtent;
  192. }
  193. else
  194. yTop = lprc->top;
  195. // Draw the whole line.
  196. cyExtent = DT_DrawStrVert(hdc, xRight, yTop, lpchLineSt, cchCount, TRUE, wFormat, lpDrawInfo)
  197. + lpDrawInfo->cyOverhang;
  198. if (cyExtent > lpDrawInfo->cyMaxExtent)
  199. lpDrawInfo->cyMaxExtent = cyExtent;
  200. }
  201. // This is called at the begining of DrawText(); This initializes the
  202. // DRAWTEXTDATAVERT structure passed to this function with all the required info.
  203. BOOL DT_InitDrawTextInfoVert(
  204. HDC hdc,
  205. LPRECT lprc,
  206. UINT wFormat,
  207. LPDRAWTEXTDATAVERT lpDrawInfo,
  208. LPDRAWTEXTPARAMSVERT lpDTparams)
  209. {
  210. SIZE sizeViewPortExt = {0, 0}, sizeWindowExt = {0, 0};
  211. TEXTMETRIC tm;
  212. LPRECT lprcDest;
  213. int iTabLength = 8; // Default Tab length is 8 characters.
  214. int iTopMargin;
  215. int iBottomMargin;
  216. if (lpDTparams)
  217. {
  218. // Only if DT_TABSTOP flag is mentioned, we must use the iTabLength field.
  219. if (wFormat & DT_TABSTOP)
  220. iTabLength = lpDTparams->iTabLength;
  221. iTopMargin = lpDTparams->iTopMargin;
  222. iBottomMargin = lpDTparams->iBottomMargin;
  223. }
  224. else
  225. iTopMargin = iBottomMargin = 0;
  226. // Get the View port and Window extents for the given DC
  227. // If this call fails, hdc must be invalid
  228. if (!GetViewportExtEx(hdc, &sizeViewPortExt))
  229. return FALSE;
  230. GetWindowExtEx(hdc, &sizeWindowExt);
  231. // For the current mapping mode, find out the sign of x from left to right.
  232. lpDrawInfo->iXSign = (((sizeViewPortExt.cx ^ sizeWindowExt.cx) & 0x80000000) ? -1 : 1);
  233. // For the current mapping mode, find out the sign of y from top to bottom.
  234. lpDrawInfo->iYSign = (((sizeViewPortExt.cy ^ sizeWindowExt.cy) & 0x80000000) ? -1 : 1);
  235. // Calculate the dimensions of the current font in this DC.
  236. GetTextMetrics(hdc, &tm);
  237. // cxLineHeight is in pixels (This will be signed).
  238. lpDrawInfo->cxLineHeight = (tm.tmHeight +
  239. ((wFormat & DT_EXTERNALLEADING) ? tm.tmExternalLeading : 0)) * lpDrawInfo->iXSign;
  240. // cyTabLength is the tab length in pixels (This will not be signed)
  241. lpDrawInfo->cyTabLength = tm.tmAveCharWidth * iTabLength;
  242. // Set the cyOverhang
  243. lpDrawInfo->cyOverhang = tm.tmOverhang;
  244. // Set up the format rectangle based on the margins.
  245. lprcDest = &(lpDrawInfo->rcFormat);
  246. *lprcDest = *lprc;
  247. // We need to do the following only if the margins are given
  248. if (iTopMargin | iBottomMargin)
  249. {
  250. lprcDest->top += iTopMargin * lpDrawInfo->iYSign;
  251. lprcDest->bottom -= (lpDrawInfo->cyBottomMargin = iBottomMargin * lpDrawInfo->iYSign);
  252. }
  253. else
  254. lpDrawInfo->cyBottomMargin = 0; // Initialize to zero.
  255. // cyMaxWidth is unsigned.
  256. lpDrawInfo->cyMaxWidth = (lprcDest->bottom - lprcDest->top) * lpDrawInfo->iYSign;
  257. lpDrawInfo->cyMaxExtent = 0; // Initialize this to zero.
  258. return TRUE;
  259. }
  260. // A word needs to be broken across lines and this finds out where to break it.
  261. LPCWSTR DT_BreakAWordVert(HDC hdc, LPCWSTR lpchText, int iLength, int iWidth, UINT wFormat, int iOverhang)
  262. {
  263. int iLow = 0, iHigh = iLength;
  264. int iNew;
  265. while ((iHigh - iLow) > 1)
  266. {
  267. iNew = iLow + (iHigh - iLow)/2;
  268. if(DT_GetExtentMinusPrefixesVert(hdc, lpchText, iNew, wFormat, iOverhang) > iWidth)
  269. iHigh = iNew;
  270. else
  271. iLow = iNew;
  272. }
  273. // If the width is too low, we must print atleast one char per line.
  274. // Else, we will be in an infinite loop.
  275. if(!iLow && iLength)
  276. iLow = 1;
  277. return (lpchText+iLow);
  278. }
  279. // This finds out the location where we can break a line.
  280. // Returns LPCSTR to the begining of next line.
  281. // Also returns via lpiLineLength, the length of the current line.
  282. // NOTE: (lpstNextLineStart - lpstCurrentLineStart) is not equal to the
  283. // line length; This is because, we exclude some white spaces at the begining
  284. // and/or end of lines; Also, CR/LF is excluded from the line length.
  285. LPWSTR DT_GetLineBreakVert(
  286. HDC hdc,
  287. LPCWSTR lpchLineStart,
  288. int cchCount,
  289. DWORD dwFormat,
  290. LPINT lpiLineLength,
  291. LPDRAWTEXTDATAVERT lpDrawInfo)
  292. {
  293. LPCWSTR lpchText, lpchEnd, lpch, lpchLineEnd;
  294. int cxStart, cyExtent, cyNewExtent;
  295. BOOL fAdjustWhiteSpaces = FALSE;
  296. WCHAR ch;
  297. cxStart = lpDrawInfo->rcFormat.left;
  298. cyExtent = cyNewExtent = 0;
  299. lpchText = lpchLineStart;
  300. lpchEnd = lpchLineStart + cchCount;
  301. lpch = lpchEnd;
  302. lpchLineEnd = lpchEnd;
  303. while(lpchText < lpchEnd)
  304. {
  305. lpchLineEnd = lpch = GetNextWordbreak(lpchText, lpchEnd, dwFormat, NULL);
  306. // DT_DrawStrVert does not return the overhang; Otherwise we will end up
  307. // adding one overhang for every word in the string.
  308. // For simulated Bold fonts, the summation of extents of individual
  309. // words in a line is greater than the extent of the whole line. So,
  310. // always calculate extent from the LineStart.
  311. // BUGTAG: #6054 -- Win95B -- SANKAR -- 3/9/95 --
  312. cyNewExtent = DT_DrawStrVert(hdc, cxStart, 0, lpchLineStart, (int)(((PBYTE)lpch - (PBYTE)lpchLineStart)/sizeof(WCHAR)),
  313. FALSE, dwFormat, lpDrawInfo);
  314. if ((dwFormat & DT_WORDBREAK) && ((cyNewExtent + lpDrawInfo->cyOverhang) > lpDrawInfo->cyMaxWidth))
  315. {
  316. // Are there more than one word in this line?
  317. if (lpchText != lpchLineStart)
  318. {
  319. lpchLineEnd = lpch = lpchText;
  320. fAdjustWhiteSpaces = TRUE;
  321. }
  322. else
  323. {
  324. //One word is longer than the maximum width permissible.
  325. //See if we are allowed to break that single word.
  326. if((dwFormat & DT_EDITCONTROL) && !(dwFormat & DT_WORD_ELLIPSIS))
  327. {
  328. lpchLineEnd = lpch = DT_BreakAWordVert(hdc, lpchText, (int)(((PBYTE)lpch - (PBYTE)lpchText)/sizeof(WCHAR)),
  329. lpDrawInfo->cyMaxWidth - cyExtent, dwFormat, lpDrawInfo->cyOverhang); //Break that word
  330. //Note: Since we broke in the middle of a word, no need to
  331. // adjust for white spaces.
  332. }
  333. else
  334. {
  335. fAdjustWhiteSpaces = TRUE;
  336. // Check if we need to end this line with ellipsis
  337. if(dwFormat & DT_WORD_ELLIPSIS)
  338. {
  339. // Don't do this if already at the end of the string.
  340. if (lpch < lpchEnd)
  341. {
  342. // If there are CR/LF at the end, skip them.
  343. if ((ch = *lpch) == CR || ch == LF)
  344. {
  345. if ((++lpch < lpchEnd) && (*lpch == (WCHAR)(ch ^ (LF ^ CR))))
  346. lpch++;
  347. fAdjustWhiteSpaces = FALSE;
  348. }
  349. }
  350. }
  351. }
  352. }
  353. // Well! We found a place to break the line. Let us break from this loop;
  354. break;
  355. }
  356. else
  357. {
  358. // Don't do this if already at the end of the string.
  359. if (lpch < lpchEnd)
  360. {
  361. if ((ch = *lpch) == CR || ch == LF)
  362. {
  363. if ((++lpch < lpchEnd) && (*lpch == (WCHAR)(ch ^ (LF ^ CR))))
  364. lpch++;
  365. fAdjustWhiteSpaces = FALSE;
  366. break;
  367. }
  368. }
  369. }
  370. // Point at the beginning of the next word.
  371. lpchText = lpch;
  372. cyExtent = cyNewExtent;
  373. }
  374. // Calculate the length of current line.
  375. *lpiLineLength = (INT)((PBYTE)lpchLineEnd - (PBYTE)lpchLineStart)/sizeof(WCHAR);
  376. // Adjust the line length and lpch to take care of spaces.
  377. if(fAdjustWhiteSpaces && (lpch < lpchEnd))
  378. lpch = DT_AdjustWhiteSpaces(lpch, lpiLineLength, dwFormat);
  379. // return the begining of next line;
  380. return (LPWSTR)lpch;
  381. }
  382. // This function checks whether the given string fits within the given
  383. // width or we need to add end-ellipse. If it required end-ellipses, it
  384. // returns TRUE and it returns the number of characters that are saved
  385. // in the given string via lpCount.
  386. BOOL NeedsEndEllipsisVert(
  387. HDC hdc,
  388. LPCWSTR lpchText,
  389. LPINT lpCount,
  390. LPDRAWTEXTDATAVERT lpDTdata,
  391. UINT wFormat)
  392. {
  393. int cchText;
  394. int ichMin, ichMax, ichMid;
  395. int cyMaxWidth;
  396. int iOverhang;
  397. int cyExtent;
  398. SIZE size;
  399. cchText = *lpCount; // Get the current count.
  400. if (cchText == 0)
  401. return FALSE;
  402. cyMaxWidth = lpDTdata->cyMaxWidth;
  403. iOverhang = lpDTdata->cyOverhang;
  404. cyExtent = DT_GetExtentMinusPrefixesVert(hdc, lpchText, cchText, wFormat, iOverhang);
  405. if (cyExtent <= cyMaxWidth)
  406. return FALSE;
  407. // Reserve room for the "..." ellipses;
  408. // (Assumption: The ellipses don't have any prefixes!)
  409. FLGetTextExtentPoint32(hdc, szEllipsis, CCHELLIPSIS, &size);
  410. cyMaxWidth -= size.cx - iOverhang;
  411. // If no room for ellipses, always show first character.
  412. //
  413. ichMax = 1;
  414. if (cyMaxWidth > 0)
  415. {
  416. // Binary search to find characters that will fit.
  417. ichMin = 0;
  418. ichMax = cchText;
  419. while (ichMin < ichMax)
  420. {
  421. // Be sure to round up, to make sure we make progress in
  422. // the loop if ichMax == ichMin + 1.
  423. ichMid = (ichMin + ichMax + 1) / 2;
  424. cyExtent = DT_GetExtentMinusPrefixesVert(hdc, lpchText, ichMid, wFormat, iOverhang);
  425. if (cyExtent < cyMaxWidth)
  426. ichMin = ichMid;
  427. else
  428. {
  429. if (cyExtent > cyMaxWidth)
  430. ichMax = ichMid - 1;
  431. else
  432. {
  433. // Exact match up up to ichMid: just exit.
  434. ichMax = ichMid;
  435. break;
  436. }
  437. }
  438. }
  439. // Make sure we always show at least the first character...
  440. if (ichMax < 1)
  441. ichMax = 1;
  442. }
  443. *lpCount = ichMax;
  444. return TRUE;
  445. }
  446. // This adds a path ellipse to the given path name.
  447. // Returns TRUE if the resultant string's extent is less the the
  448. // cyMaxWidth. FALSE, if otherwise.
  449. int AddPathEllipsisVert(
  450. HDC hdc,
  451. LPWSTR lpszPath,
  452. int cchText,
  453. UINT wFormat,
  454. int cyMaxWidth,
  455. int iOverhang)
  456. {
  457. int iLen;
  458. UINT dxFixed, dxEllipsis;
  459. LPWSTR lpEnd; /* end of the unfixed string */
  460. LPWSTR lpFixed; /* start of text that we always display */
  461. BOOL bEllipsisIn;
  462. int iLenFixed;
  463. SIZE size;
  464. lpFixed = PathFindFileName(lpszPath, cchText);
  465. if (lpFixed != lpszPath)
  466. lpFixed--; // point at the slash
  467. else
  468. return cchText;
  469. lpEnd = lpFixed;
  470. bEllipsisIn = FALSE;
  471. iLenFixed = cchText - (int)(lpFixed - lpszPath);
  472. dxFixed = DT_GetExtentMinusPrefixesVert(hdc, lpFixed, iLenFixed, wFormat, iOverhang);
  473. // It is assumed that the "..." string does not have any prefixes ('&').
  474. FLGetTextExtentPoint32(hdc, szEllipsis, CCHELLIPSIS, &size);
  475. dxEllipsis = size.cx - iOverhang;
  476. while (TRUE)
  477. {
  478. iLen = dxFixed + DT_GetExtentMinusPrefixesVert(hdc, lpszPath, (int)((PBYTE)lpEnd - (PBYTE)lpszPath)/sizeof(WCHAR),
  479. wFormat, iOverhang) - iOverhang;
  480. if (bEllipsisIn)
  481. iLen += dxEllipsis;
  482. if (iLen <= cyMaxWidth)
  483. break;
  484. bEllipsisIn = TRUE;
  485. if (lpEnd <= lpszPath)
  486. {
  487. // Things didn't fit.
  488. lpEnd = lpszPath;
  489. break;
  490. }
  491. // Step back a character.
  492. lpEnd--;
  493. }
  494. if (bEllipsisIn && (lpEnd + CCHELLIPSIS < lpFixed))
  495. {
  496. // NOTE: the strings could over lap here. So, we use LCopyStruct.
  497. MoveMemory((lpEnd + CCHELLIPSIS), lpFixed, iLenFixed * sizeof(WCHAR));
  498. CopyMemory(lpEnd, szEllipsis, CCHELLIPSIS * sizeof(WCHAR));
  499. cchText = (int)(lpEnd - lpszPath) + CCHELLIPSIS + iLenFixed;
  500. // now we can NULL terminate the string
  501. *(lpszPath + cchText) = L'\0';
  502. }
  503. return cchText;
  504. }
  505. // This function returns the number of characters actually drawn.
  506. int AddEllipsisAndDrawLineVert(
  507. HDC hdc,
  508. int xLine,
  509. LPCWSTR lpchText,
  510. int cchText,
  511. DWORD dwDTformat,
  512. LPDRAWTEXTDATAVERT lpDrawInfo)
  513. {
  514. LPWSTR pEllipsis = NULL;
  515. WCHAR szTempBuff[MAXBUFFSIZE];
  516. LPWSTR lpDest;
  517. BOOL fAlreadyCopied = FALSE;
  518. // Check if this is a filename with a path AND
  519. // Check if the width is too narrow to hold all the text.
  520. if ((dwDTformat & DT_PATH_ELLIPSIS) &&
  521. ((DT_GetExtentMinusPrefixesVert(hdc, lpchText, cchText, dwDTformat, lpDrawInfo->cyOverhang)) > lpDrawInfo->cyMaxWidth))
  522. {
  523. // We need to add Path-Ellipsis. See if we can do it in-place.
  524. if (!(dwDTformat & DT_MODIFYSTRING)) {
  525. // NOTE: When you add Path-Ellipsis, the string could grow by
  526. // CCHELLIPSIS bytes.
  527. if((cchText + CCHELLIPSIS + 1) <= MAXBUFFSIZE)
  528. lpDest = szTempBuff;
  529. else
  530. {
  531. // Alloc the buffer from local heap.
  532. if(!(pEllipsis = (LPWSTR)LocalAlloc(LPTR, (cchText+CCHELLIPSIS+1)*sizeof(WCHAR))))
  533. return 0;
  534. lpDest = (LPWSTR)pEllipsis;
  535. }
  536. // Source String may not be NULL terminated. So, copy just
  537. // the given number of characters.
  538. CopyMemory(lpDest, lpchText, cchText*sizeof(WCHAR));
  539. lpchText = lpDest; // lpchText points to the copied buff.
  540. fAlreadyCopied = TRUE; // Local copy has been made.
  541. }
  542. // Add the path ellipsis now!
  543. cchText = AddPathEllipsisVert(hdc, (LPWSTR)lpchText, cchText, dwDTformat, lpDrawInfo->cyMaxWidth, lpDrawInfo->cyOverhang);
  544. }
  545. // Check if end-ellipsis are to be added.
  546. if ((dwDTformat & (DT_END_ELLIPSIS | DT_WORD_ELLIPSIS)) &&
  547. NeedsEndEllipsisVert(hdc, lpchText, &cchText, lpDrawInfo, dwDTformat))
  548. {
  549. // We need to add end-ellipsis; See if we can do it in-place.
  550. if (!(dwDTformat & DT_MODIFYSTRING) && !fAlreadyCopied)
  551. {
  552. // See if the string is small enough for the buff on stack.
  553. if ((cchText+CCHELLIPSIS+1) <= MAXBUFFSIZE)
  554. lpDest = szTempBuff; // If so, use it.
  555. else {
  556. // Alloc the buffer from local heap.
  557. if (!(pEllipsis = (LPWSTR)LocalAlloc(LPTR, (cchText+CCHELLIPSIS+1)*sizeof(WCHAR))))
  558. return 0;
  559. lpDest = pEllipsis;
  560. }
  561. // Make a copy of the string in the local buff.
  562. CopyMemory(lpDest, lpchText, cchText*sizeof(WCHAR));
  563. lpchText = lpDest;
  564. }
  565. // Add an end-ellipsis at the proper place.
  566. CopyMemory((LPWSTR)(lpchText+cchText), szEllipsis, (CCHELLIPSIS+1)*sizeof(WCHAR));
  567. cchText += CCHELLIPSIS;
  568. }
  569. // Draw the line that we just formed.
  570. DT_DrawJustifiedLineVert(hdc, xLine, lpchText, cchText, dwDTformat, lpDrawInfo);
  571. // Free the block allocated for End-Ellipsis.
  572. if (pEllipsis)
  573. LocalFree(pEllipsis);
  574. return cchText;
  575. }
  576. BOOL IsComplexScriptPresent(LPWSTR lpchText, int cchText);
  577. int FLDrawTextExPrivWVert(
  578. HDC hdc,
  579. LPWSTR lpchText,
  580. int cchText,
  581. LPRECT lprc,
  582. UINT dwDTformat,
  583. LPDRAWTEXTPARAMSVERT lpDTparams)
  584. {
  585. DRAWTEXTDATAVERT DrawInfo;
  586. WORD wFormat = LOWORD(dwDTformat);
  587. LPWSTR lpchTextBegin;
  588. LPWSTR lpchEnd;
  589. LPWSTR lpchNextLineSt;
  590. int iLineLength;
  591. int ixSign;
  592. int xLine;
  593. int xLastLineHeight;
  594. HRGN hrgnClip;
  595. int iLineCount;
  596. RECT rc;
  597. BOOL fLastLine;
  598. WCHAR ch;
  599. UINT oldAlign;
  600. if ((cchText == 0) && lpchText && (*lpchText))
  601. {
  602. // infoview.exe passes lpchText that points to '\0'
  603. // Lotus Notes doesn't like getting a zero return here
  604. return 1;
  605. }
  606. if (cchText == -1)
  607. cchText = lstrlenW(lpchText);
  608. else if (lpchText[cchText - 1] == L'\0')
  609. cchText--; // accommodate counting of NULLS for ME
  610. if ((lpDTparams) && (lpDTparams->cbSize != sizeof(DRAWTEXTPARAMS)))
  611. {
  612. ASSERT(0 && "DrawTextExWorker: cbSize is invalid");
  613. return 0;
  614. }
  615. // If DT_MODIFYSTRING is specified, then check for read-write pointer.
  616. if ((dwDTformat & DT_MODIFYSTRING) &&
  617. (dwDTformat & (DT_END_ELLIPSIS | DT_PATH_ELLIPSIS)))
  618. {
  619. if(IsBadWritePtr(lpchText, cchText))
  620. {
  621. ASSERT(0 && "DrawTextExWorker: For DT_MODIFYSTRING, lpchText must be read-write");
  622. return 0;
  623. }
  624. }
  625. // Initialize the DrawInfo structure.
  626. if (!DT_InitDrawTextInfoVert(hdc, lprc, dwDTformat, (LPDRAWTEXTDATAVERT)&DrawInfo, lpDTparams))
  627. return 0;
  628. // If the rect is too narrow or the margins are too wide.....Just forget it!
  629. //
  630. // If wordbreak is specified, the MaxWidth must be a reasonable value.
  631. // This check is sufficient because this will allow CALCRECT and NOCLIP
  632. // cases. --SANKAR.
  633. //
  634. // This also fixed all of our known problems with AppStudio.
  635. if (DrawInfo.cyMaxWidth <= 0)
  636. {
  637. if (wFormat & DT_WORDBREAK)
  638. {
  639. ASSERT(0 && "DrawTextExW: FAILURE DrawInfo.cyMaxWidth <= 0");
  640. return 1;
  641. }
  642. }
  643. // if we're not doing the drawing, initialise the lpk-dll
  644. if (dwDTformat & DT_RTLREADING)
  645. oldAlign = SetTextAlign(hdc, TA_RTLREADING | GetTextAlign(hdc));
  646. // If we need to clip, let us do that.
  647. if (!(wFormat & DT_NOCLIP))
  648. {
  649. // Save clipping region so we can restore it later.
  650. hrgnClip = CreateRectRgn(0,0,0,0);
  651. if (hrgnClip != NULL)
  652. {
  653. if (GetClipRgn(hdc, hrgnClip) != 1)
  654. {
  655. DeleteObject(hrgnClip);
  656. hrgnClip = (HRGN)-1;
  657. }
  658. rc = *lprc;
  659. IntersectClipRect(hdc, rc.left, rc.top, rc.right, rc.bottom);
  660. }
  661. }
  662. else
  663. hrgnClip = NULL;
  664. lpchTextBegin = lpchText;
  665. lpchEnd = lpchText + cchText;
  666. ProcessDrawText:
  667. iLineCount = 0; // Reset number of lines to 1.
  668. xLine = lprc->right;
  669. if (wFormat & DT_SINGLELINE)
  670. {
  671. iLineCount = 1; // It is a single line.
  672. // Process single line DrawText.
  673. switch (wFormat & DT_VFMTMASK)
  674. {
  675. case DT_BOTTOM:
  676. xLine = lprc->left + DrawInfo.cxLineHeight;
  677. break;
  678. case DT_VCENTER:
  679. xLine = lprc->right - ((lprc->right - lprc->left - DrawInfo.cxLineHeight) / 2);
  680. break;
  681. }
  682. cchText = AddEllipsisAndDrawLineVert(hdc, xLine, lpchText, cchText, dwDTformat, &DrawInfo);
  683. xLine += DrawInfo.cxLineHeight;
  684. lpchText += cchText;
  685. }
  686. else
  687. {
  688. // Multiline
  689. // If the height of the rectangle is not an integral multiple of the
  690. // average char height, then it is possible that the last line drawn
  691. // is only partially visible. However, if DT_EDITCONTROL style is
  692. // specified, then we must make sure that the last line is not drawn if
  693. // it is going to be partially visible. This will help imitate the
  694. // appearance of an edit control.
  695. if (wFormat & DT_EDITCONTROL)
  696. xLastLineHeight = DrawInfo.cxLineHeight;
  697. else
  698. xLastLineHeight = 0;
  699. ixSign = DrawInfo.iXSign;
  700. fLastLine = FALSE;
  701. // Process multiline DrawText.
  702. while ((lpchText < lpchEnd) && (!fLastLine))
  703. {
  704. // Check if the line we are about to draw is the last line that needs
  705. // to be drawn.
  706. // Let us check if the display goes out of the clip rect and if so
  707. // let us stop here, as an optimisation;
  708. if (!(wFormat & DT_CALCRECT) && // We don't need to calc rect?
  709. !(wFormat & DT_NOCLIP) && // Must we clip the display ?
  710. // Are we outside the rect?
  711. ((xLine + DrawInfo.cxLineHeight + xLastLineHeight)*ixSign > (lprc->right*ixSign)))
  712. {
  713. fLastLine = TRUE; // Let us quit this loop
  714. }
  715. // We do the Ellipsis processing only for the last line.
  716. if (fLastLine && (dwDTformat & (DT_END_ELLIPSIS | DT_PATH_ELLIPSIS)))
  717. lpchText += AddEllipsisAndDrawLineVert(hdc, xLine, lpchText, cchText, dwDTformat, &DrawInfo);
  718. else
  719. {
  720. lpchNextLineSt = (LPWSTR)DT_GetLineBreakVert(hdc, lpchText, cchText, dwDTformat, &iLineLength, &DrawInfo);
  721. // Check if we need to put ellipsis at the end of this line.
  722. // Also check if this is the last line.
  723. if ((dwDTformat & DT_WORD_ELLIPSIS) ||
  724. ((lpchNextLineSt >= lpchEnd) && (dwDTformat & (DT_END_ELLIPSIS | DT_PATH_ELLIPSIS))))
  725. AddEllipsisAndDrawLineVert(hdc, xLine, lpchText, iLineLength, dwDTformat, &DrawInfo);
  726. else
  727. DT_DrawJustifiedLineVert(hdc, xLine, lpchText, iLineLength, dwDTformat, &DrawInfo);
  728. cchText -= (int)((PBYTE)lpchNextLineSt - (PBYTE)lpchText) / sizeof(WCHAR);
  729. lpchText = lpchNextLineSt;
  730. }
  731. iLineCount++; // We draw one more line.
  732. xLine += DrawInfo.cxLineHeight;
  733. }
  734. // For Win3.1 and NT compatibility, if the last char is a CR or a LF
  735. // then the height returned includes one more line.
  736. if (!(dwDTformat & DT_EDITCONTROL) &&
  737. (lpchEnd > lpchTextBegin) && // If zero length it will fault.
  738. (((ch = (*(lpchEnd-1))) == CR) || (ch == LF)))
  739. xLine += DrawInfo.cxLineHeight;
  740. }
  741. // If DT_CALCRECT, modify width and height of rectangle to include
  742. // all of the text drawn.
  743. if (wFormat & DT_CALCRECT)
  744. {
  745. DrawInfo.rcFormat.bottom = DrawInfo.rcFormat.top + DrawInfo.cyMaxExtent * DrawInfo.iYSign;
  746. lprc->bottom = DrawInfo.rcFormat.bottom + DrawInfo.cyBottomMargin;
  747. // If the Width is more than what was provided, we have to redo all
  748. // the calculations, because, the number of lines can be less now.
  749. // (We need to do this only if we have more than one line).
  750. if((iLineCount > 1) && (DrawInfo.cyMaxExtent > DrawInfo.cyMaxWidth))
  751. {
  752. DrawInfo.cyMaxWidth = DrawInfo.cyMaxExtent;
  753. lpchText = lpchTextBegin;
  754. cchText = (int)((PBYTE)lpchEnd - (PBYTE)lpchTextBegin) / sizeof(WCHAR);
  755. goto ProcessDrawText; // Start all over again!
  756. }
  757. lprc->left = xLine;
  758. }
  759. if (hrgnClip != NULL)
  760. {
  761. if (hrgnClip == (HRGN)-1)
  762. ExtSelectClipRgn(hdc, NULL, RGN_COPY);
  763. else
  764. {
  765. ExtSelectClipRgn(hdc, hrgnClip, RGN_COPY);
  766. DeleteObject(hrgnClip);
  767. }
  768. }
  769. if (dwDTformat & DT_RTLREADING)
  770. SetTextAlign(hdc, oldAlign);
  771. // Copy the number of characters actually drawn
  772. if(lpDTparams != NULL)
  773. lpDTparams->uiLengthDrawn = (UINT)((PBYTE)lpchText - (PBYTE)lpchTextBegin) / sizeof(WCHAR);
  774. if (xLine == lprc->right)
  775. return 1;
  776. return (xLine + lprc->right);
  777. }
  778. int FLDrawTextWVert(HDC hdc, LPCWSTR lpchText, int cchText, LPCRECT lprc, UINT format)
  779. {
  780. DRAWTEXTPARAMSVERT DTparams;
  781. LPDRAWTEXTPARAMSVERT lpDTparams = NULL;
  782. if (cchText < -1)
  783. return(0);
  784. if (format & DT_TABSTOP)
  785. {
  786. DTparams.cbSize = sizeof(DRAWTEXTPARAMSVERT);
  787. DTparams.iTopMargin = DTparams.iBottomMargin = 0;
  788. DTparams.iTabLength = (format & 0xff00) >> 8;
  789. lpDTparams = &DTparams;
  790. format &= 0xffff00ff;
  791. }
  792. return FLDrawTextExPrivWVert(hdc, (LPWSTR)lpchText, cchText, (LPRECT)lprc, format, lpDTparams);
  793. }