Source code of Windows XP (NT5)
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.

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