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.

2620 lines
90 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Copyright (C) Microsoft Corporation, 1994-1995
  4. //
  5. // File: fontlink.cpp
  6. //
  7. // Contents: Wrappers for font link functions.
  8. //
  9. //----------------------------------------------------------------------------
  10. #include "ctlspriv.h"
  11. #ifdef FONT_LINK
  12. #include <mlang.h>
  13. #include "fontlink.h"
  14. #undef DrawTextW
  15. #undef DrawTextExW
  16. #undef ExtTextOutW
  17. #undef TextOutW
  18. #undef GetCharWidthW
  19. #undef GetTextExtentPointW
  20. #undef GetTextExtentPoint32W
  21. HRESULT (*g_pfnGetGlobalFontLinkObject)(IMLangFontLink **) = NULL;
  22. BOOL LoadMLFontLink(IMLangFontLink **ppMLFontLink)
  23. {
  24. ENTERCRITICAL;
  25. *ppMLFontLink = NULL;
  26. if (NULL == g_pfnGetGlobalFontLinkObject)
  27. {
  28. HMODULE hmod = LoadLibrary(TEXT("MLANG.DLL"));
  29. if (hmod)
  30. g_pfnGetGlobalFontLinkObject = (HRESULT (*)(IMLangFontLink **))GetProcAddress(hmod, "GetGlobalFontLinkObject");
  31. }
  32. if (g_pfnGetGlobalFontLinkObject)
  33. g_pfnGetGlobalFontLinkObject(ppMLFontLink);
  34. LEAVECRITICAL;
  35. return (*ppMLFontLink)? TRUE: FALSE;
  36. }
  37. #define _MAX_WCHAR_BUFFER_SIZE 256 // Should be enough for shell space name strings
  38. #define _MAX_MB_BUFFER_SIZE _MAX_WCHAR_BUFFER_SIZE * sizeof(WCHAR)
  39. //
  40. // Helper function to decide whether we need MLang font link
  41. // On return:
  42. // S_OK : Yes, we need MLang font link and MLang can be loaded succesfully
  43. // S_FALSE : No, we don't need MLang since text can be handled natively with font assciation (ANSI) and font link.
  44. // E_FAIL : Need MLang, but, we couldn't load it
  45. HRESULT FDoFontLink(HDC hdc, IMLangFontLink **ppMLFontLink, LPCWSTR pwszName, int cch)
  46. {
  47. HRESULT hr;
  48. BOOL fNotDisplayable = TRUE;
  49. int cchWChar;
  50. int cchMB = cch * sizeof(WCHAR);
  51. // When possible, we'll use internal buffer to avoid memory operations
  52. char szBuffer[_MAX_MB_BUFFER_SIZE];
  53. WCHAR wszBuffer[_MAX_WCHAR_BUFFER_SIZE];
  54. char *pszBuffer = szBuffer;
  55. WCHAR *pwszBuffer = wszBuffer;
  56. UINT uiCharset;
  57. CHARSETINFO csi;
  58. ASSERT(ppMLFontLink);
  59. uiCharset = GetTextCharsetInfo(hdc, NULL, 0);
  60. TranslateCharsetInfo(IntToPtr_(DWORD *, uiCharset), &csi, TCI_SRCCHARSET);
  61. if (cch>0 && g_uiACP == csi.ciACP)
  62. {
  63. // Alloc buffer if multibyte buffer is not enough
  64. if (cch > _MAX_WCHAR_BUFFER_SIZE)
  65. pszBuffer = (char *)LocalAlloc(LPTR, cchMB);
  66. if (pszBuffer)
  67. {
  68. cchMB = WideCharToMultiByte(CP_ACP, 0, pwszName, cch, pszBuffer, cchMB, NULL, &fNotDisplayable);
  69. // Round-trip verification
  70. if (!fNotDisplayable)
  71. {
  72. // Alloc buffer if wide char buffer is not enough
  73. if (cch > _MAX_WCHAR_BUFFER_SIZE)
  74. pwszBuffer = (WCHAR *)LocalAlloc(LPTR, cch*sizeof(WCHAR));
  75. if (pwszBuffer)
  76. {
  77. cchWChar = MultiByteToWideChar(CP_ACP, 0, pszBuffer, cchMB, pwszBuffer, cch);
  78. if (cch == cchWChar)
  79. {
  80. for (int i=0; i<cch; i++)
  81. {
  82. if (pwszBuffer[i] != pwszName[i])
  83. {
  84. fNotDisplayable = TRUE;
  85. break;
  86. }
  87. }
  88. }
  89. else
  90. {
  91. fNotDisplayable = TRUE;
  92. }
  93. if (pwszBuffer != wszBuffer)
  94. LocalFree(pwszBuffer);
  95. }
  96. else
  97. {
  98. fNotDisplayable = TRUE;
  99. }
  100. }
  101. if (pszBuffer != szBuffer)
  102. LocalFree(pszBuffer);
  103. }
  104. }
  105. if (fNotDisplayable)
  106. {
  107. if (LoadMLFontLink(ppMLFontLink))
  108. hr = S_OK; // Need MLang font link
  109. else
  110. hr = E_FAIL; // Unable to load MLang
  111. }
  112. else
  113. {
  114. hr = S_FALSE; // Doesn't need MLang font link, but, we still need to call GDI in ANSI for Win9x FA to work properly
  115. }
  116. return hr;
  117. }
  118. //
  119. // For _GetCharWidthWFontLink()
  120. //
  121. HRESULT FDoFontLink(HDC hdc, IMLangFontLink **ppMLFontLink, WCHAR wFirstChar, WCHAR wLastChar)
  122. {
  123. WCHAR wszBuffer[_MAX_WCHAR_BUFFER_SIZE];
  124. int i = 0;
  125. int cch = wFirstChar - wLastChar + 1;
  126. WCHAR *pwszBuffer = wszBuffer;
  127. HRESULT hr = E_FAIL;
  128. if (cch > _MAX_WCHAR_BUFFER_SIZE)
  129. pwszBuffer = (WCHAR *)LocalAlloc(LPTR, cch*sizeof(WCHAR));
  130. if (pwszBuffer)
  131. {
  132. while (i < cch)
  133. {
  134. pwszBuffer[i] = wFirstChar+i;
  135. i++;
  136. }
  137. hr = FDoFontLink(hdc, ppMLFontLink, pwszBuffer, cch);
  138. if (pwszBuffer != wszBuffer)
  139. LocalFree(pwszBuffer);
  140. }
  141. return hr;
  142. }
  143. //
  144. // BUGBUG: Review for removing below big table and UsrFromWch() ...
  145. //
  146. __inline BOOL FChsDbcs(UINT chs)
  147. {
  148. return (chs == SHIFTJIS_CHARSET ||
  149. chs == HANGEUL_CHARSET ||
  150. chs == CHINESEBIG5_CHARSET ||
  151. chs == GB2312_CHARSET);
  152. }
  153. __inline int FChsBiDi(int chs)
  154. {
  155. return (chs == ARABIC_CHARSET ||
  156. chs == HEBREW_CHARSET);
  157. }
  158. __inline BOOL FCpgChinese(UINT cpg)
  159. {
  160. if (cpg == CP_ACP)
  161. cpg = GetACP();
  162. return (cpg == CP_TAIWAN || cpg == CP_CHINA);
  163. }
  164. __inline BOOL FCpgTaiwan(UINT cpg)
  165. {
  166. if (cpg == CP_ACP)
  167. cpg = GetACP();
  168. return (cpg == CP_TAIWAN);
  169. }
  170. __inline BOOL FCpgPRC(UINT cpg)
  171. {
  172. if (cpg == CP_ACP)
  173. cpg = GetACP();
  174. return (cpg == CP_CHINA);
  175. }
  176. __inline BOOL FCpgFarEast(UINT cpg)
  177. {
  178. if (cpg == CP_ACP)
  179. cpg = GetACP();
  180. return (cpg == CP_JAPAN || cpg == CP_TAIWAN || cpg == CP_CHINA ||
  181. cpg == CP_KOREA || cpg == CP_MAC_JAPAN);
  182. }
  183. __inline BOOL FCpgDbcs(UINT cpg)
  184. {
  185. return (cpg == CP_JAPAN ||
  186. cpg == CP_KOREA ||
  187. cpg == CP_TAIWAN ||
  188. cpg == CP_CHINA);
  189. }
  190. __inline int FCpgBiDi(int cpg)
  191. {
  192. return (cpg == CP_ARABIC ||
  193. cpg == CP_HEBREW);
  194. }
  195. // Table to map Unicode high byte value to first sub range for this high byte
  196. const BYTE mpbHighusr[256] =
  197. {
  198. /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
  199. /* 0 */ 0, 2, 3, 6, 9, 10, 13, 0, 0, 15, 17, 19, 21, 23, 24, 0,
  200. /* 1 */ 26, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 30,
  201. /* 2 */ 31, 35, 38, 39, 40, 43, 46, 47, 0, 0, 0, 0, 0, 0, 0, 0,
  202. /* 3 */ 48, 51, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  203. /* 4 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 59, 59,
  204. /* 5 */ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
  205. /* 6 */ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
  206. /* 7 */ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
  207. /* 8 */ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
  208. /* 9 */ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
  209. /* a */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 56, 56, 56,
  210. /* b */ 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
  211. /* c */ 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
  212. /* d */ 56, 56, 56, 56, 56, 56, 56, 56, 0, 0, 0, 0, 0, 0, 0, 0,
  213. /* e */ 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60,
  214. /* f */ 60, 60, 60, 60, 60, 60, 60, 60, 60, 61, 61, 62, 63, 63, 64, 68
  215. };
  216. int UsrFromWch(WCHAR wch)
  217. {
  218. UCHAR bLow = LOBYTE(wch);
  219. int usr;
  220. switch (usr = mpbHighusr[HIBYTE(wch)])
  221. {
  222. case usrBasicLatin:
  223. if (bLow < 0x80)
  224. break;
  225. ++usr;
  226. break;
  227. case usrLatinXA:
  228. if (bLow < 0x80)
  229. break;
  230. ++usr;
  231. break;
  232. case usrLatinXB:
  233. if (bLow < 0x50)
  234. {
  235. // ASSERT(wch <= 0x217);
  236. break;
  237. }
  238. ++usr;
  239. if (bLow < 0xb0)
  240. {
  241. // ASSERT(wch <= 0x2a8);
  242. break;;
  243. }
  244. ++usr;
  245. // ASSERT(wch <= 0x2e9);
  246. break;
  247. case usrCombDiacritical:
  248. if (bLow < 0x70)
  249. {
  250. // ASSERT(wch <= 0x345 || wch == 0x360 || wch == 0x361);
  251. break;
  252. }
  253. ++usr;
  254. if (bLow < 0xd0)
  255. {
  256. // ASSERT(wch == 0x374 || wch == 0x375 || wch == 0x37a || wch == 0x37e || (wch >= 0x384 && wch <= 0x38a) || wch == 0x38c ||
  257. // (wch >= 0x38e && wch <= 0x3ce));
  258. break;
  259. }
  260. ++usr;
  261. // ASSERT(wch <= 0x3d6 || wch == 0x3da || wch == 0x3dc || wch == 0x3de || wch == 0x3e0 || (wch >= 0x3e2 && wch <= 0x3f3));
  262. break;
  263. case usrCyrillic:
  264. // ASSERT((wch >= 0x401 && wch <= 0x40c) || (wch >= 0x40e && wch <= 0x44f) || (wch >= 0x450 && wch <= 0x45c) ||
  265. // (wch >= 0x45e && wch <= 0x486) || (wch >= 0x490 && wch <= 0x4cc) || (wch >= 0x4d0 && wch <= 0x4f9));
  266. break;
  267. case usrArmenian:
  268. if (bLow < 0x90)
  269. {
  270. // ASSERT((wch >= 0x531 && wch <= 0x556) || (wch >= 0x559 && wch <= 0x55f) || (wch >= 0x561 && wch <= 0x587) || wch == 0x589);
  271. break;
  272. }
  273. ++usr;
  274. if (bLow >= 0xd0)
  275. {
  276. // ASSERT(wch <= 0x5ea || (wch >= 0x5f0 && wch <= 0x5f4));
  277. break;
  278. }
  279. ++usr;
  280. // ASSERT(wch >= 0x5b0 && wch <= 0x5c3);
  281. break;
  282. case usrBasicArabic:
  283. if (bLow < 0x53)
  284. {
  285. // ASSERT(wch == 0x60c || wch == 0x61b || wch == 0x61f || (wch >= 0x621 && wch <= 0x63a) || (wch >= 0x640 && wch <= 0x652));
  286. break;
  287. }
  288. ++usr;
  289. // ASSERT((wch >= 0x660 && wch <= 0x66d) || (wch >= 0x670 && wch <= 0x6b7) || (wch >= 0x6ba && wch <= 0x6be) ||
  290. // (wch >= 0x6c0 && wch <= 0x6ce) || (wch >= 0x6d0 && wch <= 0x6ed) || (wch >= 0x6f0 && wch <= 0x6f9));
  291. break;
  292. case usrDevangari:
  293. case usrGurmukhi:
  294. case usrOriya:
  295. case usrTelugu:
  296. case usrThai:
  297. // REVIEW: Added asserts for these
  298. if (bLow < 0x80)
  299. break;
  300. ++usr;
  301. break;
  302. case usrMalayalam:
  303. // ASSERT(wch < 0xd80);
  304. break;
  305. case usrBasicGeorgian:
  306. if (bLow >= 0xD0)
  307. break;
  308. ++usr;
  309. // ASSERT(bLow >= 0xa0);
  310. break;
  311. case usrHangulJamo:
  312. // ASSERT(wch <= 0x11f9);
  313. break;
  314. case usrLatinExtendedAdd:
  315. // ASSERT(wch <= 0x1ef9);
  316. break;
  317. case usrGreekExtended:
  318. // ASSERT(wch <= 0x1ffe);
  319. break;
  320. case usrGeneralPunct:
  321. if (bLow < 0x70)
  322. {
  323. // ASSERT(wch <= 0x2046 || (wch >= 0x206a && wch <= 0x206f));
  324. break;
  325. }
  326. ++usr;
  327. if (bLow < 0xa0)
  328. {
  329. // ASSERT(wch == 0x2070 || (wch >= 0x2074 && wch <= 0x208e));
  330. break;
  331. }
  332. ++usr;
  333. if (bLow < 0xd0)
  334. {
  335. // ASSERT(wch <= 0x20aa);
  336. break;
  337. }
  338. ++usr;
  339. // ASSERT(wch <= 0x20e1);
  340. break;
  341. case usrLetterlikeSymbols:
  342. if (bLow < 0x50)
  343. {
  344. // ASSERT(wch <= 0x2138);
  345. break;
  346. }
  347. ++usr;
  348. if (bLow < 0x90)
  349. {
  350. // ASSERT((wch >= 0x2153 && wch <= 0x2182));
  351. break;
  352. }
  353. ++usr;
  354. // ASSERT(wch <= 0x21ea);
  355. break;
  356. case usrMathematicalOps:
  357. // ASSERT(wch <= 0x22f1);
  358. break;
  359. case usrMiscTechnical:
  360. // ASSERT(wch <= 0x237a);
  361. break;
  362. case usrControlPictures:
  363. if (bLow < 0x40)
  364. {
  365. // ASSERT(wch <= 0x2424);
  366. break;
  367. }
  368. ++usr;
  369. if (bLow < 0x60)
  370. {
  371. // ASSERT(wch <= 0x244a);
  372. break;
  373. }
  374. ++usr;
  375. // ASSERT(wch <= 0x24ea);
  376. break;
  377. case usrBoxDrawing:
  378. if (bLow < 0x80)
  379. break;
  380. ++usr;
  381. if (bLow < 0xa0)
  382. {
  383. // ASSERT(wch <= 0x2595);
  384. break;
  385. }
  386. ++usr;
  387. // ASSERT(wch <= 0x25ef);
  388. break;
  389. case usrMiscDingbats:
  390. // ASSERT(wch <= 0x2613 || (wch >= 0x261a && wch <= 0x266f));
  391. break;
  392. case usrDingbats:
  393. break;
  394. case usrCJKSymAndPunct:
  395. if (bLow < 0x40)
  396. {
  397. // ASSERT(wch <= 0x3037 || wch == 0x303f);
  398. break;
  399. }
  400. ++usr;
  401. if (bLow < 0xa0)
  402. {
  403. // ASSERT((wch >= 0x3041 && wch <= 0x3094) || (wch >= 0x3099 && wch <= 0x309e));
  404. break;
  405. }
  406. ++usr;
  407. // ASSERT(wch >= 0x30a1 && wch <= 0x30fe);
  408. break;
  409. case usrBopomofo:
  410. if (bLow < 0x30)
  411. {
  412. // ASSERT(wch >= 0x3105 && wch <= 0x312c);
  413. break;
  414. }
  415. ++usr;
  416. if (bLow < 0x90)
  417. {
  418. // ASSERT(wch >= 0x3131 && wch <= 0x318e);
  419. break;
  420. }
  421. ++usr;
  422. // ASSERT(wch <= 0x319f);
  423. break;
  424. case usrEnclosedCJKLtMnth:
  425. // ASSERT((wch >= 0x3200 && wch <= 0x321c) || (wch >= 0x3220 && wch <= 0x3243) || (wch >= 0x3260 && wch <= 0x327b) ||
  426. // (wch >= 0x327f && wch <= 0x32b0) || (wch >= 0x32c0 && wch <= 0x32cb) || (wch >= 0x32d0 && wch <= 0x32fe));
  427. break;
  428. case usrCJKCompatibility:
  429. // ASSERT(wch <= 0x3376 || (wch >= 0x337b && wch <= 0x33dd) || (wch >= 0x33e0 && wch <= 0x33FE));
  430. break;
  431. case usrHangul:
  432. // ASSERT(wch <= 0xd7a3);
  433. break;
  434. case usrCJKUnifiedIdeo:
  435. break;
  436. case usrPrivateUseArea:
  437. break;
  438. case usrCJKCompatibilityIdeographs:
  439. // ASSERT(wch <= 0xfa2d);
  440. break;
  441. case usrAlphaPresentationForms:
  442. if (bLow < 0x50)
  443. {
  444. break;
  445. }
  446. ++usr;
  447. break;
  448. case usrArabicPresentationFormsA:
  449. // ASSERT(wch <= 0xfdfb);
  450. break;
  451. case usrCombiningHalfMarks:
  452. if (bLow < 0x30)
  453. {
  454. // ASSERT(wch >= 0xfe20 && wch <= 0xfe23);
  455. break;
  456. }
  457. ++usr;
  458. if (bLow < 0x50)
  459. {
  460. // ASSERT((wch >= 0xfe30 && wch <= 0xfe44) || wch >= 0xfe49);
  461. break;
  462. }
  463. ++usr;
  464. if (bLow < 0x70)
  465. {
  466. // ASSERT((wch >= 0xfe50 && wch <= 0xfe52) || (wch >= 0xfe54 && wch <= 0xfe66) || (wch >= 0xfe68 && wch <= 0xfe6b));
  467. break;
  468. }
  469. ++usr;
  470. // REVIEW : Need assert for this range
  471. break;
  472. case usrHFWidthForms:
  473. if (bLow < 0xf0)
  474. {
  475. // ASSERT((wch >= 0xff01 && wch <= 0xff5e) || (wch >= 0xff61 && wch <= 0xffbe) || (wch >= 0xffc2 && wch <= 0xffc7) ||
  476. // (wch >= 0xffca && wch <= 0xffcf) || (wch >= 0xffd2 && wch <= 0xffd7) || (wch >= 0xffda && wch <= 0xffdc) ||
  477. // (wch >= 0xffe0 && wch <= 0xffe6));
  478. break;
  479. }
  480. ++usr;
  481. // ASSERT(wch == 0xfffd);
  482. break;
  483. default:
  484. ASSERT(FALSE); // bad Unicode character!!
  485. break;
  486. }
  487. return usr;
  488. }
  489. BOOL _OtherGetCharWidthW(HDC hdc, UINT uFirstChar, UINT uLastChar, LPINT lpnWidths)
  490. {
  491. ASSERT(uFirstChar <= uLastChar);
  492. return GetCharWidthW(hdc, uFirstChar, uLastChar, lpnWidths);
  493. }
  494. //
  495. // _GetCharWidthWFontLink
  496. //
  497. // This is a filter for _GetCharWidthW() calls that does font linking.
  498. //
  499. // The input string is scanned and fonts are switched if not all chars are
  500. // supported by the current font in the HDC.
  501. //
  502. BOOL _GetCharWidthWFontLink(HDC hdc, UINT uFirstChar, UINT uLastChar, LPINT lpnWidths)
  503. {
  504. HFONT hfont = NULL;
  505. HFONT hfontSav = NULL;
  506. HFONT hfontMap = NULL;
  507. WCHAR xch, xchFirst, xchLast = (WCHAR)uLastChar;
  508. DWORD dwFontCodePages, dwCharCodePages;
  509. BOOL fRet = FALSE;
  510. IMLangFontLink *pMLFontLink = NULL;
  511. switch (FDoFontLink(hdc, &pMLFontLink, (WCHAR)uFirstChar, (WCHAR)uLastChar))
  512. {
  513. case S_OK:
  514. break;
  515. case S_FALSE:
  516. UINT ui;
  517. UINT uCharAnsi;
  518. ASSERT(uFirstChar <= uLastChar);
  519. for (ui=uFirstChar; ui<= uLastChar; ui++)
  520. {
  521. uCharAnsi = 0;
  522. if (WideCharToMultiByte(CP_ACP, 0, (LPCWSTR) &ui, 1, (LPSTR) &uCharAnsi, sizeof(uCharAnsi), NULL, NULL))
  523. fRet = GetCharWidthA(hdc, uCharAnsi, uCharAnsi, &(lpnWidths[ui - uFirstChar]));
  524. if (!fRet)
  525. break;
  526. }
  527. return fRet;
  528. default:
  529. return FALSE;
  530. }
  531. hfont = (HFONT)GetCurrentObject(hdc, OBJ_FONT);
  532. pMLFontLink->GetFontCodePages(hdc, hfont, &dwFontCodePages);
  533. // See if whole string can be handled by current font
  534. for (xch = (WCHAR)uFirstChar; xch <= xchLast; xch++)
  535. {
  536. pMLFontLink->GetCharCodePages(xch, &dwCharCodePages);
  537. if (!(dwFontCodePages & dwCharCodePages))
  538. {
  539. // Output the run
  540. if ((xch - uFirstChar) > 0)
  541. fRet = _OtherGetCharWidthW(hdc, uFirstChar, xch - 1, lpnWidths);
  542. break;
  543. }
  544. }
  545. while (xch <= xchLast)
  546. {
  547. xchFirst = xch;
  548. pMLFontLink->MapFont(hdc, dwCharCodePages, hfont, &hfontMap);
  549. hfontSav = (HFONT)SelectObject(hdc, hfontMap);
  550. pMLFontLink->GetFontCodePages(hdc, hfontMap, &dwFontCodePages);
  551. // Collect up run of characters supported by this font
  552. for (xch++; xch <= xchLast; xch++)
  553. {
  554. pMLFontLink->GetCharCodePages(xch, &dwCharCodePages);
  555. if (!(dwFontCodePages & dwCharCodePages))
  556. break;
  557. }
  558. // Output the run
  559. fRet = _OtherGetCharWidthW(hdc, xchFirst, xch - 1, lpnWidths + (xchFirst - uFirstChar));
  560. SelectObject(hdc, hfontSav);
  561. pMLFontLink->ReleaseFont(hfontMap);
  562. // BUGBUG:
  563. if (!fRet)
  564. break;
  565. }
  566. pMLFontLink->Release();
  567. return fRet;
  568. }
  569. BOOL GetCharWidthWrap(HDC hdc, UINT uFirstChar, UINT uLastChar, LPINT lpnWidths)
  570. {
  571. BOOL fRet = FALSE;
  572. // On NT5, we use system API behavior including fontlink
  573. if (g_bRunOnNT5)
  574. return GetCharWidthW(hdc, uFirstChar, uLastChar, lpnWidths);
  575. if (uLastChar > 127) // Optimization, skip for below 127
  576. {
  577. fRet = _GetCharWidthWFontLink(hdc, uFirstChar, uLastChar, lpnWidths);
  578. }
  579. if (!fRet)
  580. fRet = _OtherGetCharWidthW(hdc, uFirstChar, uLastChar, lpnWidths);
  581. return fRet;
  582. }
  583. BOOL _OtherGetTextExtentPointW(HDC hdc, LPCWSTR lpwch, int cch, LPSIZE lpSize)
  584. {
  585. BOOL fRet;
  586. if (cch == 0)
  587. {
  588. fRet = GetTextExtentPointA(hdc, " ", 1, lpSize);
  589. lpSize->cx = 0;
  590. return fRet;
  591. }
  592. return GetTextExtentPoint32W(hdc, lpwch, cch, lpSize);
  593. }
  594. //
  595. // _GetTextExtentPointWFontLink
  596. //
  597. // This is a filter for GetTextExtentPointW() that does font linking.
  598. //
  599. // The input string is scanned and fonts are switched if not all chars are
  600. // supported by the current font in the HDC.
  601. //
  602. BOOL _GetTextExtentPointWFontLink(HDC hdc, LPCWSTR lpwch, int cch, LPSIZE lpSize)
  603. {
  604. HFONT hfont = NULL;
  605. HFONT hfontSav = NULL;
  606. HFONT hfontMap = NULL;
  607. BOOL fRet = FALSE;
  608. int cchDone;
  609. long cchCodePages;
  610. DWORD dwACP, dwFontCodePages, dwCodePages;
  611. SIZE size;
  612. IMLangFontLink *pMLFontLink = NULL;
  613. ASSERT(cch != 0);
  614. switch (FDoFontLink(hdc, &pMLFontLink, lpwch, cch))
  615. {
  616. case S_OK:
  617. break;
  618. case S_FALSE:
  619. {
  620. // Times by the size of WCHAR should be enough for multibyte string buffer
  621. char *lpStr = (char *)LocalAlloc(LPTR, cch*sizeof(WCHAR));
  622. if (lpStr)
  623. {
  624. if (cch = WideCharToMultiByte(CP_ACP,0,lpwch, cch, lpStr, cch*sizeof(WCHAR), NULL, NULL))
  625. fRet = GetTextExtentPointA(hdc, lpStr, cch, lpSize);
  626. LocalFree(lpStr);
  627. }
  628. }
  629. return fRet;
  630. default:
  631. return FALSE;
  632. }
  633. hfont = (HFONT)GetCurrentObject(hdc, OBJ_FONT);
  634. pMLFontLink->GetFontCodePages(hdc, hfont, &dwFontCodePages);
  635. pMLFontLink->CodePageToCodePages(g_uiACP, &dwACP); // Give priority to CP_ACP
  636. // See if whole string can be handled by current font
  637. pMLFontLink->GetStrCodePages(lpwch, cch, dwACP, &dwCodePages, &cchCodePages);
  638. // current font supports whole string ?
  639. if ((dwFontCodePages & dwCodePages) && cch == cchCodePages)
  640. {
  641. pMLFontLink->Release();
  642. return FALSE;
  643. }
  644. // Get Hight of DC font
  645. if (!(fRet = GetTextExtentPointA(hdc, " ", 1, lpSize)))
  646. {
  647. pMLFontLink->Release();
  648. return FALSE;
  649. }
  650. lpSize->cx = 0;
  651. for (cchDone = 0; cchDone < cch; cchDone += cchCodePages)
  652. {
  653. pMLFontLink->GetStrCodePages(lpwch + cchDone, cch - cchDone, dwACP, &dwCodePages, &cchCodePages);
  654. if (!(dwFontCodePages & dwCodePages))
  655. {
  656. pMLFontLink->MapFont(hdc, dwCodePages, hfont, &hfontMap);
  657. hfontSav = (HFONT)SelectObject(hdc, hfontMap);
  658. }
  659. // cchCodePages shouldn't be 0
  660. ASSERT(cchCodePages);
  661. if (cchCodePages > 0)
  662. {
  663. fRet = _OtherGetTextExtentPointW(hdc, lpwch + cchDone, cchCodePages, &size);
  664. lpSize->cx += size.cx;
  665. }
  666. if (NULL != hfontSav)
  667. {
  668. SelectObject(hdc, hfontSav);
  669. pMLFontLink->ReleaseFont(hfontMap);
  670. hfontSav = NULL;
  671. }
  672. }
  673. pMLFontLink->Release();
  674. return fRet;
  675. }
  676. BOOL GetTextExtentPointWrap(HDC hdc, LPCWSTR lpwch, int cch, LPSIZE lpSize)
  677. {
  678. BOOL fRet = FALSE;
  679. // On NT5, we use system API behavior including fontlink
  680. if (g_bRunOnNT5)
  681. return GetTextExtentPointW(hdc, lpwch, cch, lpSize);
  682. if (cch)
  683. {
  684. // Optimize for all < 128 case
  685. if (cch < 256 && lpwch[0] <= 127)
  686. {
  687. char lpchA[256];
  688. int ich;
  689. BOOL fAscii = TRUE;
  690. for (ich = 0; ich < cch; ich++)
  691. {
  692. WCHAR wch = lpwch[ich];
  693. if (wch <= 127)
  694. lpchA[ich] = (char) wch;
  695. else
  696. {
  697. fAscii = FALSE;
  698. break;
  699. }
  700. }
  701. if (fAscii)
  702. return GetTextExtentPointA(hdc, lpchA, cch, lpSize);
  703. }
  704. fRet = _GetTextExtentPointWFontLink(hdc, lpwch, cch, lpSize);
  705. }
  706. if (!fRet)
  707. fRet = _OtherGetTextExtentPointW(hdc, lpwch, cch, lpSize);
  708. return fRet;
  709. }
  710. BOOL GetTextExtentPoint32Wrap(HDC hdc, LPCWSTR lpwch, int cch, LPSIZE lpSize)
  711. {
  712. return GetTextExtentPointWrap(hdc, lpwch, cch, lpSize);
  713. }
  714. BOOL _OtherExtTextOutW(HDC hdc, int xp, int yp, UINT eto, CONST RECT *lprect,
  715. LPCWSTR lpwch, UINT cLen, CONST INT *lpdxp)
  716. {
  717. UINT cpg = CP_DEFAULT;
  718. if (!(eto & ETO_GLYPH_INDEX) && cLen < 256 && lpwch[0] <= 127)
  719. {
  720. char lpchA[256];
  721. UINT ich;
  722. BOOL fAscii = TRUE;
  723. for (ich = 0; ich < cLen; ich++)
  724. {
  725. WCHAR wch = lpwch[ich];
  726. if (wch <= 127)
  727. lpchA[ich] = (char) wch;
  728. else
  729. {
  730. fAscii = FALSE;
  731. break;
  732. }
  733. }
  734. if (fAscii)
  735. return ExtTextOutA(hdc, xp, yp, eto, lprect, lpchA, cLen, lpdxp);
  736. }
  737. return ExtTextOutW(hdc, xp, yp, eto, lprect, lpwch, cLen, lpdxp);
  738. }
  739. BOOL _ExtTextOutWFontLink(HDC hdc, int xp, int yp, UINT eto, CONST RECT *lprect,
  740. LPCWSTR lpwch, UINT cLen, CONST INT *lpdxp)
  741. {
  742. HFONT hfont = NULL;
  743. HFONT hfontSav = NULL;
  744. HFONT hfontMap = NULL;
  745. BOOL fRet = FALSE;
  746. UINT ta;
  747. int fDoTa = FALSE;
  748. int fQueryTa = TRUE;
  749. POINT pt;
  750. int cchDone;
  751. DWORD dwACP, dwFontCodePages, dwCodePages;
  752. long cchCodePages;
  753. IMLangFontLink *pMLFontLink = NULL;
  754. if (cLen == 0)
  755. return FALSE;
  756. switch (FDoFontLink(hdc, &pMLFontLink, lpwch, cLen))
  757. {
  758. case S_OK:
  759. break;
  760. case S_FALSE:
  761. {
  762. // Times by the size of WCHAR should be enough for multibyte string buffer
  763. char *lpStr = (char *)LocalAlloc(LPTR, cLen*sizeof(WCHAR));
  764. if (lpStr)
  765. {
  766. if (cLen = WideCharToMultiByte(CP_ACP,0,lpwch, cLen, lpStr, cLen*sizeof(WCHAR), NULL, NULL))
  767. fRet = ExtTextOutA(hdc, xp, yp, eto, lprect, lpStr, cLen, lpdxp);
  768. LocalFree(lpStr);
  769. }
  770. }
  771. return fRet;
  772. default:
  773. return FALSE;
  774. }
  775. hfont = (HFONT)GetCurrentObject(hdc, OBJ_FONT);
  776. pMLFontLink->GetFontCodePages(hdc, hfont, &dwFontCodePages);
  777. pMLFontLink->CodePageToCodePages(g_uiACP, &dwACP); // Give priority to CP_ACP
  778. // See if whole string can be handled by current font
  779. pMLFontLink->GetStrCodePages(lpwch, cLen, dwACP, &dwCodePages, &cchCodePages);
  780. // current font supports whole string ?
  781. if ((dwFontCodePages & dwCodePages) && cLen == (UINT)cchCodePages)
  782. {
  783. pMLFontLink->Release();
  784. return FALSE;
  785. }
  786. for (cchDone = 0; (UINT)cchDone < cLen; cchDone += cchCodePages)
  787. {
  788. pMLFontLink->GetStrCodePages(lpwch + cchDone, cLen - cchDone, dwACP, &dwCodePages, &cchCodePages);
  789. if (!(dwFontCodePages & dwCodePages))
  790. {
  791. pMLFontLink->MapFont(hdc, dwCodePages, hfont, &hfontMap); // BUGBUG: Baseline?
  792. hfontSav = (HFONT)SelectObject(hdc, hfontMap);
  793. }
  794. // cchCodePages shouldn't be 0
  795. ASSERT(cchCodePages);
  796. if (cchCodePages > 0)
  797. {
  798. // If rendering in multiple parts, need to use TA_UPDATECP
  799. if ((UINT)cchCodePages != cLen && fQueryTa)
  800. {
  801. ta = GetTextAlign(hdc);
  802. if ((ta & TA_UPDATECP) == 0) // Don't do the move if x, y aren't being used
  803. {
  804. MoveToEx(hdc, xp, yp, &pt);
  805. fDoTa = TRUE;
  806. }
  807. fQueryTa = FALSE;
  808. }
  809. if (fDoTa)
  810. SetTextAlign(hdc, ta | TA_UPDATECP);
  811. fRet = _OtherExtTextOutW(hdc, xp, yp, eto, lprect, lpwch + cchDone, cchCodePages,
  812. lpdxp ? lpdxp + cchDone : NULL);
  813. eto = eto & ~ETO_OPAQUE; // Don't do mupltiple OPAQUEs!!!
  814. if (fDoTa)
  815. SetTextAlign(hdc, ta);
  816. if (!fRet)
  817. break;
  818. }
  819. if (NULL != hfontSav)
  820. {
  821. SelectObject(hdc, hfontSav);
  822. pMLFontLink->ReleaseFont(hfontMap);
  823. hfontSav = NULL;
  824. }
  825. }
  826. if (fDoTa) // Don't do the move if x, y aren't being used
  827. MoveToEx(hdc, pt.x, pt.y, NULL);
  828. pMLFontLink->Release();
  829. return fRet;
  830. }
  831. BOOL ExtTextOutWrap(HDC hdc, int xp, int yp, UINT eto, CONST RECT *lprect, LPCWSTR lpwch, UINT cLen, CONST INT *lpdxp)
  832. {
  833. BOOL fRet = FALSE;
  834. // On NT5, we use system API behavior including fontlink
  835. if (g_bRunOnNT5)
  836. return ExtTextOutW(hdc, xp, yp, eto, lprect, lpwch, cLen, lpdxp);
  837. if (cLen == 0)
  838. {
  839. char chT;
  840. return ExtTextOutA(hdc, xp, yp, eto, lprect, &chT, cLen, lpdxp);
  841. }
  842. // Optimize for all < 128 case
  843. if (!(eto & ETO_GLYPH_INDEX) && cLen < 256 && lpwch[0] <= 127)
  844. {
  845. char lpchA[256];
  846. UINT ich;
  847. BOOL fAscii = TRUE;
  848. for (ich = 0; ich < cLen; ich++)
  849. {
  850. WCHAR wch = lpwch[ich];
  851. if (wch <= 127)
  852. lpchA[ich] = (char) wch;
  853. else
  854. {
  855. fAscii = FALSE;
  856. break;
  857. }
  858. }
  859. if (fAscii)
  860. return ExtTextOutA(hdc, xp, yp, eto, lprect, lpchA, cLen, lpdxp);
  861. }
  862. #ifdef DEBUG
  863. TEXTMETRIC tm;
  864. // BisharaK: fix bugs#40706, 43200 -- Meta is assumed false for Glyph out.
  865. if (!(eto & ETO_GLYPH_INDEX))
  866. {
  867. switch (GetObjectType(hdc))
  868. {
  869. case OBJ_METADC:
  870. case OBJ_ENHMETADC:
  871. ASSERT(0 && "MetafileExtTextOutW wrapper need.");
  872. break;
  873. default:
  874. break;
  875. }
  876. }
  877. GetTextMetrics(hdc, &tm);
  878. if (tm.tmCharSet == SYMBOL_CHARSET)
  879. {
  880. ASSERT(0 && "SymbolExtTextOutW wrapper need.");
  881. }
  882. else if (tm.tmPitchAndFamily & TMPF_DEVICE)
  883. {
  884. ASSERT(0 && "DevExtTextOutW wrapper need.");
  885. }
  886. else
  887. #endif
  888. {
  889. // Font linking support for UI rendering
  890. fRet = _ExtTextOutWFontLink(hdc, xp, yp, eto, lprect, lpwch, cLen, lpdxp);
  891. }
  892. if (!fRet)
  893. fRet = _OtherExtTextOutW(hdc, xp, yp, eto, lprect, lpwch, cLen, lpdxp);
  894. return fRet;
  895. }
  896. BOOL TextOutWrap(HDC hdc, int xp, int yp, LPCWSTR lpwch, int cLen)
  897. {
  898. return ExtTextOutWrap(hdc, xp, yp, 0, NULL, lpwch, cLen, NULL);
  899. }
  900. typedef struct {
  901. RECT rcFormat; // Format rectangle.
  902. int cxTabLength; // Tab length in pixels.
  903. int iXSign;
  904. int iYSign;
  905. int cyLineHeight; // Height of a line based on DT_EXTERNALLEADING
  906. int cxMaxWidth; // Width of the format rectangle.
  907. int cxMaxExtent; // Width of the longest line drawn.
  908. int cxRightMargin; // Right margin in pixels (with proper sign) on DT_NOPREFIX flag.
  909. int cxOverhang; // Character overhang.
  910. } DRAWTEXTDATA, *LPDRAWTEXTDATA;
  911. #define CR 13
  912. #define LF 10
  913. #define DT_HFMTMASK 0x03
  914. #define DT_VFMTMASK 0x0C
  915. // FE support both Kanji and English mnemonic characters,
  916. // toggled from control panel. Both mnemonics are embedded in menu
  917. // resource templates. The following prefixes guide their parsing.
  918. #define CH_ENGLISHPREFIX 0x1E
  919. #define CH_KANJIPREFIX 0x1F
  920. #define CCHELLIPSIS 3
  921. static CONST WCHAR szEllipsis[CCHELLIPSIS+1] = TEXT("...");
  922. // Max length of a full path is around 260. But, most of the time, it will
  923. // be less than 128. So, we alloc only this much on stack. If the string is
  924. // longer, we alloc from local heap (which is slower).
  925. //
  926. // BOGUS: For international versions, we need to give some more margin here.
  927. //
  928. #define MAXBUFFSIZE 128
  929. /***************************************************************************\
  930. * There are word breaking characters which are compatible with
  931. * Japanese Windows 3.1 and FarEast Windows 95.
  932. *
  933. * SJ - Country Japan , Charset SHIFTJIS, Codepage 932.
  934. * GB - Country PRC , Charset GB2312 , Codepage 936.
  935. * B5 - Country Taiwan, Charset BIG5 , Codepage 950.
  936. * WS - Country Korea , Charset WANGSUNG, Codepage 949.
  937. * JB - Country Korea , Charset JOHAB , Codepage 1361. *** LATER ***
  938. *
  939. * [START BREAK CHARACTERS]
  940. *
  941. * These character should not be the last charatcer of the line.
  942. *
  943. * Unicode Japan PRC Taiwan Korea
  944. * -------+---------+---------+---------+---------+
  945. *
  946. * + ASCII
  947. *
  948. * U+0024 (SJ+0024) (WS+0024) Dollar sign
  949. * U+0028 (SJ+0028) (WS+0028) Opening parenthesis
  950. * U+003C (SJ+003C) Less-than sign
  951. * U+005C (SJ+005C) Backslash
  952. * U+005B (SJ+005B) (GB+005B) (WS+005B) Opening square bracket
  953. * U+007B (SJ+007B) (GB+007B) (WS+007B) Opening curly bracket
  954. *
  955. * + General punctuation
  956. *
  957. * U+2018 (WS+A1AE) Single Turned Comma Quotation Mark
  958. * U+201C (WS+A1B0) Double Comma Quotation Mark
  959. *
  960. * + CJK symbols and punctuation
  961. *
  962. * U+3008 (WS+A1B4) Opening Angle Bracket
  963. * U+300A (SJ+8173) (WS+A1B6) Opening Double Angle Bracket
  964. * U+300C (SJ+8175) (WS+A1B8) Opening Corner Bracket
  965. * U+300E (SJ+8177) (WS+A1BA) Opening White Corner Bracket
  966. * U+3010 (SJ+9179) (WS+A1BC) Opening Black Lenticular Bracket
  967. * U+3014 (SJ+816B) (WS+A1B2) Opening Tortoise Shell Bracket
  968. *
  969. * + Fullwidth ASCII variants
  970. *
  971. * U+FF04 (WS+A3A4) Fullwidth Dollar Sign
  972. * U+FF08 (SJ+8169) (WS+A3A8) Fullwidth opening parenthesis
  973. * U+FF1C (SJ+8183) Fullwidth less-than sign
  974. * U+FF3B (SJ+816D) (WS+A3DB) Fullwidth opening square bracket
  975. * U+FF5B (SJ+816F) (WS+A3FB) Fullwidth opening curly bracket
  976. *
  977. * + Halfwidth Katakana variants
  978. *
  979. * U+FF62 (SJ+00A2) Halfwidth Opening Corner Bracket
  980. *
  981. * + Fullwidth symbol variants
  982. *
  983. * U+FFE1 (WS+A1CC) Fullwidth Pound Sign
  984. * U+FFE6 (WS+A3DC) Fullwidth Won Sign
  985. *
  986. * [END BREAK CHARACTERS]
  987. *
  988. * These character should not be the top charatcer of the line.
  989. *
  990. * Unicode Japan PRC Taiwan Korea
  991. * -------+---------+---------+---------+---------+
  992. *
  993. * + ASCII
  994. *
  995. * U+0021 (SJ+0021) (GB+0021) (B5+0021) (WS+0021) Exclamation mark
  996. * U+0025 (WS+0025) Percent Sign
  997. * U+0029 (SJ+0029) (WS+0029) Closing parenthesis
  998. * U+002C (SJ+002C) (GB+002C) (B5+002C) (WS+002C) Comma
  999. * U+002E (SJ+002E) (GB+002E) (B5+002E) (WS+002E) Priod
  1000. * U+003A (WS+003A) Colon
  1001. * U+003B (WS+003B) Semicolon
  1002. * U+003E (SJ+003E) Greater-than sign
  1003. * U+003F (SJ+003F) (GB+003F) (B5+003F) (WS+003F) Question mark
  1004. * U+005D (SJ+005D) (GB+005D) (B5+005D) (WS+005D) Closing square bracket
  1005. * U+007D (SJ+007D) (GB+007D) (B5+007D) (WS+007D) Closing curly bracket
  1006. *
  1007. * + Latin1
  1008. *
  1009. * U+00A8 (GB+A1A7) Spacing diaeresis
  1010. * U+00B0 (WS+A1C6) Degree Sign
  1011. * U+00B7 (B5+A150) Middle Dot
  1012. *
  1013. * + Modifier letters
  1014. *
  1015. * U+02C7 (GB+A1A6) Modifier latter hacek
  1016. * U+02C9 (GB+A1A5) Modifier letter macron
  1017. *
  1018. * + General punctuation
  1019. *
  1020. * U+2013 (B5+A156) En Dash
  1021. * U+2014 (b5+A158) Em Dash
  1022. * U+2015 (GB+A1AA) Quotation dash
  1023. * U+2016 (GB+A1AC) Double vertical bar
  1024. * U+2018 (GB+A1AE) Single turned comma quotation mark
  1025. * U+2019 (GB+A1AF) (B5+A1A6) (WS+A1AF) Single comma quotation mark
  1026. * U+201D (GB+A1B1) (B5+A1A8) (WS+A1B1) Double comma quotation mark
  1027. * U+2022 (GB+A1A4) Bullet
  1028. * U+2025 (B5+A14C) Two Dot Leader
  1029. * U+2026 (GB+A1AD) (B5+A14B) Horizontal ellipsis
  1030. * U+2027 (B5+A145) Hyphenation Point
  1031. * U+2032 (B5+A1AC) (WS+A1C7) Prime
  1032. * U+2033 (WS+A1C8) Double Prime
  1033. *
  1034. * + Letterlike symbols
  1035. *
  1036. * U+2103 (WS+A1C9) Degrees Centigrade
  1037. *
  1038. * + Mathemetical opetartors
  1039. *
  1040. * U+2236 (GB+A1C3) Ratio
  1041. *
  1042. * + Form and Chart components
  1043. *
  1044. * U+2574 (B5+A15A) Forms Light Left
  1045. *
  1046. * + CJK symbols and punctuation
  1047. *
  1048. * U+3001 (SJ+8141) (GB+A1A2) (B5+A142) Ideographic comma
  1049. * U+3002 (SJ+8142) (GB+A1A3) (B5+A143) Ideographic period
  1050. * U+3003 (GB+A1A8) Ditto mark
  1051. * U+3005 (GB+A1A9) Ideographic iteration
  1052. * U+3009 (GB+A1B5) (B5+A172) (WS+A1B5) Closing angle bracket
  1053. * U+300B (SJ+8174) (GB+A1B7) (B5+A16E) (WS+A1B7) Closing double angle bracket
  1054. * U+300D (SJ+8176) (GB+A1B9) (B5+A176) (WS+A1B9) Closing corner bracket
  1055. * U+300F (SJ+8178) (GB+A1BB) (B5+A17A) (WS+A1BB) Closing white corner bracket
  1056. * U+3011 (SJ+817A) (GB+A1BF) (B5+A16A) (WS+A1BD) Closing black lenticular bracket
  1057. * U+3015 (SJ+816C) (GB+A1B3) (B5+A166) (WS+A1B3) Closing tortoise shell bracket
  1058. * U+3017 (GB+A1BD) Closing white lenticular bracket
  1059. * U+301E (B5+A1AA) Double Prime Quotation Mark
  1060. *
  1061. * + Hiragana
  1062. *
  1063. * U+309B (SJ+814A) Katakana-Hiragana voiced sound mark
  1064. * U+309C (SJ+814B) Katakana-Hiragana semi-voiced sound mark
  1065. *
  1066. * + CNS 11643 compatibility
  1067. *
  1068. * U+FE30 (B5+A14A) Glyph for Vertical 2 Dot Leader
  1069. * U+FE31 (B5+A157) Glyph For Vertical Em Dash
  1070. * U+FE33 (B5+A159) Glyph for Vertical Spacing Underscore
  1071. * U+FE34 (B5+A15B) Glyph for Vertical Spacing Wavy Underscore
  1072. * U+FE36 (B5+A160) Glyph For Vertical Closing Parenthesis
  1073. * U+FE38 (B5+A164) Glyph For Vertical Closing Curly Bracket
  1074. * U+FE3A (B5+A168) Glyph For Vertical Closing Tortoise Shell Bracket
  1075. * U+FE3C (B5+A16C) Glyph For Vertical Closing Black Lenticular Bracket
  1076. * U+FE3E (B5+A16E) Closing Double Angle Bracket
  1077. * U+FE40 (B5+A174) Glyph For Vertical Closing Angle Bracket
  1078. * U+FE42 (B5+A178) Glyph For Vertical Closing Corner Bracket
  1079. * U+FE44 (B5+A17C) Glyph For Vertical Closing White Corner Bracket
  1080. * U+FE4F (B5+A15C) Spacing Wavy Underscore
  1081. *
  1082. * + Small variants
  1083. *
  1084. * U+FE50 (B5+A14D) Small Comma
  1085. * U+FE51 (B5+A14E) Small Ideographic Comma
  1086. * U+FE52 (B5+A14F) Small Period
  1087. * U+FE54 (B5+A151) Small Semicolon
  1088. * U+FE55 (B5+A152) Small Colon
  1089. * U+FE56 (B5+A153) Small Question Mark
  1090. * U+FE57 (B5+A154) Small Exclamation Mark
  1091. * U+FE5A (B5+A17E) Small Closing Parenthesis
  1092. * U+FE5C (B5+A1A2) Small Closing Curly Bracket
  1093. * U+FE5E (B5+A1A4) Small Closing Tortoise Shell Bracket
  1094. *
  1095. * + Fullwidth ASCII variants
  1096. *
  1097. * U+FF01 (SJ+8149) (GB+A3A1) (B5+A149) (WS+A3A1) Fullwidth exclamation mark
  1098. * U+FF02 (GB+A3A2) Fullwidth Quotation mark
  1099. * U+FF05 (WS+A3A5) Fullwidth Percent Sign
  1100. * U+FF07 (GB+A3A7) Fullwidth Apostrophe
  1101. * U+FF09 (SJ+816A) (GB+A3A9) (B5+A15E) (WS+A3A9) Fullwidth Closing parenthesis
  1102. * U+FF0C (SJ+8143) (GB+A3AC) (B5+A141) (WS+A3AC) Fullwidth comma
  1103. * U+FF0D (GB+A3AD) Fullwidth Hyphen-minus
  1104. * U+FF0E (SJ+8144) (B5+A144) (WS+A3AE) Fullwidth period
  1105. * U+FF1A (GB+A3BA) (B4+A147) (WS+A3BA) Fullwidth colon
  1106. * U+FF1B (GB+A3BB) (B5+A146) (WS+A3BB) Fullwidth semicolon
  1107. * U+FF1E (SJ+8184) Fullwidth Greater-than sign
  1108. * U+FF1F (SJ+8148) (GB+A3BF) (B5+A148) (WS+A3BF) Fullwidth question mark
  1109. * U+FF3D (SJ+816E) (GB+A3DD) (WS+A3DD) Fullwidth Closing square bracket
  1110. * U+FF5C (B5+A155) Fullwidth Vertical Bar
  1111. * U+FF5D (SJ+8170) (B5+A162) (WS+A3FD) Fullwidth Closing curly bracket
  1112. * U+FF5E (GB+A1AB) Fullwidth Spacing tilde
  1113. *
  1114. * + Halfwidth Katakana variants
  1115. *
  1116. * U+FF61 (SJ+00A1) Halfwidth Ideographic period
  1117. * U+FF63 (SJ+00A3) Halfwidth Closing corner bracket
  1118. * U+FF64 (SJ+00A4) Halfwidth Ideographic comma
  1119. * U+FF9E (SJ+00DE) Halfwidth Katakana voiced sound mark
  1120. * U+FF9F (SJ+00DF) Halfwidth Katakana semi-voiced sound mark
  1121. *
  1122. * + Fullwidth symbol variants
  1123. *
  1124. * U+FFE0 (WS+A1CB) Fullwidth Cent Sign
  1125. *
  1126. \***************************************************************************/
  1127. /***************************************************************************\
  1128. * End Break table.
  1129. * These character should not be the top charatcer of the line.
  1130. \***************************************************************************/
  1131. CONST BYTE aASCII_Latin1_EndBreak[] = {
  1132. /* 00 0 1 2 3 4 5 6 7 8 9 A B C D E F */
  1133. /* 2X */ 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0,
  1134. /* 3X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1,
  1135. /* 4X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  1136. /* 5X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
  1137. /* 6X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  1138. /* 7X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
  1139. /* 8X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  1140. /* 9X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  1141. /* AX */ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
  1142. /* BX */ 1, 0, 0, 0, 0, 0, 0, 1
  1143. };
  1144. CONST BYTE aGeneralPunctuation_EndBreak[] = {
  1145. /* 20 0 1 2 3 4 5 6 7 8 9 A B C D E F */
  1146. /* 1X */ 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0,
  1147. /* 2X */ 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
  1148. /* 3X */ 0, 0, 1, 1
  1149. };
  1150. CONST BYTE aCJKSymbol_EndBreak[] = {
  1151. /* 30 0 1 2 3 4 5 6 7 8 9 A B C D E F */
  1152. /* 0X */ 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1,
  1153. /* 1X */ 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1
  1154. };
  1155. CONST BYTE aCNS11643_SmallVariants_EndBreak[] = {
  1156. /* FE 0 1 2 3 4 5 6 7 8 9 A B C D E F */
  1157. /* 3X */ 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
  1158. /* 4X */ 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
  1159. /* 5X */ 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1
  1160. };
  1161. CONST BYTE aFullWidthHalfWidthVariants_EndBreak[] = {
  1162. /* FF 0 1 2 3 4 5 6 7 8 9 A B C D E F */
  1163. /* 0X */ 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0,
  1164. /* 1X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1,
  1165. /* 2X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  1166. /* 3X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
  1167. /* 4X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  1168. /* 5X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0,
  1169. /* 6X */ 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  1170. /* 7X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  1171. /* 8X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  1172. /* 9X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1
  1173. };
  1174. /***************************************************************************\
  1175. * UserIsFELineBreak() - Detects Far East word breaking characters. *
  1176. * *
  1177. * History: *
  1178. * 10-Mar-1996 HideyukN Created. *
  1179. \***************************************************************************/
  1180. BOOL UserIsFELineBreakEnd(WCHAR wch)
  1181. {
  1182. switch (wch>>8)
  1183. {
  1184. case 0x00:
  1185. // Check if word breaking chars in ASCII or Latin1.
  1186. if ((wch >= 0x0021) && (wch <= 0x00B7))
  1187. return ((BOOL)(aASCII_Latin1_EndBreak[wch - 0x0021]));
  1188. else
  1189. return FALSE;
  1190. case 0x02:
  1191. // Check if work breaking chars in "Modifier letters"
  1192. if ((wch == 0x02C7) || (wch == 0x02C9))
  1193. return TRUE;
  1194. else
  1195. return FALSE;
  1196. case 0x20:
  1197. // Check if work breaking chars in "General punctuation"
  1198. if ((wch >= 0x2013) && (wch <= 0x2033))
  1199. return ((BOOL)(aGeneralPunctuation_EndBreak[wch - 0x2013]));
  1200. else
  1201. return FALSE;
  1202. case 0x21:
  1203. // Check if work breaking chars in "Letterlike symbols"
  1204. if (wch == 0x2103)
  1205. return TRUE;
  1206. else
  1207. return FALSE;
  1208. case 0x22:
  1209. // Check if work breaking chars in "Mathemetical opetartors"
  1210. if (wch == 0x2236)
  1211. return TRUE;
  1212. else
  1213. return FALSE;
  1214. case 0x25:
  1215. // Check if work breaking chars in "Form and Chart components"
  1216. if (wch == 0x2574)
  1217. return TRUE;
  1218. else
  1219. return FALSE;
  1220. case 0x30:
  1221. // Check if word breaking chars in "CJK symbols and punctuation"
  1222. // and Hiragana.
  1223. if ((wch >= 0x3001) && (wch <= 0x301E))
  1224. return ((BOOL)(aCJKSymbol_EndBreak[wch - 0x3001]));
  1225. else if ((wch == 0x309B) || (wch == 0x309C))
  1226. return TRUE;
  1227. else
  1228. return FALSE;
  1229. case 0xFE:
  1230. // Check if word breaking chars in "CNS 11643 compatibility"
  1231. // or "Small variants".
  1232. if ((wch >= 0xFE30) && (wch <= 0xFE5E))
  1233. return ((BOOL)(aCNS11643_SmallVariants_EndBreak[wch - 0xFE30]));
  1234. else
  1235. return FALSE;
  1236. case 0xFF:
  1237. // Check if word breaking chars in "Fullwidth ASCII variants",
  1238. // "Halfwidth Katakana variants" or "Fullwidth symbol variants".
  1239. if ((wch >= 0xFF01) && (wch <= 0xFF9F))
  1240. return ((BOOL)(aFullWidthHalfWidthVariants_EndBreak[wch - 0xFF01]));
  1241. else if (wch >= 0xFFE0)
  1242. return TRUE;
  1243. else
  1244. return FALSE;
  1245. default:
  1246. return FALSE;
  1247. }
  1248. }
  1249. #define UserIsFELineBreak(wChar) UserIsFELineBreakEnd(wChar)
  1250. typedef struct _FULLWIDTH_UNICODE {
  1251. WCHAR Start;
  1252. WCHAR End;
  1253. } FULLWIDTH_UNICODE, *PFULLWIDTH_UNICODE;
  1254. #define NUM_FULLWIDTH_UNICODES 4
  1255. CONST FULLWIDTH_UNICODE FullWidthUnicodes[] =
  1256. {
  1257. { 0x4E00, 0x9FFF }, // CJK_UNIFIED_IDOGRAPHS
  1258. { 0x3040, 0x309F }, // HIRAGANA
  1259. { 0x30A0, 0x30FF }, // KATAKANA
  1260. { 0xAC00, 0xD7A3 } // HANGUL
  1261. };
  1262. BOOL UserIsFullWidth(WCHAR wChar)
  1263. {
  1264. int index;
  1265. // Early out for ASCII.
  1266. if (wChar < 0x0080)
  1267. {
  1268. // if the character < 0x0080, it should be a halfwidth character.
  1269. return FALSE;
  1270. }
  1271. // Scan FullWdith definition table... most of FullWidth character is
  1272. // defined here... this is more faster than call NLS API.
  1273. for (index = 0; index < NUM_FULLWIDTH_UNICODES; index++)
  1274. {
  1275. if ((wChar >= FullWidthUnicodes[index].Start) && (wChar <= FullWidthUnicodes[index].End))
  1276. return TRUE;
  1277. }
  1278. // BUGBUG: We need one more case here to match NT5 implementation - beomoh
  1279. // if this Unicode character is mapped to Double-Byte character,
  1280. // this is also FullWidth character..
  1281. return FALSE;
  1282. }
  1283. LPCWSTR GetNextWordbreak(LPCWSTR lpch,
  1284. LPCWSTR lpchEnd,
  1285. DWORD dwFormat,
  1286. LPDRAWTEXTDATA lpDrawInfo)
  1287. {
  1288. /* ichNonWhite is used to make sure we always make progress. */
  1289. int ichNonWhite = 1;
  1290. int ichComplexBreak = 0; // Breaking opportunity for complex scripts
  1291. #if ((DT_WORDBREAK & ~0xff) != 0)
  1292. #error cannot use BOOLEAN for DT_WORDBREAK, or you should use "!!" before assigning it
  1293. #endif
  1294. BOOLEAN fBreakSpace = (BOOLEAN)(dwFormat & DT_WORDBREAK);
  1295. // If DT_WORDBREAK and DT_NOFULLWIDTHCHARBREAK are both set, we must
  1296. // stop assuming FullWidth characters as word as we're doing in
  1297. // NT4 and Win95. Instead, CR/LF and/or white space will only be
  1298. // a line-break characters.
  1299. BOOLEAN fDbcsCharBreak = (fBreakSpace && !(dwFormat & DT_NOFULLWIDTHCHARBREAK));
  1300. // We must terminate this loop before lpch == lpchEnd, otherwise, we may gp fault during *lpch.
  1301. while (lpch < lpchEnd)
  1302. {
  1303. switch (*lpch)
  1304. {
  1305. case CR:
  1306. case LF:
  1307. return lpch;
  1308. case '\t':
  1309. case ' ':
  1310. if (fBreakSpace)
  1311. return (lpch + ichNonWhite);
  1312. // FALL THRU //
  1313. default:
  1314. // Since most Japanese writing don't use space character
  1315. // to separate each word, we define each Kanji character
  1316. // as a word.
  1317. if (fDbcsCharBreak && UserIsFullWidth(*lpch))
  1318. {
  1319. if (!ichNonWhite)
  1320. return lpch;
  1321. // if the next character is the last character of this string,
  1322. // We return the character, even this is a "KINSOKU" charcter...
  1323. if ((lpch+1) != lpchEnd)
  1324. {
  1325. // Check next character of FullWidth character.
  1326. // if the next character is "KINSOKU" character, the character
  1327. // should be handled as a part of previous FullWidth character.
  1328. // Never handle is as A character, and should not be a Word also.
  1329. if (UserIsFELineBreak(*(lpch+1)))
  1330. {
  1331. // Then if the character is "KINSOKU" character, we return
  1332. // the next of this character,...
  1333. return (lpch + 1 + 1);
  1334. }
  1335. }
  1336. // Otherwise, we just return the chracter that is next of FullWidth
  1337. // Character. Because we treat A FullWidth chacter as A Word.
  1338. return (lpch + 1);
  1339. }
  1340. lpch++;
  1341. ichNonWhite = 0;
  1342. }
  1343. }
  1344. return lpch;
  1345. }
  1346. // This routine returns the count of accelerator mnemonics and the
  1347. // character location (starting at 0) of the character to underline.
  1348. // A single CH_PREFIX character will be striped and the following character
  1349. // underlined, all double CH_PREFIX character sequences will be replaced by
  1350. // a single CH_PREFIX (this is done by PSMTextOut). This routine is used
  1351. // to determine the actual character length of the string that will be
  1352. // printed, and the location the underline should be placed. Only
  1353. // cch characters from the input string will be processed. If the lpstrCopy
  1354. // parameter is non-NULL, this routine will make a printable copy of the
  1355. // string with all single prefix characters removed and all double prefix
  1356. // characters collapsed to a single character. If copying, a maximum
  1357. // character count must be specified which will limit the number of
  1358. // characters copied.
  1359. //
  1360. // The location of the single CH_PREFIX is returned in the low order
  1361. // word, and the count of CH_PREFIX characters that will be striped
  1362. // from the string during printing is in the hi order word. If the
  1363. // high order word is 0, the low order word is meaningless. If there
  1364. // were no single prefix characters (i.e. nothing to underline), the
  1365. // low order word will be -1 (to distinguish from location 0).
  1366. //
  1367. // These routines assume that there is only one single CH_PREFIX character
  1368. // in the string.
  1369. //
  1370. // WARNING! this rountine returns information in BYTE count not CHAR count
  1371. // (so it can easily be passed onto GreExtTextOutW which takes byte
  1372. // counts as well)
  1373. LONG GetPrefixCount(
  1374. LPCWSTR lpstr,
  1375. int cch,
  1376. LPWSTR lpstrCopy,
  1377. int charcopycount)
  1378. {
  1379. int chprintpos = 0; // Num of chars that will be printed
  1380. int chcount = 0; // Num of prefix chars that will be removed
  1381. int chprefixloc = -1; // Pos (in printed chars) of the prefix
  1382. WCHAR ch;
  1383. // If not copying, use a large bogus count...
  1384. if (lpstrCopy == NULL)
  1385. charcopycount = 32767;
  1386. while ((cch-- > 0) && *lpstr && charcopycount-- != 0)
  1387. {
  1388. // Is this guy a prefix character ?
  1389. if ((ch = *lpstr++) == CH_PREFIX)
  1390. {
  1391. // Yup - increment the count of characters removed during print.
  1392. chcount++;
  1393. // Is the next also a prefix char?
  1394. if (*lpstr != CH_PREFIX)
  1395. {
  1396. // Nope - this is a real one, mark its location.
  1397. chprefixloc = chprintpos;
  1398. }
  1399. else
  1400. {
  1401. // yup - simply copy it if copying.
  1402. if (lpstrCopy != NULL)
  1403. *(lpstrCopy++) = CH_PREFIX;
  1404. cch--;
  1405. lpstr++;
  1406. chprintpos++;
  1407. }
  1408. }
  1409. else if (ch == CH_ENGLISHPREFIX) // Still needs to be parsed
  1410. {
  1411. // Yup - increment the count of characters removed during print.
  1412. chcount++;
  1413. // Next character is a real one, mark its location.
  1414. chprefixloc = chprintpos;
  1415. }
  1416. else if (ch == CH_KANJIPREFIX) // Still needs to be parsed
  1417. {
  1418. // We only support Alpha Numeric(CH_ENGLISHPREFIX).
  1419. // no support for Kana(CH_KANJIPREFIX).
  1420. // Yup - increment the count of characters removed during print.
  1421. chcount++;
  1422. if(cch)
  1423. {
  1424. // don't copy the character
  1425. chcount++;
  1426. lpstr++;
  1427. cch--;
  1428. }
  1429. }
  1430. else
  1431. {
  1432. // Nope - just inc count of char. that will be printed
  1433. chprintpos++;
  1434. if (lpstrCopy != NULL)
  1435. *(lpstrCopy++) = ch;
  1436. }
  1437. }
  1438. if (lpstrCopy != NULL)
  1439. *lpstrCopy = 0;
  1440. // Return the character counts
  1441. return MAKELONG(chprefixloc, chcount);
  1442. }
  1443. // Returns total width of prefix character. Japanese Windows has
  1444. // three shortcut prefixes, '&',\036 and \037. They may have
  1445. // different width.
  1446. int KKGetPrefixWidth(HDC hdc, LPCWSTR lpStr, int cch)
  1447. {
  1448. SIZE size;
  1449. SIZE iPrefix1 = {-1L,-1L};
  1450. SIZE iPrefix2 = {-1L,-1L};
  1451. SIZE iPrefix3 = {-1L,-1L};
  1452. int iTotal = 0;
  1453. while (cch-- > 0 && *lpStr)
  1454. {
  1455. switch(*lpStr)
  1456. {
  1457. case CH_PREFIX:
  1458. if (lpStr[1] != CH_PREFIX)
  1459. {
  1460. if (iPrefix1.cx == -1)
  1461. GetTextExtentPointWrap(hdc, lpStr, 1, &iPrefix1);
  1462. iTotal += iPrefix1.cx;
  1463. }
  1464. else
  1465. {
  1466. lpStr++;
  1467. cch--;
  1468. }
  1469. break;
  1470. case CH_ENGLISHPREFIX:
  1471. if (iPrefix2.cx == -1)
  1472. GetTextExtentPointWrap(hdc, lpStr, 1, &iPrefix2);
  1473. iTotal += iPrefix2.cx;
  1474. break;
  1475. case CH_KANJIPREFIX:
  1476. if (iPrefix3.cx == -1)
  1477. GetTextExtentPointWrap(hdc, lpStr, 1, &iPrefix3);
  1478. iTotal += iPrefix3.cx;
  1479. // In NT, always alpha numeric mode, Then we have to sum
  1480. // KANA accel key prefix non visible char width.
  1481. // so always add the extent for next char.
  1482. GetTextExtentPointWrap(hdc, lpStr, 1, &size);
  1483. iTotal += size.cx;
  1484. break;
  1485. default:
  1486. // No need to taking care of Double byte since 2nd byte of
  1487. // DBC is grater than 0x2f but all shortcut keys are less
  1488. // than 0x30.
  1489. break;
  1490. }
  1491. lpStr++;
  1492. }
  1493. return iTotal;
  1494. }
  1495. // Outputs the text and puts and _ below the character with an &
  1496. // before it. Note that this routine isn't used for menus since menus
  1497. // have their own special one so that it is specialized and faster...
  1498. void PSMTextOut(
  1499. HDC hdc,
  1500. int xLeft,
  1501. int yTop,
  1502. LPWSTR lpsz,
  1503. int cch,
  1504. DWORD dwFlags)
  1505. {
  1506. int cx;
  1507. LONG textsize, result;
  1508. WCHAR achWorkBuffer[255];
  1509. WCHAR *pchOut = achWorkBuffer;
  1510. TEXTMETRICW textMetric;
  1511. SIZE size;
  1512. RECT rc;
  1513. COLORREF color;
  1514. if (dwFlags & DT_NOPREFIX)
  1515. {
  1516. TextOutWrap(hdc, xLeft, yTop, lpsz, cch);
  1517. return;
  1518. }
  1519. if (cch > sizeof(achWorkBuffer)/sizeof(WCHAR))
  1520. {
  1521. pchOut = (WCHAR*)LocalAlloc(LPTR, (cch+1) * sizeof(WCHAR));
  1522. if (pchOut == NULL)
  1523. return;
  1524. }
  1525. result = GetPrefixCount(lpsz, cch, pchOut, cch);
  1526. // DT_PREFIXONLY is a new 5.0 option used when switching from keyboard cues off to on.
  1527. if (!(dwFlags & DT_PREFIXONLY))
  1528. TextOutWrap(hdc, xLeft, yTop, pchOut, cch - HIWORD(result));
  1529. // Any true prefix characters to underline?
  1530. if (LOWORD(result) == 0xFFFF || dwFlags & DT_HIDEPREFIX)
  1531. {
  1532. if (pchOut != achWorkBuffer)
  1533. LocalFree(pchOut);
  1534. return;
  1535. }
  1536. if (!GetTextMetricsW(hdc, &textMetric))
  1537. {
  1538. textMetric.tmOverhang = 0;
  1539. textMetric.tmAscent = 0;
  1540. }
  1541. // For proportional fonts, find starting point of underline.
  1542. if (LOWORD(result) != 0)
  1543. {
  1544. // How far in does underline start (if not at 0th byte.).
  1545. GetTextExtentPointWrap(hdc, pchOut, LOWORD(result), &size);
  1546. xLeft += size.cx;
  1547. // Adjust starting point of underline if not at first char and there is
  1548. // an overhang. (Italics or bold fonts.)
  1549. xLeft = xLeft - textMetric.tmOverhang;
  1550. }
  1551. // Adjust for proportional font when setting the length of the underline and
  1552. // height of text.
  1553. GetTextExtentPointWrap(hdc, pchOut + LOWORD(result), 1, &size);
  1554. textsize = size.cx;
  1555. // Find the width of the underline character. Just subtract out the overhang
  1556. // divided by two so that we look better with italic fonts. This is not
  1557. // going to effect embolded fonts since their overhang is 1.
  1558. cx = LOWORD(textsize) - textMetric.tmOverhang / 2;
  1559. // Get height of text so that underline is at bottom.
  1560. yTop += textMetric.tmAscent + 1;
  1561. // Draw the underline using the foreground color.
  1562. SetRect(&rc, xLeft, yTop, xLeft+cx, yTop+1);
  1563. color = SetBkColor(hdc, GetTextColor(hdc));
  1564. ExtTextOutWrap(hdc, xLeft, yTop, ETO_OPAQUE, &rc, TEXT(""), 0, NULL);
  1565. SetBkColor(hdc, color);
  1566. if (pchOut != achWorkBuffer)
  1567. LocalFree(pchOut);
  1568. }
  1569. int DT_GetExtentMinusPrefixes(HDC hdc, LPCWSTR lpchStr, int cchCount, UINT wFormat, int iOverhang)
  1570. {
  1571. int iPrefixCount;
  1572. int cxPrefixes = 0;
  1573. WCHAR PrefixChar = CH_PREFIX;
  1574. SIZE size;
  1575. if (!(wFormat & DT_NOPREFIX) &&
  1576. (iPrefixCount = HIWORD(GetPrefixCount(lpchStr, cchCount, NULL, 0))))
  1577. {
  1578. // Kanji Windows has three shortcut prefixes...
  1579. if (g_fDBCSEnabled)
  1580. {
  1581. // 16bit apps compatibility
  1582. cxPrefixes = KKGetPrefixWidth(hdc, lpchStr, cchCount) - (iPrefixCount * iOverhang);
  1583. }
  1584. else
  1585. {
  1586. cxPrefixes = GetTextExtentPointWrap(hdc, &PrefixChar, 1, &size);
  1587. cxPrefixes = size.cx - iOverhang;
  1588. cxPrefixes *= iPrefixCount;
  1589. }
  1590. }
  1591. GetTextExtentPointWrap(hdc, lpchStr, cchCount, &size);
  1592. return (size.cx - cxPrefixes);
  1593. }
  1594. // This will draw the given string in the given location without worrying
  1595. // about the left/right justification. Gets the extent and returns it.
  1596. // If fDraw is TRUE and if NOT DT_CALCRECT, this draws the text.
  1597. // NOTE: This returns the extent minus Overhang.
  1598. int DT_DrawStr(HDC hdc, int xLeft, int yTop, LPCWSTR lpchStr,
  1599. int cchCount, BOOL fDraw, UINT wFormat,
  1600. LPDRAWTEXTDATA lpDrawInfo)
  1601. {
  1602. LPCWSTR lpch;
  1603. int iLen;
  1604. int cxExtent;
  1605. int xOldLeft = xLeft; // Save the xLeft given to compute the extent later
  1606. int xTabLength = lpDrawInfo->cxTabLength;
  1607. int iTabOrigin = lpDrawInfo->rcFormat.left;
  1608. // Check if the tabs need to be expanded
  1609. if (wFormat & DT_EXPANDTABS)
  1610. {
  1611. while (cchCount)
  1612. {
  1613. // Look for a tab
  1614. for (iLen = 0, lpch = lpchStr; iLen < cchCount; iLen++)
  1615. if(*lpch++ == TEXT('\t'))
  1616. break;
  1617. // Draw text, if any, upto the tab
  1618. if (iLen)
  1619. {
  1620. // Draw the substring taking care of the prefixes.
  1621. if (fDraw && !(wFormat & DT_CALCRECT)) // Only if we need to draw text
  1622. PSMTextOut(hdc, xLeft, yTop, (LPWSTR)lpchStr, iLen, wFormat);
  1623. // Get the extent of this sub string and add it to xLeft.
  1624. xLeft += DT_GetExtentMinusPrefixes(hdc, lpchStr, iLen, wFormat, lpDrawInfo->cxOverhang) - lpDrawInfo->cxOverhang;
  1625. }
  1626. //if a TAB was found earlier, calculate the start of next sub-string.
  1627. if (iLen < cchCount)
  1628. {
  1629. iLen++; // Skip the tab
  1630. if (xTabLength) // Tab length could be zero
  1631. xLeft = (((xLeft - iTabOrigin)/xTabLength) + 1)*xTabLength + iTabOrigin;
  1632. }
  1633. // Calculate the details of the string that remains to be drawn.
  1634. cchCount -= iLen;
  1635. lpchStr = lpch;
  1636. }
  1637. cxExtent = xLeft - xOldLeft;
  1638. }
  1639. else
  1640. {
  1641. // If required, draw the text
  1642. if (fDraw && !(wFormat & DT_CALCRECT))
  1643. PSMTextOut(hdc, xLeft, yTop, (LPWSTR)lpchStr, cchCount, wFormat);
  1644. // Compute the extent of the text.
  1645. cxExtent = DT_GetExtentMinusPrefixes(hdc, lpchStr, cchCount, wFormat, lpDrawInfo->cxOverhang) - lpDrawInfo->cxOverhang;
  1646. }
  1647. return cxExtent;
  1648. }
  1649. // This function draws one complete line with proper justification
  1650. void DT_DrawJustifiedLine(HDC hdc, int yTop, LPCWSTR lpchLineSt, int cchCount, UINT wFormat, LPDRAWTEXTDATA lpDrawInfo)
  1651. {
  1652. LPRECT lprc;
  1653. int cxExtent;
  1654. int xLeft;
  1655. lprc = &(lpDrawInfo->rcFormat);
  1656. xLeft = lprc->left;
  1657. // Handle the special justifications (right or centered) properly.
  1658. if (wFormat & (DT_CENTER | DT_RIGHT))
  1659. {
  1660. cxExtent = DT_DrawStr(hdc, xLeft, yTop, lpchLineSt, cchCount, FALSE, wFormat, lpDrawInfo)
  1661. + lpDrawInfo->cxOverhang;
  1662. if(wFormat & DT_CENTER)
  1663. xLeft = lprc->left + (((lprc->right - lprc->left) - cxExtent) >> 1);
  1664. else
  1665. xLeft = lprc->right - cxExtent;
  1666. }
  1667. else
  1668. xLeft = lprc->left;
  1669. // Draw the whole line.
  1670. cxExtent = DT_DrawStr(hdc, xLeft, yTop, lpchLineSt, cchCount, TRUE, wFormat, lpDrawInfo)
  1671. + lpDrawInfo->cxOverhang;
  1672. if (cxExtent > lpDrawInfo->cxMaxExtent)
  1673. lpDrawInfo->cxMaxExtent = cxExtent;
  1674. }
  1675. // This is called at the begining of DrawText(); This initializes the
  1676. // DRAWTEXTDATA structure passed to this function with all the required info.
  1677. BOOL DT_InitDrawTextInfo(
  1678. HDC hdc,
  1679. LPRECT lprc,
  1680. UINT wFormat,
  1681. LPDRAWTEXTDATA lpDrawInfo,
  1682. LPDRAWTEXTPARAMS lpDTparams)
  1683. {
  1684. SIZE sizeViewPortExt = {0, 0}, sizeWindowExt = {0, 0};
  1685. TEXTMETRICW tm;
  1686. LPRECT lprcDest;
  1687. int iTabLength = 8; // Default Tab length is 8 characters.
  1688. int iLeftMargin;
  1689. int iRightMargin;
  1690. if (lpDTparams)
  1691. {
  1692. // Only if DT_TABSTOP flag is mentioned, we must use the iTabLength field.
  1693. if (wFormat & DT_TABSTOP)
  1694. iTabLength = lpDTparams->iTabLength;
  1695. iLeftMargin = lpDTparams->iLeftMargin;
  1696. iRightMargin = lpDTparams->iRightMargin;
  1697. }
  1698. else
  1699. iLeftMargin = iRightMargin = 0;
  1700. // Get the View port and Window extents for the given DC
  1701. // If this call fails, hdc must be invalid
  1702. if (!GetViewportExtEx(hdc, &sizeViewPortExt))
  1703. return FALSE;
  1704. GetWindowExtEx(hdc, &sizeWindowExt);
  1705. // For the current mapping mode, find out the sign of x from left to right.
  1706. lpDrawInfo->iXSign = (((sizeViewPortExt.cx ^ sizeWindowExt.cx) & 0x80000000) ? -1 : 1);
  1707. // For the current mapping mode, find out the sign of y from top to bottom.
  1708. lpDrawInfo->iYSign = (((sizeViewPortExt.cy ^ sizeWindowExt.cy) & 0x80000000) ? -1 : 1);
  1709. // Calculate the dimensions of the current font in this DC.
  1710. GetTextMetrics(hdc, &tm);
  1711. // cyLineHeight is in pixels (This will be signed).
  1712. lpDrawInfo->cyLineHeight = (tm.tmHeight +
  1713. ((wFormat & DT_EXTERNALLEADING) ? tm.tmExternalLeading : 0)) * lpDrawInfo->iYSign;
  1714. // cxTabLength is the tab length in pixels (This will not be signed)
  1715. lpDrawInfo->cxTabLength = tm.tmAveCharWidth * iTabLength;
  1716. // Set the cxOverhang
  1717. lpDrawInfo->cxOverhang = tm.tmOverhang;
  1718. // Set up the format rectangle based on the margins.
  1719. lprcDest = &(lpDrawInfo->rcFormat);
  1720. *lprcDest = *lprc;
  1721. // We need to do the following only if the margins are given
  1722. if (iLeftMargin | iRightMargin)
  1723. {
  1724. lprcDest->left += iLeftMargin * lpDrawInfo->iXSign;
  1725. lprcDest->right -= (lpDrawInfo->cxRightMargin = iRightMargin * lpDrawInfo->iXSign);
  1726. }
  1727. else
  1728. lpDrawInfo->cxRightMargin = 0; // Initialize to zero.
  1729. // cxMaxWidth is unsigned.
  1730. lpDrawInfo->cxMaxWidth = (lprcDest->right - lprcDest->left) * lpDrawInfo->iXSign;
  1731. lpDrawInfo->cxMaxExtent = 0; // Initialize this to zero.
  1732. return TRUE;
  1733. }
  1734. // In the case of WORDWRAP, we need to treat the white spaces at the
  1735. // begining/end of each line specially. This function does that.
  1736. // lpStNext = points to the begining of next line.
  1737. // lpiCount = points to the count of characters in the current line.
  1738. LPCWSTR DT_AdjustWhiteSpaces(LPCWSTR lpStNext, LPINT lpiCount, UINT wFormat)
  1739. {
  1740. switch (wFormat & DT_HFMTMASK)
  1741. {
  1742. case DT_LEFT:
  1743. // Prevent a white space at the begining of a left justfied text.
  1744. // Is there a white space at the begining of next line......
  1745. if ((*lpStNext == L' ') || (*lpStNext == L'\t'))
  1746. {
  1747. // ...then, exclude it from next line.
  1748. lpStNext++;
  1749. }
  1750. break;
  1751. case DT_RIGHT:
  1752. // Prevent a white space at the end of a RIGHT justified text.
  1753. // Is there a white space at the end of current line,.......
  1754. if ((*(lpStNext-1) == L' ') || (*(lpStNext - 1) == L'\t'))
  1755. {
  1756. // .....then, Skip the white space from the current line.
  1757. (*lpiCount)--;
  1758. }
  1759. break;
  1760. case DT_CENTER:
  1761. // Exclude white spaces from the begining and end of CENTERed lines.
  1762. // If there is a white space at the end of current line.......
  1763. if ((*(lpStNext-1) == L' ') || (*(lpStNext - 1) == L'\t'))
  1764. (*lpiCount)--; //...., don't count it for justification.
  1765. // If there is a white space at the begining of next line.......
  1766. if ((*lpStNext == L' ') || (*lpStNext == L'\t'))
  1767. lpStNext++; //...., exclude it from next line.
  1768. break;
  1769. }
  1770. return lpStNext;
  1771. }
  1772. // A word needs to be broken across lines and this finds out where to break it.
  1773. LPCWSTR DT_BreakAWord(HDC hdc, LPCWSTR lpchText, int iLength, int iWidth, UINT wFormat, int iOverhang)
  1774. {
  1775. int iLow = 0, iHigh = iLength;
  1776. int iNew;
  1777. while ((iHigh - iLow) > 1)
  1778. {
  1779. iNew = iLow + (iHigh - iLow)/2;
  1780. if(DT_GetExtentMinusPrefixes(hdc, lpchText, iNew, wFormat, iOverhang) > iWidth)
  1781. iHigh = iNew;
  1782. else
  1783. iLow = iNew;
  1784. }
  1785. // If the width is too low, we must print atleast one char per line.
  1786. // Else, we will be in an infinite loop.
  1787. if(!iLow && iLength)
  1788. iLow = 1;
  1789. return (lpchText+iLow);
  1790. }
  1791. // This finds out the location where we can break a line.
  1792. // Returns LPCSTR to the begining of next line.
  1793. // Also returns via lpiLineLength, the length of the current line.
  1794. // NOTE: (lpstNextLineStart - lpstCurrentLineStart) is not equal to the
  1795. // line length; This is because, we exclude some white spaces at the begining
  1796. // and/or end of lines; Also, CR/LF is excluded from the line length.
  1797. LPWSTR DT_GetLineBreak(
  1798. HDC hdc,
  1799. LPCWSTR lpchLineStart,
  1800. int cchCount,
  1801. DWORD dwFormat,
  1802. LPINT lpiLineLength,
  1803. LPDRAWTEXTDATA lpDrawInfo)
  1804. {
  1805. LPCWSTR lpchText, lpchEnd, lpch, lpchLineEnd;
  1806. int cxStart, cxExtent, cxNewExtent;
  1807. BOOL fAdjustWhiteSpaces = FALSE;
  1808. WCHAR ch;
  1809. cxStart = lpDrawInfo->rcFormat.left;
  1810. cxExtent = cxNewExtent = 0;
  1811. lpchText = lpchLineStart;
  1812. lpchEnd = lpchLineStart + cchCount;
  1813. while(lpchText < lpchEnd)
  1814. {
  1815. lpchLineEnd = lpch = GetNextWordbreak(lpchText, lpchEnd, dwFormat, lpDrawInfo);
  1816. // DT_DrawStr does not return the overhang; Otherwise we will end up
  1817. // adding one overhang for every word in the string.
  1818. // For simulated Bold fonts, the summation of extents of individual
  1819. // words in a line is greater than the extent of the whole line. So,
  1820. // always calculate extent from the LineStart.
  1821. // BUGTAG: #6054 -- Win95B -- SANKAR -- 3/9/95 --
  1822. cxNewExtent = DT_DrawStr(hdc, cxStart, 0, lpchLineStart, (int)(((PBYTE)lpch - (PBYTE)lpchLineStart)/sizeof(WCHAR)),
  1823. FALSE, dwFormat, lpDrawInfo);
  1824. if ((dwFormat & DT_WORDBREAK) && ((cxNewExtent + lpDrawInfo->cxOverhang) > lpDrawInfo->cxMaxWidth))
  1825. {
  1826. // Are there more than one word in this line?
  1827. if (lpchText != lpchLineStart)
  1828. {
  1829. lpchLineEnd = lpch = lpchText;
  1830. fAdjustWhiteSpaces = TRUE;
  1831. }
  1832. else
  1833. {
  1834. //One word is longer than the maximum width permissible.
  1835. //See if we are allowed to break that single word.
  1836. if((dwFormat & DT_EDITCONTROL) && !(dwFormat & DT_WORD_ELLIPSIS))
  1837. {
  1838. lpchLineEnd = lpch = DT_BreakAWord(hdc, lpchText, (int)(((PBYTE)lpch - (PBYTE)lpchText)/sizeof(WCHAR)),
  1839. lpDrawInfo->cxMaxWidth - cxExtent, dwFormat, lpDrawInfo->cxOverhang); //Break that word
  1840. //Note: Since we broke in the middle of a word, no need to
  1841. // adjust for white spaces.
  1842. }
  1843. else
  1844. {
  1845. fAdjustWhiteSpaces = TRUE;
  1846. // Check if we need to end this line with ellipsis
  1847. if(dwFormat & DT_WORD_ELLIPSIS)
  1848. {
  1849. // Don't do this if already at the end of the string.
  1850. if (lpch < lpchEnd)
  1851. {
  1852. // If there are CR/LF at the end, skip them.
  1853. if ((ch = *lpch) == CR || ch == LF)
  1854. {
  1855. if ((++lpch < lpchEnd) && (*lpch == (WCHAR)(ch ^ (LF ^ CR))))
  1856. lpch++;
  1857. fAdjustWhiteSpaces = FALSE;
  1858. }
  1859. }
  1860. }
  1861. }
  1862. }
  1863. // Well! We found a place to break the line. Let us break from this loop;
  1864. break;
  1865. }
  1866. else
  1867. {
  1868. // Don't do this if already at the end of the string.
  1869. if (lpch < lpchEnd)
  1870. {
  1871. if ((ch = *lpch) == CR || ch == LF)
  1872. {
  1873. if ((++lpch < lpchEnd) && (*lpch == (WCHAR)(ch ^ (LF ^ CR))))
  1874. lpch++;
  1875. fAdjustWhiteSpaces = FALSE;
  1876. break;
  1877. }
  1878. }
  1879. }
  1880. // Point at the beginning of the next word.
  1881. lpchText = lpch;
  1882. cxExtent = cxNewExtent;
  1883. }
  1884. // Calculate the length of current line.
  1885. *lpiLineLength = (INT)((PBYTE)lpchLineEnd - (PBYTE)lpchLineStart)/sizeof(WCHAR);
  1886. // Adjust the line length and lpch to take care of spaces.
  1887. if(fAdjustWhiteSpaces && (lpch < lpchEnd))
  1888. lpch = DT_AdjustWhiteSpaces(lpch, lpiLineLength, dwFormat);
  1889. // return the begining of next line;
  1890. return (LPWSTR)lpch;
  1891. }
  1892. // This function checks whether the given string fits within the given
  1893. // width or we need to add end-ellipse. If it required end-ellipses, it
  1894. // returns TRUE and it returns the number of characters that are saved
  1895. // in the given string via lpCount.
  1896. BOOL NeedsEndEllipsis(
  1897. HDC hdc,
  1898. LPCWSTR lpchText,
  1899. LPINT lpCount,
  1900. LPDRAWTEXTDATA lpDTdata,
  1901. UINT wFormat)
  1902. {
  1903. int cchText;
  1904. int ichMin, ichMax, ichMid;
  1905. int cxMaxWidth;
  1906. int iOverhang;
  1907. int cxExtent;
  1908. SIZE size;
  1909. cchText = *lpCount; // Get the current count.
  1910. if (cchText == 0)
  1911. return FALSE;
  1912. cxMaxWidth = lpDTdata->cxMaxWidth;
  1913. iOverhang = lpDTdata->cxOverhang;
  1914. cxExtent = DT_GetExtentMinusPrefixes(hdc, lpchText, cchText, wFormat, iOverhang);
  1915. if (cxExtent <= cxMaxWidth)
  1916. return FALSE;
  1917. // Reserve room for the "..." ellipses;
  1918. // (Assumption: The ellipses don't have any prefixes!)
  1919. GetTextExtentPointWrap(hdc, szEllipsis, CCHELLIPSIS, &size);
  1920. cxMaxWidth -= size.cx - iOverhang;
  1921. // If no room for ellipses, always show first character.
  1922. //
  1923. ichMax = 1;
  1924. if (cxMaxWidth > 0)
  1925. {
  1926. // Binary search to find characters that will fit.
  1927. ichMin = 0;
  1928. ichMax = cchText;
  1929. while (ichMin < ichMax)
  1930. {
  1931. // Be sure to round up, to make sure we make progress in
  1932. // the loop if ichMax == ichMin + 1.
  1933. ichMid = (ichMin + ichMax + 1) / 2;
  1934. cxExtent = DT_GetExtentMinusPrefixes(hdc, lpchText, ichMid, wFormat, iOverhang);
  1935. if (cxExtent < cxMaxWidth)
  1936. ichMin = ichMid;
  1937. else
  1938. {
  1939. if (cxExtent > cxMaxWidth)
  1940. ichMax = ichMid - 1;
  1941. else
  1942. {
  1943. // Exact match up up to ichMid: just exit.
  1944. ichMax = ichMid;
  1945. break;
  1946. }
  1947. }
  1948. }
  1949. // Make sure we always show at least the first character...
  1950. if (ichMax < 1)
  1951. ichMax = 1;
  1952. }
  1953. *lpCount = ichMax;
  1954. return TRUE;
  1955. }
  1956. // Returns a pointer to the last component of a path string.
  1957. //
  1958. // in:
  1959. // path name, either fully qualified or not
  1960. //
  1961. // returns:
  1962. // pointer into the path where the path is. if none is found
  1963. // returns a poiter to the start of the path
  1964. //
  1965. // c:\foo\bar -> bar
  1966. // c:\foo -> foo
  1967. // c:\foo\ -> c:\foo\ (REVIEW: is this case busted?)
  1968. // c:\ -> c:\ (REVIEW: this case is strange)
  1969. // c: -> c:
  1970. // foo -> foo
  1971. LPWSTR PathFindFileName(LPCWSTR pPath, int cchText)
  1972. {
  1973. LPCWSTR pT;
  1974. for (pT = pPath; cchText > 0 && *pPath; pPath++, cchText--)
  1975. {
  1976. if ((pPath[0] == L'\\' || pPath[0] == L':') && pPath[1])
  1977. pT = pPath + 1;
  1978. }
  1979. return (LPWSTR)pT;
  1980. }
  1981. // This adds a path ellipse to the given path name.
  1982. // Returns TRUE if the resultant string's extent is less the the
  1983. // cxMaxWidth. FALSE, if otherwise.
  1984. int AddPathEllipsis(
  1985. HDC hdc,
  1986. LPWSTR lpszPath,
  1987. int cchText,
  1988. UINT wFormat,
  1989. int cxMaxWidth,
  1990. int iOverhang)
  1991. {
  1992. int iLen;
  1993. UINT dxFixed, dxEllipsis;
  1994. LPWSTR lpEnd; /* end of the unfixed string */
  1995. LPWSTR lpFixed; /* start of text that we always display */
  1996. BOOL bEllipsisIn;
  1997. int iLenFixed;
  1998. SIZE size;
  1999. lpFixed = PathFindFileName(lpszPath, cchText);
  2000. if (lpFixed != lpszPath)
  2001. lpFixed--; // point at the slash
  2002. else
  2003. return cchText;
  2004. lpEnd = lpFixed;
  2005. bEllipsisIn = FALSE;
  2006. iLenFixed = cchText - (int)(lpFixed - lpszPath);
  2007. dxFixed = DT_GetExtentMinusPrefixes(hdc, lpFixed, iLenFixed, wFormat, iOverhang);
  2008. // It is assumed that the "..." string does not have any prefixes ('&').
  2009. GetTextExtentPointWrap(hdc, szEllipsis, CCHELLIPSIS, &size);
  2010. dxEllipsis = size.cx - iOverhang;
  2011. while (TRUE)
  2012. {
  2013. iLen = dxFixed + DT_GetExtentMinusPrefixes(hdc, lpszPath, (int)((PBYTE)lpEnd - (PBYTE)lpszPath)/sizeof(WCHAR),
  2014. wFormat, iOverhang) - iOverhang;
  2015. if (bEllipsisIn)
  2016. iLen += dxEllipsis;
  2017. if (iLen <= cxMaxWidth)
  2018. break;
  2019. bEllipsisIn = TRUE;
  2020. if (lpEnd <= lpszPath)
  2021. {
  2022. // Things didn't fit.
  2023. lpEnd = lpszPath;
  2024. break;
  2025. }
  2026. // Step back a character.
  2027. lpEnd--;
  2028. }
  2029. if (bEllipsisIn && (lpEnd + CCHELLIPSIS < lpFixed))
  2030. {
  2031. // NOTE: the strings could over lap here. So, we use LCopyStruct.
  2032. MoveMemory((lpEnd + CCHELLIPSIS), lpFixed, iLenFixed * sizeof(WCHAR));
  2033. CopyMemory(lpEnd, szEllipsis, CCHELLIPSIS * sizeof(WCHAR));
  2034. cchText = (int)(lpEnd - lpszPath) + CCHELLIPSIS + iLenFixed;
  2035. // now we can NULL terminate the string
  2036. *(lpszPath + cchText) = TEXT('\0');
  2037. }
  2038. return cchText;
  2039. }
  2040. // This function returns the number of characters actually drawn.
  2041. int AddEllipsisAndDrawLine(
  2042. HDC hdc,
  2043. int yLine,
  2044. LPCWSTR lpchText,
  2045. int cchText,
  2046. DWORD dwDTformat,
  2047. LPDRAWTEXTDATA lpDrawInfo)
  2048. {
  2049. LPWSTR pEllipsis = NULL;
  2050. WCHAR szTempBuff[MAXBUFFSIZE];
  2051. LPWSTR lpDest;
  2052. BOOL fAlreadyCopied = FALSE;
  2053. // Check if this is a filename with a path AND
  2054. // Check if the width is too narrow to hold all the text.
  2055. if ((dwDTformat & DT_PATH_ELLIPSIS) &&
  2056. ((DT_GetExtentMinusPrefixes(hdc, lpchText, cchText, dwDTformat, lpDrawInfo->cxOverhang)) > lpDrawInfo->cxMaxWidth))
  2057. {
  2058. // We need to add Path-Ellipsis. See if we can do it in-place.
  2059. if (!(dwDTformat & DT_MODIFYSTRING)) {
  2060. // NOTE: When you add Path-Ellipsis, the string could grow by
  2061. // CCHELLIPSIS bytes.
  2062. if((cchText + CCHELLIPSIS + 1) <= MAXBUFFSIZE)
  2063. lpDest = szTempBuff;
  2064. else
  2065. {
  2066. // Alloc the buffer from local heap.
  2067. if(!(pEllipsis = (LPWSTR)LocalAlloc(LPTR, (cchText+CCHELLIPSIS+1)*sizeof(WCHAR))))
  2068. return 0;
  2069. lpDest = (LPWSTR)pEllipsis;
  2070. }
  2071. // Source String may not be NULL terminated. So, copy just
  2072. // the given number of characters.
  2073. CopyMemory(lpDest, lpchText, cchText*sizeof(WCHAR));
  2074. lpchText = lpDest; // lpchText points to the copied buff.
  2075. fAlreadyCopied = TRUE; // Local copy has been made.
  2076. }
  2077. // Add the path ellipsis now!
  2078. cchText = AddPathEllipsis(hdc, (LPWSTR)lpchText, cchText, dwDTformat, lpDrawInfo->cxMaxWidth, lpDrawInfo->cxOverhang);
  2079. }
  2080. // Check if end-ellipsis are to be added.
  2081. if ((dwDTformat & (DT_END_ELLIPSIS | DT_WORD_ELLIPSIS)) &&
  2082. NeedsEndEllipsis(hdc, lpchText, &cchText, lpDrawInfo, dwDTformat))
  2083. {
  2084. // We need to add end-ellipsis; See if we can do it in-place.
  2085. if (!(dwDTformat & DT_MODIFYSTRING) && !fAlreadyCopied)
  2086. {
  2087. // See if the string is small enough for the buff on stack.
  2088. if ((cchText+CCHELLIPSIS+1) <= MAXBUFFSIZE)
  2089. lpDest = szTempBuff; // If so, use it.
  2090. else {
  2091. // Alloc the buffer from local heap.
  2092. if (!(pEllipsis = (LPWSTR)LocalAlloc(LPTR, (cchText+CCHELLIPSIS+1)*sizeof(WCHAR))))
  2093. return 0;
  2094. lpDest = pEllipsis;
  2095. }
  2096. // Make a copy of the string in the local buff.
  2097. CopyMemory(lpDest, lpchText, cchText*sizeof(WCHAR));
  2098. lpchText = lpDest;
  2099. }
  2100. // Add an end-ellipsis at the proper place.
  2101. CopyMemory((LPWSTR)(lpchText+cchText), szEllipsis, (CCHELLIPSIS+1)*sizeof(WCHAR));
  2102. cchText += CCHELLIPSIS;
  2103. }
  2104. // Draw the line that we just formed.
  2105. DT_DrawJustifiedLine(hdc, yLine, lpchText, cchText, dwDTformat, lpDrawInfo);
  2106. // Free the block allocated for End-Ellipsis.
  2107. if (pEllipsis)
  2108. LocalFree(pEllipsis);
  2109. return cchText;
  2110. }
  2111. BOOL IsComplexScriptPresent(LPWSTR lpchText, int cchText)
  2112. {
  2113. if (g_bComplexPlatform) {
  2114. for (int i = 0; i < cchText; i++) {
  2115. if (InRange(lpchText[i], 0x0590, 0x0FFF)) {
  2116. return TRUE;
  2117. }
  2118. }
  2119. }
  2120. return FALSE;
  2121. }
  2122. int DrawTextExPrivWrap(
  2123. HDC hdc,
  2124. LPWSTR lpchText,
  2125. int cchText,
  2126. LPRECT lprc,
  2127. UINT dwDTformat,
  2128. LPDRAWTEXTPARAMS lpDTparams)
  2129. {
  2130. DRAWTEXTDATA DrawInfo;
  2131. WORD wFormat = LOWORD(dwDTformat);
  2132. LPWSTR lpchTextBegin;
  2133. LPWSTR lpchEnd;
  2134. LPWSTR lpchNextLineSt;
  2135. int iLineLength;
  2136. int iySign;
  2137. int yLine;
  2138. int yLastLineHeight;
  2139. HRGN hrgnClip;
  2140. int iLineCount;
  2141. RECT rc;
  2142. BOOL fLastLine;
  2143. WCHAR ch;
  2144. UINT oldAlign;
  2145. // On NT5, we use system API behavior including fontlink
  2146. if (g_bRunOnNT5)
  2147. return DrawTextExW(hdc, lpchText, cchText, lprc, dwDTformat, lpDTparams);
  2148. if ((cchText == 0) && lpchText && (*lpchText))
  2149. {
  2150. // infoview.exe passes lpchText that points to '\0'
  2151. // Lotus Notes doesn't like getting a zero return here
  2152. return 1;
  2153. }
  2154. if (cchText == -1)
  2155. cchText = lstrlenW(lpchText);
  2156. else if (lpchText[cchText - 1] == L'\0')
  2157. cchText--; // accommodate counting of NULLS for ME
  2158. // We got the string length, then check if it a complex string or not.
  2159. // If yes then call the system DrawTextEx API to do the job it knows how to
  2160. // handle the complex scripts.
  2161. if (IsComplexScriptPresent(lpchText, cchText))
  2162. {
  2163. //Call the system DrawtextExW
  2164. return DrawTextExW(hdc, lpchText, cchText, lprc, dwDTformat, lpDTparams);
  2165. }
  2166. if ((lpDTparams) && (lpDTparams->cbSize != sizeof(DRAWTEXTPARAMS)))
  2167. {
  2168. ASSERT(0 && "DrawTextExWorker: cbSize is invalid");
  2169. return 0;
  2170. }
  2171. // If DT_MODIFYSTRING is specified, then check for read-write pointer.
  2172. if ((dwDTformat & DT_MODIFYSTRING) &&
  2173. (dwDTformat & (DT_END_ELLIPSIS | DT_PATH_ELLIPSIS)))
  2174. {
  2175. if(IsBadWritePtr(lpchText, cchText))
  2176. {
  2177. ASSERT(0 && "DrawTextExWorker: For DT_MODIFYSTRING, lpchText must be read-write");
  2178. return 0;
  2179. }
  2180. }
  2181. // Initialize the DrawInfo structure.
  2182. if (!DT_InitDrawTextInfo(hdc, lprc, dwDTformat, (LPDRAWTEXTDATA)&DrawInfo, lpDTparams))
  2183. return 0;
  2184. // If the rect is too narrow or the margins are too wide.....Just forget it!
  2185. //
  2186. // If wordbreak is specified, the MaxWidth must be a reasonable value.
  2187. // This check is sufficient because this will allow CALCRECT and NOCLIP
  2188. // cases. --SANKAR.
  2189. //
  2190. // This also fixed all of our known problems with AppStudio.
  2191. if (DrawInfo.cxMaxWidth <= 0)
  2192. {
  2193. if (wFormat & DT_WORDBREAK)
  2194. {
  2195. ASSERT(0 && "DrawTextExW: FAILURE DrawInfo.cxMaxWidth <= 0");
  2196. return 1;
  2197. }
  2198. }
  2199. // if we're not doing the drawing, initialise the lpk-dll
  2200. if (dwDTformat & DT_RTLREADING)
  2201. oldAlign = SetTextAlign(hdc, TA_RTLREADING | GetTextAlign(hdc));
  2202. // If we need to clip, let us do that.
  2203. if (!(wFormat & DT_NOCLIP))
  2204. {
  2205. // Save clipping region so we can restore it later.
  2206. hrgnClip = CreateRectRgn(0,0,0,0);
  2207. if (hrgnClip != NULL)
  2208. {
  2209. if (GetClipRgn(hdc, hrgnClip) != 1)
  2210. {
  2211. DeleteObject(hrgnClip);
  2212. hrgnClip = (HRGN)-1;
  2213. }
  2214. rc = *lprc;
  2215. IntersectClipRect(hdc, rc.left, rc.top, rc.right, rc.bottom);
  2216. }
  2217. }
  2218. else
  2219. hrgnClip = NULL;
  2220. lpchTextBegin = lpchText;
  2221. lpchEnd = lpchText + cchText;
  2222. ProcessDrawText:
  2223. iLineCount = 0; // Reset number of lines to 1.
  2224. yLine = lprc->top;
  2225. if (wFormat & DT_SINGLELINE)
  2226. {
  2227. iLineCount = 1; // It is a single line.
  2228. // Process single line DrawText.
  2229. switch (wFormat & DT_VFMTMASK)
  2230. {
  2231. case DT_BOTTOM:
  2232. yLine = lprc->bottom - DrawInfo.cyLineHeight;
  2233. break;
  2234. case DT_VCENTER:
  2235. yLine = lprc->top + ((lprc->bottom - lprc->top - DrawInfo.cyLineHeight) / 2);
  2236. break;
  2237. }
  2238. cchText = AddEllipsisAndDrawLine(hdc, yLine, lpchText, cchText, dwDTformat, &DrawInfo);
  2239. yLine += DrawInfo.cyLineHeight;
  2240. lpchText += cchText;
  2241. }
  2242. else
  2243. {
  2244. // Multiline
  2245. // If the height of the rectangle is not an integral multiple of the
  2246. // average char height, then it is possible that the last line drawn
  2247. // is only partially visible. However, if DT_EDITCONTROL style is
  2248. // specified, then we must make sure that the last line is not drawn if
  2249. // it is going to be partially visible. This will help imitate the
  2250. // appearance of an edit control.
  2251. if (wFormat & DT_EDITCONTROL)
  2252. yLastLineHeight = DrawInfo.cyLineHeight;
  2253. else
  2254. yLastLineHeight = 0;
  2255. iySign = DrawInfo.iYSign;
  2256. fLastLine = FALSE;
  2257. // Process multiline DrawText.
  2258. while ((lpchText < lpchEnd) && (!fLastLine))
  2259. {
  2260. // Check if the line we are about to draw is the last line that needs
  2261. // to be drawn.
  2262. // Let us check if the display goes out of the clip rect and if so
  2263. // let us stop here, as an optimisation;
  2264. if (!(wFormat & DT_CALCRECT) && // We don't need to calc rect?
  2265. !(wFormat & DT_NOCLIP) && // Must we clip the display ?
  2266. // Are we outside the rect?
  2267. ((yLine + DrawInfo.cyLineHeight + yLastLineHeight)*iySign > (lprc->bottom*iySign)))
  2268. {
  2269. fLastLine = TRUE; // Let us quit this loop
  2270. }
  2271. // We do the Ellipsis processing only for the last line.
  2272. if (fLastLine && (dwDTformat & (DT_END_ELLIPSIS | DT_PATH_ELLIPSIS)))
  2273. lpchText += AddEllipsisAndDrawLine(hdc, yLine, lpchText, cchText, dwDTformat, &DrawInfo);
  2274. else
  2275. {
  2276. lpchNextLineSt = (LPWSTR)DT_GetLineBreak(hdc, lpchText, cchText, dwDTformat, &iLineLength, &DrawInfo);
  2277. // Check if we need to put ellipsis at the end of this line.
  2278. // Also check if this is the last line.
  2279. if ((dwDTformat & DT_WORD_ELLIPSIS) ||
  2280. ((lpchNextLineSt >= lpchEnd) && (dwDTformat & (DT_END_ELLIPSIS | DT_PATH_ELLIPSIS))))
  2281. AddEllipsisAndDrawLine(hdc, yLine, lpchText, iLineLength, dwDTformat, &DrawInfo);
  2282. else
  2283. DT_DrawJustifiedLine(hdc, yLine, lpchText, iLineLength, dwDTformat, &DrawInfo);
  2284. cchText -= (int)((PBYTE)lpchNextLineSt - (PBYTE)lpchText) / sizeof(WCHAR);
  2285. lpchText = lpchNextLineSt;
  2286. }
  2287. iLineCount++; // We draw one more line.
  2288. yLine += DrawInfo.cyLineHeight;
  2289. }
  2290. // For Win3.1 and NT compatibility, if the last char is a CR or a LF
  2291. // then the height returned includes one more line.
  2292. if (!(dwDTformat & DT_EDITCONTROL) &&
  2293. (lpchEnd > lpchTextBegin) && // If zero length it will fault.
  2294. (((ch = (*(lpchEnd-1))) == CR) || (ch == LF)))
  2295. yLine += DrawInfo.cyLineHeight;
  2296. }
  2297. // If DT_CALCRECT, modify width and height of rectangle to include
  2298. // all of the text drawn.
  2299. if (wFormat & DT_CALCRECT)
  2300. {
  2301. DrawInfo.rcFormat.right = DrawInfo.rcFormat.left + DrawInfo.cxMaxExtent * DrawInfo.iXSign;
  2302. lprc->right = DrawInfo.rcFormat.right + DrawInfo.cxRightMargin;
  2303. // If the Width is more than what was provided, we have to redo all
  2304. // the calculations, because, the number of lines can be less now.
  2305. // (We need to do this only if we have more than one line).
  2306. if((iLineCount > 1) && (DrawInfo.cxMaxExtent > DrawInfo.cxMaxWidth))
  2307. {
  2308. DrawInfo.cxMaxWidth = DrawInfo.cxMaxExtent;
  2309. lpchText = lpchTextBegin;
  2310. cchText = (int)((PBYTE)lpchEnd - (PBYTE)lpchTextBegin) / sizeof(WCHAR);
  2311. goto ProcessDrawText; // Start all over again!
  2312. }
  2313. lprc->bottom = yLine;
  2314. }
  2315. if (hrgnClip != NULL)
  2316. {
  2317. if (hrgnClip == (HRGN)-1)
  2318. ExtSelectClipRgn(hdc, NULL, RGN_COPY);
  2319. else
  2320. {
  2321. ExtSelectClipRgn(hdc, hrgnClip, RGN_COPY);
  2322. DeleteObject(hrgnClip);
  2323. }
  2324. }
  2325. if (dwDTformat & DT_RTLREADING)
  2326. SetTextAlign(hdc, oldAlign);
  2327. // Copy the number of characters actually drawn
  2328. if(lpDTparams != NULL)
  2329. lpDTparams->uiLengthDrawn = (UINT)((PBYTE)lpchText - (PBYTE)lpchTextBegin) / sizeof(WCHAR);
  2330. if (yLine == lprc->top)
  2331. return 1;
  2332. return (yLine - lprc->top);
  2333. }
  2334. int DrawTextWrap(HDC hdc, LPCWSTR lpchText, int cchText, LPRECT lprc, UINT format)
  2335. {
  2336. DRAWTEXTPARAMS DTparams;
  2337. LPDRAWTEXTPARAMS lpDTparams = NULL;
  2338. if (cchText < -1)
  2339. return(0);
  2340. if (format & DT_TABSTOP)
  2341. {
  2342. DTparams.cbSize = sizeof(DRAWTEXTPARAMS);
  2343. DTparams.iLeftMargin = DTparams.iRightMargin = 0;
  2344. DTparams.iTabLength = (format & 0xff00) >> 8;
  2345. lpDTparams = &DTparams;
  2346. format &= 0xffff00ff;
  2347. }
  2348. return DrawTextExPrivWrap(hdc, (LPWSTR)lpchText, cchText, lprc, format, lpDTparams);
  2349. }
  2350. #endif // FONT_LINK