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

2171 lines
76 KiB

  1. //
  2. // fontlink.cpp
  3. //
  4. #include "private.h"
  5. #include "fontlink.h"
  6. #include "xstring.h"
  7. #include "osver.h"
  8. #include "globals.h"
  9. #include "flshare.h"
  10. #include "ciccs.h"
  11. extern CCicCriticalSectionStatic g_cs;
  12. // Just like IMLangFontLink::MapFont, except has a hack for vertical fonts
  13. HRESULT MapFont(IMLangFontLink *pMLFontLink, HDC hdc, DWORD dwCodePages, HFONT hSrcFont, HFONT* phDestFont, BOOL *pfWrappedFont)
  14. {
  15. HRESULT hr;
  16. LOGFONT lfSrc;
  17. LOGFONT lfMap;
  18. HFONT hfontVert;
  19. TCHAR achVertFaceName[LF_FACESIZE];
  20. *pfWrappedFont = FALSE;
  21. hr = pMLFontLink->MapFont(hdc, dwCodePages, hSrcFont, phDestFont);
  22. if (hr != S_OK)
  23. return hr;
  24. // check, was this a vertical font?
  25. if (!GetObject(hSrcFont, sizeof(lfSrc), &lfSrc))
  26. return S_OK; // the MapFont call stil succeeded
  27. if (lfSrc.lfFaceName[0] != '@')
  28. return S_OK; // not a vertical font
  29. // is the mapped font vertical?
  30. if (!GetObject(*phDestFont, sizeof(lfMap), &lfMap))
  31. return S_OK; // the MapFont call stil succeeded
  32. if (lfMap.lfFaceName[0] == '@')
  33. return S_OK; // everything's ok
  34. // if we get here, src font is vertical but mlang has returned a
  35. // non-vertical font. Try to create a vertical one.
  36. // create a new '@' version
  37. memmove(&lfMap.lfFaceName[1], &lfMap.lfFaceName[0], LF_FACESIZE-sizeof(TCHAR));
  38. lfMap.lfFaceName[0] = '@';
  39. lfMap.lfFaceName[LF_FACESIZE-1] = 0;
  40. // save it for later
  41. memcpy(achVertFaceName, lfMap.lfFaceName, LF_FACESIZE*sizeof(TCHAR));
  42. // Issue: perf: one idea here would be to cache the lru font
  43. hfontVert = CreateFontIndirect(&lfMap);
  44. if (hfontVert == 0)
  45. return S_OK;
  46. // did it work?
  47. if (!GetObject(hfontVert, sizeof(lfMap), &lfMap))
  48. goto ExitDelete;
  49. if (lstrcmp(achVertFaceName, lfMap.lfFaceName) != 0)
  50. goto ExitDelete; // no vertical version available
  51. // got it, swap out the fonts
  52. pMLFontLink->ReleaseFont(*phDestFont);
  53. *phDestFont = hfontVert;
  54. *pfWrappedFont = TRUE;
  55. return S_OK;
  56. ExitDelete:
  57. DeleteObject(hfontVert);
  58. return S_OK;
  59. }
  60. // IMLangFontLink::ReleaseFont wrapper, matched with MapFont
  61. HRESULT ReleaseFont(IMLangFontLink *pMLFontLink, HFONT hfontMap, BOOL fWrappedFont)
  62. {
  63. if (!fWrappedFont)
  64. return pMLFontLink->ReleaseFont(hfontMap);
  65. // Issue: cache!
  66. DeleteObject(hfontMap);
  67. return S_OK;
  68. }
  69. #define CH_PREFIX L'&'
  70. BOOL LoadMLFontLink(IMLangFontLink **ppMLFontLink)
  71. {
  72. EnterCriticalSection(g_cs);
  73. *ppMLFontLink = NULL;
  74. if (NULL == g_pfnGetGlobalFontLinkObject)
  75. {
  76. if (g_hMlang == 0)
  77. {
  78. g_hMlang = LoadSystemLibrary(TEXT("MLANG.DLL"));
  79. }
  80. if (g_hMlang)
  81. {
  82. g_pfnGetGlobalFontLinkObject = (HRESULT (*)(IMLangFontLink **))GetProcAddress(g_hMlang, "GetGlobalFontLinkObject");
  83. }
  84. }
  85. Assert(g_hMlang);
  86. if (g_pfnGetGlobalFontLinkObject)
  87. {
  88. g_pfnGetGlobalFontLinkObject(ppMLFontLink);
  89. }
  90. g_bComplexPlatform = GetSystemModuleHandle(TEXT("LPK.DLL")) ? TRUE : FALSE;
  91. LeaveCriticalSection(g_cs);
  92. return (*ppMLFontLink)? TRUE: FALSE;
  93. }
  94. BOOL _OtherExtTextOutW(HDC hdc, int xp, int yp, UINT eto, CONST RECT *lprect,
  95. LPCWSTR lpwch, UINT cLen, CONST INT *lpdxp)
  96. {
  97. if (!(eto & ETO_GLYPH_INDEX) && cLen < 256 && lpwch[0] <= 127)
  98. {
  99. char lpchA[256];
  100. UINT ich;
  101. BOOL fAscii = TRUE;
  102. for (ich = 0; ich < cLen; ich++)
  103. {
  104. WCHAR wch = lpwch[ich];
  105. if (wch <= 127)
  106. lpchA[ich] = (char) wch;
  107. else
  108. {
  109. fAscii = FALSE;
  110. break;
  111. }
  112. }
  113. if (fAscii)
  114. return ExtTextOutA(hdc, xp, yp, eto, lprect, lpchA, cLen, lpdxp);
  115. }
  116. return (ExtTextOutW(hdc, xp, yp, eto, lprect, lpwch, cLen, lpdxp));
  117. }
  118. BOOL _ExtTextOutWFontLink(HDC hdc, int xp, int yp, UINT eto, CONST RECT *lprect,
  119. LPCWSTR lpwch, UINT cLen, CONST INT *lpdxp)
  120. {
  121. HFONT hfont = NULL;
  122. HFONT hfontSav = NULL;
  123. HFONT hfontMap = NULL;
  124. BOOL fRet = FALSE;
  125. UINT ta;
  126. int fDoTa = FALSE;
  127. int fQueryTa = TRUE;
  128. POINT pt;
  129. int cchDone;
  130. DWORD dwACP, dwFontCodePages, dwCodePages;
  131. long cchCodePages;
  132. IMLangFontLink *pMLFontLink = NULL;
  133. BOOL fWrappedFont;
  134. if (cLen == 0)
  135. return FALSE;
  136. if (!LoadMLFontLink(&pMLFontLink))
  137. return FALSE;
  138. hfont = (HFONT)GetCurrentObject(hdc, OBJ_FONT);
  139. pMLFontLink->GetFontCodePages(hdc, hfont, &dwFontCodePages);
  140. pMLFontLink->CodePageToCodePages(g_uiACP, &dwACP); // Give priority to CP_ACP
  141. // See if whole string can be handled by current font
  142. pMLFontLink->GetStrCodePages(lpwch, cLen, dwACP, &dwCodePages, &cchCodePages);
  143. // current font supports whole string ?
  144. if ((dwFontCodePages & dwCodePages) && cLen == (UINT)cchCodePages)
  145. {
  146. pMLFontLink->Release();
  147. return FALSE;
  148. }
  149. for (cchDone = 0; (UINT)cchDone < cLen; cchDone += cchCodePages)
  150. {
  151. pMLFontLink->GetStrCodePages(lpwch + cchDone, cLen - cchDone, dwACP, &dwCodePages, &cchCodePages);
  152. if (!(dwFontCodePages & dwCodePages))
  153. {
  154. MapFont(pMLFontLink, hdc, dwCodePages, hfont, &hfontMap, &fWrappedFont); // Issue: Baseline?
  155. hfontSav = (HFONT)SelectObject(hdc, hfontMap);
  156. }
  157. // cchCodePages shouldn't be 0
  158. ASSERT(cchCodePages);
  159. if (cchCodePages > 0)
  160. {
  161. // If rendering in multiple parts, need to use TA_UPDATECP
  162. if ((UINT)cchCodePages != cLen && fQueryTa)
  163. {
  164. ta = GetTextAlign(hdc);
  165. if ((ta & TA_UPDATECP) == 0) // Don't do the move if x, y aren't being used
  166. {
  167. MoveToEx(hdc, xp, yp, &pt);
  168. fDoTa = TRUE;
  169. }
  170. fQueryTa = FALSE;
  171. }
  172. if (fDoTa)
  173. SetTextAlign(hdc, ta | TA_UPDATECP);
  174. fRet = _OtherExtTextOutW(hdc, xp, yp, eto, lprect, lpwch + cchDone, cchCodePages,
  175. lpdxp ? lpdxp + cchDone : NULL);
  176. eto = eto & ~ETO_OPAQUE; // Don't do mupltiple OPAQUEs!!!
  177. if (fDoTa)
  178. SetTextAlign(hdc, ta);
  179. if (!fRet)
  180. break;
  181. }
  182. if (NULL != hfontSav)
  183. {
  184. SelectObject(hdc, hfontSav);
  185. ReleaseFont(pMLFontLink, hfontMap, fWrappedFont);
  186. hfontSav = NULL;
  187. }
  188. }
  189. if (fDoTa) // Don't do the move if x, y aren't being used
  190. MoveToEx(hdc, pt.x, pt.y, NULL);
  191. pMLFontLink->Release();
  192. return fRet;
  193. }
  194. BOOL FLExtTextOutW(HDC hdc, int xp, int yp, UINT eto, CONST RECT *lprect, LPCWSTR lpwch, UINT cLen, CONST INT *lpdxp)
  195. {
  196. BOOL fRet = FALSE;
  197. if (cLen == 0)
  198. {
  199. char chT;
  200. return ExtTextOutA(hdc, xp, yp, eto, lprect, &chT, cLen, lpdxp);
  201. }
  202. // Optimize for all < 128 case
  203. if (!(eto & ETO_GLYPH_INDEX) && cLen < 256 && lpwch[0] <= 127)
  204. {
  205. char lpchA[256];
  206. UINT ich;
  207. BOOL fAscii = TRUE;
  208. for (ich = 0; ich < cLen; ich++)
  209. {
  210. WCHAR wch = lpwch[ich];
  211. if (wch <= 127)
  212. lpchA[ich] = (char) wch;
  213. else
  214. {
  215. fAscii = FALSE;
  216. break;
  217. }
  218. }
  219. if (fAscii)
  220. return ExtTextOutA(hdc, xp, yp, eto, lprect, lpchA, cLen, lpdxp);
  221. }
  222. // Font linking support for UI rendering
  223. fRet = _ExtTextOutWFontLink(hdc, xp, yp, eto, lprect, lpwch, cLen, lpdxp);
  224. if (!fRet)
  225. fRet = _OtherExtTextOutW(hdc, xp, yp, eto, lprect, lpwch, cLen, lpdxp);
  226. return fRet;
  227. }
  228. BOOL FLTextOutW(HDC hdc, int xp, int yp, LPCWSTR lpwch, int cLen)
  229. {
  230. return FLExtTextOutW(hdc, xp, yp, 0, NULL, lpwch, cLen, NULL);
  231. }
  232. // this is a workaround for a win95 bug: gdi will fault if lpwch is NULL, even
  233. // when cch == 0
  234. inline BOOL SafeGetTextExtentPoint32W(HDC hdc, LPCWSTR lpwch, int cch, LPSIZE lpSize)
  235. {
  236. if (cch == 0)
  237. {
  238. memset(lpSize, 0, sizeof(*lpSize));
  239. return TRUE;
  240. }
  241. return GetTextExtentPoint32W(hdc, lpwch, cch, lpSize);
  242. }
  243. //
  244. // _GetTextExtentPointWFontLink
  245. //
  246. // This is a filter for GetTextExtentPointW() that does font linking.
  247. //
  248. // The input string is scanned and fonts are switched if not all chars are
  249. // supported by the current font in the HDC.
  250. //
  251. BOOL _GetTextExtentPointWFontLink(HDC hdc, LPCWSTR lpwch, int cch, LPSIZE lpSize)
  252. {
  253. HFONT hfont = NULL;
  254. HFONT hfontSav = NULL;
  255. HFONT hfontMap = NULL;
  256. BOOL fRet = FALSE;
  257. int cchDone;
  258. long cchCodePages;
  259. DWORD dwACP, dwFontCodePages, dwCodePages;
  260. SIZE size;
  261. IMLangFontLink *pMLFontLink = NULL;
  262. BOOL fWrappedFont;
  263. ASSERT(cch != 0);
  264. if (!LoadMLFontLink(&pMLFontLink))
  265. return FALSE;
  266. hfont = (HFONT)GetCurrentObject(hdc, OBJ_FONT);
  267. pMLFontLink->GetFontCodePages(hdc, hfont, &dwFontCodePages);
  268. pMLFontLink->CodePageToCodePages(g_uiACP, &dwACP); // Give priority to CP_ACP
  269. // See if whole string can be handled by current font
  270. pMLFontLink->GetStrCodePages(lpwch, cch, dwACP, &dwCodePages, &cchCodePages);
  271. // current font supports whole string ?
  272. if ((dwFontCodePages & dwCodePages) && cch == cchCodePages)
  273. {
  274. pMLFontLink->Release();
  275. return FALSE;
  276. }
  277. // Get Hight of DC font
  278. if (!(fRet = GetTextExtentPointA(hdc, " ", 1, lpSize)))
  279. {
  280. pMLFontLink->Release();
  281. return FALSE;
  282. }
  283. lpSize->cx = 0;
  284. for (cchDone = 0; cchDone < cch; cchDone += cchCodePages)
  285. {
  286. pMLFontLink->GetStrCodePages(lpwch + cchDone, cch - cchDone, dwACP, &dwCodePages, &cchCodePages);
  287. if (!(dwFontCodePages & dwCodePages))
  288. {
  289. MapFont(pMLFontLink, hdc, dwCodePages, hfont, &hfontMap, &fWrappedFont);
  290. hfontSav = (HFONT)SelectObject(hdc, hfontMap);
  291. }
  292. // cchCodePages shouldn't be 0
  293. ASSERT(cchCodePages);
  294. if (cchCodePages > 0)
  295. {
  296. fRet = SafeGetTextExtentPoint32W(hdc, lpwch + cchDone, cchCodePages, &size);
  297. lpSize->cx += size.cx;
  298. }
  299. if (NULL != hfontSav)
  300. {
  301. SelectObject(hdc, hfontSav);
  302. ReleaseFont(pMLFontLink, hfontMap, fWrappedFont);
  303. hfontSav = NULL;
  304. }
  305. }
  306. pMLFontLink->Release();
  307. return fRet;
  308. }
  309. BOOL FLGetTextExtentPoint32(HDC hdc, LPCWSTR lpwch, int cch, LPSIZE lpSize)
  310. {
  311. BOOL fRet = FALSE;
  312. if (cch)
  313. {
  314. // Optimize for all < 128 case
  315. if (cch < 256 && lpwch[0] <= 127)
  316. {
  317. char lpchA[256];
  318. int ich;
  319. BOOL fAscii = TRUE;
  320. for (ich = 0; ich < cch; ich++)
  321. {
  322. WCHAR wch = lpwch[ich];
  323. if (wch <= 127)
  324. lpchA[ich] = (char) wch;
  325. else
  326. {
  327. fAscii = FALSE;
  328. break;
  329. }
  330. }
  331. if (fAscii)
  332. return GetTextExtentPointA(hdc, lpchA, cch, lpSize);
  333. }
  334. fRet = _GetTextExtentPointWFontLink(hdc, lpwch, cch, lpSize);
  335. }
  336. if (!fRet)
  337. fRet = SafeGetTextExtentPoint32W(hdc, lpwch, cch, lpSize);
  338. return fRet;
  339. }
  340. //
  341. // Issue: Review for removing below big table and UsrFromWch() ...
  342. //
  343. __inline BOOL FChsDbcs(UINT chs)
  344. {
  345. return (chs == SHIFTJIS_CHARSET ||
  346. chs == HANGEUL_CHARSET ||
  347. chs == CHINESEBIG5_CHARSET ||
  348. chs == GB2312_CHARSET);
  349. }
  350. __inline int FChsBiDi(int chs)
  351. {
  352. return (chs == ARABIC_CHARSET ||
  353. chs == HEBREW_CHARSET);
  354. }
  355. __inline BOOL FCpgChinese(UINT cpg)
  356. {
  357. if (cpg == CP_ACP)
  358. cpg = GetACP();
  359. return (cpg == CP_TAIWAN || cpg == CP_CHINA);
  360. }
  361. __inline BOOL FCpgTaiwan(UINT cpg)
  362. {
  363. if (cpg == CP_ACP)
  364. cpg = GetACP();
  365. return (cpg == CP_TAIWAN);
  366. }
  367. __inline BOOL FCpgPRC(UINT cpg)
  368. {
  369. if (cpg == CP_ACP)
  370. cpg = GetACP();
  371. return (cpg == CP_CHINA);
  372. }
  373. __inline BOOL FCpgFarEast(UINT cpg)
  374. {
  375. if (cpg == CP_ACP)
  376. cpg = GetACP();
  377. return (cpg == CP_JAPAN || cpg == CP_TAIWAN || cpg == CP_CHINA ||
  378. cpg == CP_KOREA || cpg == CP_MAC_JAPAN);
  379. }
  380. __inline BOOL FCpgDbcs(UINT cpg)
  381. {
  382. return (cpg == CP_JAPAN ||
  383. cpg == CP_KOREA ||
  384. cpg == CP_TAIWAN ||
  385. cpg == CP_CHINA);
  386. }
  387. __inline int FCpgBiDi(int cpg)
  388. {
  389. return (cpg == CP_ARABIC ||
  390. cpg == CP_HEBREW);
  391. }
  392. HFONT GetBiDiFont(HDC hdc)
  393. {
  394. HFONT hfont = NULL;
  395. HFONT hfontTmp;
  396. LOGFONT lf;
  397. hfontTmp = (HFONT)GetCurrentObject(hdc, OBJ_FONT);
  398. GetObject(hfontTmp, sizeof(lf), &lf);
  399. // Issue: Should I loop on the string to check if it contains BiDi chars?
  400. if ( !FChsBiDi(lf.lfCharSet))
  401. {
  402. lf.lfCharSet = DEFAULT_CHARSET;
  403. hfont = CreateFontIndirect(&lf);
  404. }
  405. return hfont;
  406. }
  407. static const WCHAR szEllipsis[CCHELLIPSIS+1] = L"...";
  408. /***************************************************************************\
  409. * There are word breaking characters which are compatible with
  410. * Japanese Windows 3.1 and FarEast Windows 95.
  411. *
  412. * SJ - Country Japan , Charset SHIFTJIS, Codepage 932.
  413. * GB - Country PRC , Charset GB2312 , Codepage 936.
  414. * B5 - Taiwan, Charset BIG5 , Codepage 950.
  415. * WS - Country Korea , Charset WANGSUNG, Codepage 949.
  416. * JB - Country Korea , Charset JOHAB , Codepage 1361. *** LATER ***
  417. *
  418. * [START BREAK CHARACTERS]
  419. *
  420. * These character should not be the last charatcer of the line.
  421. *
  422. * Unicode Japan PRC Taiwan Korea
  423. * -------+---------+---------+---------+---------+
  424. *
  425. * + ASCII
  426. *
  427. * U+0024 (SJ+0024) (WS+0024) Dollar sign
  428. * U+0028 (SJ+0028) (WS+0028) Opening parenthesis
  429. * U+003C (SJ+003C) Less-than sign
  430. * U+005C (SJ+005C) Backslash
  431. * U+005B (SJ+005B) (GB+005B) (WS+005B) Opening square bracket
  432. * U+007B (SJ+007B) (GB+007B) (WS+007B) Opening curly bracket
  433. *
  434. * + General punctuation
  435. *
  436. * U+2018 (WS+A1AE) Single Turned Comma Quotation Mark
  437. * U+201C (WS+A1B0) Double Comma Quotation Mark
  438. *
  439. * + CJK symbols and punctuation
  440. *
  441. * U+3008 (WS+A1B4) Opening Angle Bracket
  442. * U+300A (SJ+8173) (WS+A1B6) Opening Double Angle Bracket
  443. * U+300C (SJ+8175) (WS+A1B8) Opening Corner Bracket
  444. * U+300E (SJ+8177) (WS+A1BA) Opening White Corner Bracket
  445. * U+3010 (SJ+9179) (WS+A1BC) Opening Black Lenticular Bracket
  446. * U+3014 (SJ+816B) (WS+A1B2) Opening Tortoise Shell Bracket
  447. *
  448. * + Fullwidth ASCII variants
  449. *
  450. * U+FF04 (WS+A3A4) Fullwidth Dollar Sign
  451. * U+FF08 (SJ+8169) (WS+A3A8) Fullwidth opening parenthesis
  452. * U+FF1C (SJ+8183) Fullwidth less-than sign
  453. * U+FF3B (SJ+816D) (WS+A3DB) Fullwidth opening square bracket
  454. * U+FF5B (SJ+816F) (WS+A3FB) Fullwidth opening curly bracket
  455. *
  456. * + Halfwidth Katakana variants
  457. *
  458. * U+FF62 (SJ+00A2) Halfwidth Opening Corner Bracket
  459. *
  460. * + Fullwidth symbol variants
  461. *
  462. * U+FFE1 (WS+A1CC) Fullwidth Pound Sign
  463. * U+FFE6 (WS+A3DC) Fullwidth Won Sign
  464. *
  465. * [END BREAK CHARACTERS]
  466. *
  467. * These character should not be the top charatcer of the line.
  468. *
  469. * Unicode Japan PRC Taiwan Korea
  470. * -------+---------+---------+---------+---------+
  471. *
  472. * + ASCII
  473. *
  474. * U+0021 (SJ+0021) (GB+0021) (B5+0021) (WS+0021) Exclamation mark
  475. * U+0025 (WS+0025) Percent Sign
  476. * U+0029 (SJ+0029) (WS+0029) Closing parenthesis
  477. * U+002C (SJ+002C) (GB+002C) (B5+002C) (WS+002C) Comma
  478. * U+002E (SJ+002E) (GB+002E) (B5+002E) (WS+002E) Priod
  479. * U+003A (WS+003A) Colon
  480. * U+003B (WS+003B) Semicolon
  481. * U+003E (SJ+003E) Greater-than sign
  482. * U+003F (SJ+003F) (GB+003F) (B5+003F) (WS+003F) Question mark
  483. * U+005D (SJ+005D) (GB+005D) (B5+005D) (WS+005D) Closing square bracket
  484. * U+007D (SJ+007D) (GB+007D) (B5+007D) (WS+007D) Closing curly bracket
  485. *
  486. * + Latin1
  487. *
  488. * U+00A8 (GB+A1A7) Spacing diaeresis
  489. * U+00B0 (WS+A1C6) Degree Sign
  490. * U+00B7 (B5+A150) Middle Dot
  491. *
  492. * + Modifier letters
  493. *
  494. * U+02C7 (GB+A1A6) Modifier latter hacek
  495. * U+02C9 (GB+A1A5) Modifier letter macron
  496. *
  497. * + General punctuation
  498. *
  499. * U+2013 (B5+A156) En Dash
  500. * U+2014 (b5+A158) Em Dash
  501. * U+2015 (GB+A1AA) Quotation dash
  502. * U+2016 (GB+A1AC) Double vertical bar
  503. * U+2018 (GB+A1AE) Single turned comma quotation mark
  504. * U+2019 (GB+A1AF) (B5+A1A6) (WS+A1AF) Single comma quotation mark
  505. * U+201D (GB+A1B1) (B5+A1A8) (WS+A1B1) Double comma quotation mark
  506. * U+2022 (GB+A1A4) Bullet
  507. * U+2025 (B5+A14C) Two Dot Leader
  508. * U+2026 (GB+A1AD) (B5+A14B) Horizontal ellipsis
  509. * U+2027 (B5+A145) Hyphenation Point
  510. * U+2032 (B5+A1AC) (WS+A1C7) Prime
  511. * U+2033 (WS+A1C8) Double Prime
  512. *
  513. * + Letterlike symbols
  514. *
  515. * U+2103 (WS+A1C9) Degrees Centigrade
  516. *
  517. * + Mathemetical opetartors
  518. *
  519. * U+2236 (GB+A1C3) Ratio
  520. *
  521. * + Form and Chart components
  522. *
  523. * U+2574 (B5+A15A) Forms Light Left
  524. *
  525. * + CJK symbols and punctuation
  526. *
  527. * U+3001 (SJ+8141) (GB+A1A2) (B5+A142) Ideographic comma
  528. * U+3002 (SJ+8142) (GB+A1A3) (B5+A143) Ideographic period
  529. * U+3003 (GB+A1A8) Ditto mark
  530. * U+3005 (GB+A1A9) Ideographic iteration
  531. * U+3009 (GB+A1B5) (B5+A172) (WS+A1B5) Closing angle bracket
  532. * U+300B (SJ+8174) (GB+A1B7) (B5+A16E) (WS+A1B7) Closing double angle bracket
  533. * U+300D (SJ+8176) (GB+A1B9) (B5+A176) (WS+A1B9) Closing corner bracket
  534. * U+300F (SJ+8178) (GB+A1BB) (B5+A17A) (WS+A1BB) Closing white corner bracket
  535. * U+3011 (SJ+817A) (GB+A1BF) (B5+A16A) (WS+A1BD) Closing black lenticular bracket
  536. * U+3015 (SJ+816C) (GB+A1B3) (B5+A166) (WS+A1B3) Closing tortoise shell bracket
  537. * U+3017 (GB+A1BD) Closing white lenticular bracket
  538. * U+301E (B5+A1AA) Double Prime Quotation Mark
  539. *
  540. * + Hiragana
  541. *
  542. * U+309B (SJ+814A) Katakana-Hiragana voiced sound mark
  543. * U+309C (SJ+814B) Katakana-Hiragana semi-voiced sound mark
  544. *
  545. * + CNS 11643 compatibility
  546. *
  547. * U+FE30 (B5+A14A) Glyph for Vertical 2 Dot Leader
  548. * U+FE31 (B5+A157) Glyph For Vertical Em Dash
  549. * U+FE33 (B5+A159) Glyph for Vertical Spacing Underscore
  550. * U+FE34 (B5+A15B) Glyph for Vertical Spacing Wavy Underscore
  551. * U+FE36 (B5+A160) Glyph For Vertical Closing Parenthesis
  552. * U+FE38 (B5+A164) Glyph For Vertical Closing Curly Bracket
  553. * U+FE3A (B5+A168) Glyph For Vertical Closing Tortoise Shell Bracket
  554. * U+FE3C (B5+A16C) Glyph For Vertical Closing Black Lenticular Bracket
  555. * U+FE3E (B5+A16E) Closing Double Angle Bracket
  556. * U+FE40 (B5+A174) Glyph For Vertical Closing Angle Bracket
  557. * U+FE42 (B5+A178) Glyph For Vertical Closing Corner Bracket
  558. * U+FE44 (B5+A17C) Glyph For Vertical Closing White Corner Bracket
  559. * U+FE4F (B5+A15C) Spacing Wavy Underscore
  560. *
  561. * + Small variants
  562. *
  563. * U+FE50 (B5+A14D) Small Comma
  564. * U+FE51 (B5+A14E) Small Ideographic Comma
  565. * U+FE52 (B5+A14F) Small Period
  566. * U+FE54 (B5+A151) Small Semicolon
  567. * U+FE55 (B5+A152) Small Colon
  568. * U+FE56 (B5+A153) Small Question Mark
  569. * U+FE57 (B5+A154) Small Exclamation Mark
  570. * U+FE5A (B5+A17E) Small Closing Parenthesis
  571. * U+FE5C (B5+A1A2) Small Closing Curly Bracket
  572. * U+FE5E (B5+A1A4) Small Closing Tortoise Shell Bracket
  573. *
  574. * + Fullwidth ASCII variants
  575. *
  576. * U+FF01 (SJ+8149) (GB+A3A1) (B5+A149) (WS+A3A1) Fullwidth exclamation mark
  577. * U+FF02 (GB+A3A2) Fullwidth Quotation mark
  578. * U+FF05 (WS+A3A5) Fullwidth Percent Sign
  579. * U+FF07 (GB+A3A7) Fullwidth Apostrophe
  580. * U+FF09 (SJ+816A) (GB+A3A9) (B5+A15E) (WS+A3A9) Fullwidth Closing parenthesis
  581. * U+FF0C (SJ+8143) (GB+A3AC) (B5+A141) (WS+A3AC) Fullwidth comma
  582. * U+FF0D (GB+A3AD) Fullwidth Hyphen-minus
  583. * U+FF0E (SJ+8144) (B5+A144) (WS+A3AE) Fullwidth period
  584. * U+FF1A (GB+A3BA) (B4+A147) (WS+A3BA) Fullwidth colon
  585. * U+FF1B (GB+A3BB) (B5+A146) (WS+A3BB) Fullwidth semicolon
  586. * U+FF1E (SJ+8184) Fullwidth Greater-than sign
  587. * U+FF1F (SJ+8148) (GB+A3BF) (B5+A148) (WS+A3BF) Fullwidth question mark
  588. * U+FF3D (SJ+816E) (GB+A3DD) (WS+A3DD) Fullwidth Closing square bracket
  589. * U+FF5C (B5+A155) Fullwidth Vertical Bar
  590. * U+FF5D (SJ+8170) (B5+A162) (WS+A3FD) Fullwidth Closing curly bracket
  591. * U+FF5E (GB+A1AB) Fullwidth Spacing tilde
  592. *
  593. * + Halfwidth Katakana variants
  594. *
  595. * U+FF61 (SJ+00A1) Halfwidth Ideographic period
  596. * U+FF63 (SJ+00A3) Halfwidth Closing corner bracket
  597. * U+FF64 (SJ+00A4) Halfwidth Ideographic comma
  598. * U+FF9E (SJ+00DE) Halfwidth Katakana voiced sound mark
  599. * U+FF9F (SJ+00DF) Halfwidth Katakana semi-voiced sound mark
  600. *
  601. * + Fullwidth symbol variants
  602. *
  603. * U+FFE0 (WS+A1CB) Fullwidth Cent Sign
  604. *
  605. \***************************************************************************/
  606. #if 0 // not currently used --- FYI only
  607. /***************************************************************************\
  608. * Start Break table
  609. * These character should not be the last charatcer of the line.
  610. \***************************************************************************/
  611. CONST BYTE aASCII_StartBreak[] = {
  612. /* 00 0 1 2 3 4 5 6 7 8 9 A B C D E F */
  613. /* 2X */ 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
  614. /* 3X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
  615. /* 4X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  616. /* 5X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0,
  617. /* 6X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  618. /* 7X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
  619. };
  620. CONST BYTE aCJKSymbol_StartBreak[] = {
  621. /* 30 0 1 2 3 4 5 6 7 8 9 A B C D E F */
  622. /* 0X */ 1, 0, 1, 0, 1, 0, 1, 0,
  623. /* 1X */ 1, 0, 0, 0, 1
  624. };
  625. CONST BYTE aFullWidthHalfWidthVariants_StartBreak[] = {
  626. /* FF 0 1 2 3 4 5 6 7 8 9 A B C D E F */
  627. /* 0X */ 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
  628. /* 1X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
  629. /* 2X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  630. /* 3X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
  631. /* 4X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  632. /* 5X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
  633. /* 6X */ 0, 0, 1
  634. };
  635. #endif
  636. /***************************************************************************\
  637. * End Break table.
  638. * These character should not be the top charatcer of the line.
  639. \***************************************************************************/
  640. CONST BYTE aASCII_Latin1_EndBreak[] = {
  641. /* 00 0 1 2 3 4 5 6 7 8 9 A B C D E F */
  642. /* 2X */ 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0,
  643. /* 3X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1,
  644. /* 4X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  645. /* 5X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
  646. /* 6X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  647. /* 7X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
  648. /* 8X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  649. /* 9X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  650. /* AX */ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
  651. /* BX */ 1, 0, 0, 0, 0, 0, 0, 1
  652. };
  653. CONST BYTE aGeneralPunctuation_EndBreak[] = {
  654. /* 20 0 1 2 3 4 5 6 7 8 9 A B C D E F */
  655. /* 1X */ 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0,
  656. /* 2X */ 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
  657. /* 3X */ 0, 0, 1, 1
  658. };
  659. CONST BYTE aCJKSymbol_EndBreak[] = {
  660. /* 30 0 1 2 3 4 5 6 7 8 9 A B C D E F */
  661. /* 0X */ 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1,
  662. /* 1X */ 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1
  663. };
  664. CONST BYTE aCNS11643_SmallVariants_EndBreak[] = {
  665. /* FE 0 1 2 3 4 5 6 7 8 9 A B C D E F */
  666. /* 3X */ 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
  667. /* 4X */ 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
  668. /* 5X */ 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1
  669. };
  670. CONST BYTE aFullWidthHalfWidthVariants_EndBreak[] = {
  671. /* FF 0 1 2 3 4 5 6 7 8 9 A B C D E F */
  672. /* 0X */ 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0,
  673. /* 1X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1,
  674. /* 2X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  675. /* 3X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
  676. /* 4X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  677. /* 5X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0,
  678. /* 6X */ 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  679. /* 7X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  680. /* 8X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  681. /* 9X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1
  682. };
  683. /***************************************************************************\
  684. * UserIsFELineBreak() - Detects East Asian word breaking characters. *
  685. * *
  686. * History: *
  687. * 10-Mar-1996 HideyukN Created. *
  688. \***************************************************************************/
  689. #if 0 // not currently used --- FYI only
  690. BOOL UserIsFELineBreakStart(WCHAR wch)
  691. {
  692. switch (wch>>8)
  693. {
  694. case 0x00:
  695. // Check if word breaking chars in ASCII.
  696. if ((wch >= 0x0024) && (wch <= 0x007B))
  697. return ((BOOL)(aASCII_StartBreak[wch - 0x0024]));
  698. else
  699. return FALSE;
  700. case 0x20:
  701. // Check if work breaking chars in "General punctuation"
  702. if ((wch == 0x2018) || (wch == 0x201C))
  703. return TRUE;
  704. else
  705. return FALSE;
  706. case 0x30:
  707. // Check if word breaking chars in "CJK symbols and punctuation"
  708. // and Hiragana.
  709. if ((wch >= 0x3008) && (wch <= 0x3014))
  710. return ((BOOL)(aCJKSymbol_StartBreak[wch - 0x3008]));
  711. else
  712. return FALSE;
  713. case 0xFF:
  714. // Check if word breaking chars in "Fullwidth ASCII variants",
  715. // "Halfwidth Katakana variants" or "Fullwidth Symbol variants".
  716. if ((wch >= 0xFF04) && (wch <= 0xFF62))
  717. return ((BOOL)(aFullWidthHalfWidthVariants_StartBreak[wch - 0xFF04]));
  718. else if ((wch == 0xFFE1) || (wch == 0xFFE6))
  719. return TRUE;
  720. else
  721. return FALSE;
  722. default:
  723. return FALSE;
  724. }
  725. }
  726. #endif
  727. BOOL UserIsFELineBreakEnd(WCHAR wch)
  728. {
  729. switch (wch>>8)
  730. {
  731. case 0x00:
  732. // Check if word breaking chars in ASCII or Latin1.
  733. if ((wch >= 0x0021) && (wch <= 0x00B7))
  734. return ((BOOL)(aASCII_Latin1_EndBreak[wch - 0x0021]));
  735. else
  736. return FALSE;
  737. case 0x02:
  738. // Check if work breaking chars in "Modifier letters"
  739. if ((wch == 0x02C7) || (wch == 0x02C9))
  740. return TRUE;
  741. else
  742. return FALSE;
  743. case 0x20:
  744. // Check if work breaking chars in "General punctuation"
  745. if ((wch >= 0x2013) && (wch <= 0x2033))
  746. return ((BOOL)(aGeneralPunctuation_EndBreak[wch - 0x2013]));
  747. else
  748. return FALSE;
  749. case 0x21:
  750. // Check if work breaking chars in "Letterlike symbols"
  751. if (wch == 0x2103)
  752. return TRUE;
  753. else
  754. return FALSE;
  755. case 0x22:
  756. // Check if work breaking chars in "Mathemetical opetartors"
  757. if (wch == 0x2236)
  758. return TRUE;
  759. else
  760. return FALSE;
  761. case 0x25:
  762. // Check if work breaking chars in "Form and Chart components"
  763. if (wch == 0x2574)
  764. return TRUE;
  765. else
  766. return FALSE;
  767. case 0x30:
  768. // Check if word breaking chars in "CJK symbols and punctuation"
  769. // and Hiragana.
  770. if ((wch >= 0x3001) && (wch <= 0x301E))
  771. return ((BOOL)(aCJKSymbol_EndBreak[wch - 0x3001]));
  772. else if ((wch == 0x309B) || (wch == 0x309C))
  773. return TRUE;
  774. else
  775. return FALSE;
  776. case 0xFE:
  777. // Check if word breaking chars in "CNS 11643 compatibility"
  778. // or "Small variants".
  779. if ((wch >= 0xFE30) && (wch <= 0xFE5E))
  780. return ((BOOL)(aCNS11643_SmallVariants_EndBreak[wch - 0xFE30]));
  781. else
  782. return FALSE;
  783. case 0xFF:
  784. // Check if word breaking chars in "Fullwidth ASCII variants",
  785. // "Halfwidth Katakana variants" or "Fullwidth symbol variants".
  786. if ((wch >= 0xFF01) && (wch <= 0xFF9F))
  787. return ((BOOL)(aFullWidthHalfWidthVariants_EndBreak[wch - 0xFF01]));
  788. else if (wch >= 0xFFE0)
  789. return TRUE;
  790. else
  791. return FALSE;
  792. default:
  793. return FALSE;
  794. }
  795. }
  796. #define UserIsFELineBreak(wChar) UserIsFELineBreakEnd(wChar)
  797. typedef struct _FULLWIDTH_UNICODE {
  798. WCHAR Start;
  799. WCHAR End;
  800. } FULLWIDTH_UNICODE, *PFULLWIDTH_UNICODE;
  801. #define NUM_FULLWIDTH_UNICODES 4
  802. CONST FULLWIDTH_UNICODE FullWidthUnicodes[] =
  803. {
  804. { 0x4E00, 0x9FFF }, // CJK_UNIFIED_IDOGRAPHS
  805. { 0x3040, 0x309F }, // HIRAGANA
  806. { 0x30A0, 0x30FF }, // KATAKANA
  807. { 0xAC00, 0xD7A3 } // HANGUL
  808. };
  809. BOOL UserIsFullWidth(WCHAR wChar)
  810. {
  811. int index;
  812. // Early out for ASCII.
  813. if (wChar < 0x0080)
  814. {
  815. // if the character < 0x0080, it should be a halfwidth character.
  816. return FALSE;
  817. }
  818. // Scan FullWdith definition table... most of FullWidth character is
  819. // defined here... this is more faster than call NLS API.
  820. for (index = 0; index < NUM_FULLWIDTH_UNICODES; index++)
  821. {
  822. if ((wChar >= FullWidthUnicodes[index].Start) && (wChar <= FullWidthUnicodes[index].End))
  823. return TRUE;
  824. }
  825. // Issue: We need one more case here to match NT5 implementation - beomoh
  826. // if this Unicode character is mapped to Double-Byte character,
  827. // this is also FullWidth character..
  828. return FALSE;
  829. }
  830. LPCWSTR GetNextWordbreak(LPCWSTR lpch,
  831. LPCWSTR lpchEnd,
  832. DWORD dwFormat,
  833. LPDRAWTEXTDATA lpDrawInfo)
  834. {
  835. /* ichNonWhite is used to make sure we always make progress. */
  836. int ichNonWhite = 1;
  837. int ichComplexBreak = 0; // Breaking opportunity for complex scripts
  838. #if ((DT_WORDBREAK & ~0xff) != 0)
  839. #error cannot use BOOLEAN for DT_WORDBREAK, or you should use "!!" before assigning it
  840. #endif
  841. BOOLEAN fBreakSpace = (BOOLEAN)(dwFormat & DT_WORDBREAK);
  842. // If DT_WORDBREAK and DT_NOFULLWIDTHCHARBREAK are both set, we must
  843. // stop assuming FullWidth characters as word as we're doing in
  844. // NT4 and Win95. Instead, CR/LF and/or white space will only be
  845. // a line-break characters.
  846. BOOLEAN fDbcsCharBreak = (fBreakSpace && !(dwFormat & DT_NOFULLWIDTHCHARBREAK));
  847. // We must terminate this loop before lpch == lpchEnd, otherwise, we may gp fault during *lpch.
  848. while (lpch < lpchEnd)
  849. {
  850. switch (*lpch)
  851. {
  852. case CR:
  853. case LF:
  854. return lpch;
  855. case '\t':
  856. case ' ':
  857. if (fBreakSpace)
  858. return (lpch + ichNonWhite);
  859. // FALL THRU //
  860. default:
  861. // Since most Japanese writing don't use space character
  862. // to separate each word, we define each Kanji character
  863. // as a word.
  864. if (fDbcsCharBreak && UserIsFullWidth(*lpch))
  865. {
  866. if (!ichNonWhite)
  867. return lpch;
  868. // if the next character is the last character of this string,
  869. // We return the character, even this is a "KINSOKU" charcter...
  870. if ((lpch+1) != lpchEnd)
  871. {
  872. // Check next character of FullWidth character.
  873. // if the next character is "KINSOKU" character, the character
  874. // should be handled as a part of previous FullWidth character.
  875. // Never handle is as A character, and should not be a Word also.
  876. if (UserIsFELineBreak(*(lpch+1)))
  877. {
  878. // Then if the character is "KINSOKU" character, we return
  879. // the next of this character,...
  880. return (lpch + 1 + 1);
  881. }
  882. }
  883. // Otherwise, we just return the chracter that is next of FullWidth
  884. // Character. Because we treat A FullWidth chacter as A Word.
  885. return (lpch + 1);
  886. }
  887. lpch++;
  888. ichNonWhite = 0;
  889. }
  890. }
  891. return lpch;
  892. }
  893. // This routine returns the count of accelerator mnemonics and the
  894. // character location (starting at 0) of the character to underline.
  895. // A single CH_PREFIX character will be striped and the following character
  896. // underlined, all double CH_PREFIX character sequences will be replaced by
  897. // a single CH_PREFIX (this is done by PSMTextOut). This routine is used
  898. // to determine the actual character length of the string that will be
  899. // printed, and the location the underline should be placed. Only
  900. // cch characters from the input string will be processed. If the lpstrCopy
  901. // parameter is non-NULL, this routine will make a printable copy of the
  902. // string with all single prefix characters removed and all double prefix
  903. // characters collapsed to a single character. If copying, a maximum
  904. // character count must be specified which will limit the number of
  905. // characters copied.
  906. //
  907. // The location of the single CH_PREFIX is returned in the low order
  908. // word, and the count of CH_PREFIX characters that will be striped
  909. // from the string during printing is in the hi order word. If the
  910. // high order word is 0, the low order word is meaningless. If there
  911. // were no single prefix characters (i.e. nothing to underline), the
  912. // low order word will be -1 (to distinguish from location 0).
  913. //
  914. // These routines assume that there is only one single CH_PREFIX character
  915. // in the string.
  916. //
  917. // WARNING! this rountine returns information in BYTE count not CHAR count
  918. // (so it can easily be passed onto GreExtTextOutW which takes byte
  919. // counts as well)
  920. LONG GetPrefixCount(
  921. LPCWSTR lpstr,
  922. int cch,
  923. LPWSTR lpstrCopy,
  924. int charcopycount)
  925. {
  926. int chprintpos = 0; // Num of chars that will be printed
  927. int chcount = 0; // Num of prefix chars that will be removed
  928. int chprefixloc = -1; // Pos (in printed chars) of the prefix
  929. WCHAR ch;
  930. // If not copying, use a large bogus count...
  931. if (lpstrCopy == NULL)
  932. charcopycount = 32767;
  933. while ((cch-- > 0) && *lpstr && charcopycount-- != 0)
  934. {
  935. // Is this guy a prefix character ?
  936. if ((ch = *lpstr++) == CH_PREFIX)
  937. {
  938. // Yup - increment the count of characters removed during print.
  939. chcount++;
  940. // Is the next also a prefix char?
  941. if (*lpstr != CH_PREFIX)
  942. {
  943. // Nope - this is a real one, mark its location.
  944. chprefixloc = chprintpos;
  945. }
  946. else
  947. {
  948. // yup - simply copy it if copying.
  949. if (lpstrCopy != NULL)
  950. *(lpstrCopy++) = CH_PREFIX;
  951. cch--;
  952. lpstr++;
  953. chprintpos++;
  954. }
  955. }
  956. else if (ch == CH_ENGLISHPREFIX) // Still needs to be parsed
  957. {
  958. // Yup - increment the count of characters removed during print.
  959. chcount++;
  960. // Next character is a real one, mark its location.
  961. chprefixloc = chprintpos;
  962. }
  963. else if (ch == CH_KANJIPREFIX) // Still needs to be parsed
  964. {
  965. // We only support Alpha Numeric(CH_ENGLISHPREFIX).
  966. // no support for Kana(CH_KANJIPREFIX).
  967. // Yup - increment the count of characters removed during print.
  968. chcount++;
  969. if(cch)
  970. {
  971. // don't copy the character
  972. chcount++;
  973. lpstr++;
  974. cch--;
  975. }
  976. }
  977. else
  978. {
  979. // Nope - just inc count of char. that will be printed
  980. chprintpos++;
  981. if (lpstrCopy != NULL)
  982. *(lpstrCopy++) = ch;
  983. }
  984. }
  985. if (lpstrCopy != NULL)
  986. *lpstrCopy = 0;
  987. // Return the character counts
  988. return MAKELONG(chprefixloc, chcount);
  989. }
  990. // Returns total width of prefix character. Japanese Windows has
  991. // three shortcut prefixes, '&',\036 and \037. They may have
  992. // different width.
  993. int KKGetPrefixWidth(HDC hdc, LPCWSTR lpStr, int cch)
  994. {
  995. SIZE size;
  996. SIZE iPrefix1 = {-1L,-1L};
  997. SIZE iPrefix2 = {-1L,-1L};
  998. SIZE iPrefix3 = {-1L,-1L};
  999. int iTotal = 0;
  1000. while (cch-- > 0 && *lpStr)
  1001. {
  1002. switch(*lpStr)
  1003. {
  1004. case CH_PREFIX:
  1005. if (lpStr[1] != CH_PREFIX)
  1006. {
  1007. if (iPrefix1.cx == -1)
  1008. FLGetTextExtentPoint32(hdc, lpStr, 1, &iPrefix1);
  1009. iTotal += iPrefix1.cx;
  1010. }
  1011. else
  1012. {
  1013. lpStr++;
  1014. cch--;
  1015. }
  1016. break;
  1017. case CH_ENGLISHPREFIX:
  1018. if (iPrefix2.cx == -1)
  1019. FLGetTextExtentPoint32(hdc, lpStr, 1, &iPrefix2);
  1020. iTotal += iPrefix2.cx;
  1021. break;
  1022. case CH_KANJIPREFIX:
  1023. if (iPrefix3.cx == -1)
  1024. FLGetTextExtentPoint32(hdc, lpStr, 1, &iPrefix3);
  1025. iTotal += iPrefix3.cx;
  1026. // In NT, always alpha numeric mode, Then we have to sum
  1027. // KANA accel key prefix non visible char width.
  1028. // so always add the extent for next char.
  1029. FLGetTextExtentPoint32(hdc, lpStr, 1, &size);
  1030. iTotal += size.cx;
  1031. break;
  1032. default:
  1033. // No need to taking care of Double byte since 2nd byte of
  1034. // DBC is grater than 0x2f but all shortcut keys are less
  1035. // than 0x30.
  1036. break;
  1037. }
  1038. lpStr++;
  1039. }
  1040. return iTotal;
  1041. }
  1042. // Outputs the text and puts and _ below the character with an &
  1043. // before it. Note that this routine isn't used for menus since menus
  1044. // have their own special one so that it is specialized and faster...
  1045. void PSMTextOut(
  1046. HDC hdc,
  1047. int xLeft,
  1048. int yTop,
  1049. LPWSTR lpsz,
  1050. int cch,
  1051. DWORD dwFlags)
  1052. {
  1053. int cx;
  1054. LONG textsize, result;
  1055. WCHAR achWorkBuffer[255];
  1056. WCHAR *pchOut = achWorkBuffer;
  1057. TEXTMETRIC textMetric;
  1058. SIZE size;
  1059. RECT rc;
  1060. COLORREF color;
  1061. if (dwFlags & DT_NOPREFIX)
  1062. {
  1063. FLTextOutW(hdc, xLeft, yTop, lpsz, cch);
  1064. return;
  1065. }
  1066. if (cch > sizeof(achWorkBuffer)/sizeof(WCHAR))
  1067. {
  1068. pchOut = (WCHAR*)LocalAlloc(LPTR, (cch+1) * sizeof(WCHAR));
  1069. if (pchOut == NULL)
  1070. return;
  1071. }
  1072. result = GetPrefixCount(lpsz, cch, pchOut, cch);
  1073. // DT_PREFIXONLY is a new 5.0 option used when switching from keyboard cues off to on.
  1074. if (!(dwFlags & DT_PREFIXONLY))
  1075. FLTextOutW(hdc, xLeft, yTop, pchOut, cch - HIWORD(result));
  1076. // Any true prefix characters to underline?
  1077. if (LOWORD(result) == 0xFFFF || dwFlags & DT_HIDEPREFIX)
  1078. {
  1079. if (pchOut != achWorkBuffer)
  1080. LocalFree(pchOut);
  1081. return;
  1082. }
  1083. if (!GetTextMetrics(hdc, &textMetric))
  1084. {
  1085. textMetric.tmOverhang = 0;
  1086. textMetric.tmAscent = 0;
  1087. }
  1088. // For proportional fonts, find starting point of underline.
  1089. if (LOWORD(result) != 0)
  1090. {
  1091. // How far in does underline start (if not at 0th byte.).
  1092. FLGetTextExtentPoint32(hdc, pchOut, LOWORD(result), &size);
  1093. xLeft += size.cx;
  1094. // Adjust starting point of underline if not at first char and there is
  1095. // an overhang. (Italics or bold fonts.)
  1096. xLeft = xLeft - textMetric.tmOverhang;
  1097. }
  1098. // Adjust for proportional font when setting the length of the underline and
  1099. // height of text.
  1100. FLGetTextExtentPoint32(hdc, pchOut + LOWORD(result), 1, &size);
  1101. textsize = size.cx;
  1102. // Find the width of the underline character. Just subtract out the overhang
  1103. // divided by two so that we look better with italic fonts. This is not
  1104. // going to effect embolded fonts since their overhang is 1.
  1105. cx = LOWORD(textsize) - textMetric.tmOverhang / 2;
  1106. // Get height of text so that underline is at bottom.
  1107. yTop += textMetric.tmAscent + 1;
  1108. // Draw the underline using the foreground color.
  1109. SetRect(&rc, xLeft, yTop, xLeft+cx, yTop+1);
  1110. color = SetBkColor(hdc, GetTextColor(hdc));
  1111. FLExtTextOutW(hdc, xLeft, yTop, ETO_OPAQUE, &rc, L"", 0, NULL);
  1112. SetBkColor(hdc, color);
  1113. if (pchOut != achWorkBuffer)
  1114. LocalFree(pchOut);
  1115. }
  1116. int DT_GetExtentMinusPrefixes(HDC hdc, LPCWSTR lpchStr, int cchCount, UINT wFormat, int iOverhang)
  1117. {
  1118. int iPrefixCount;
  1119. int cxPrefixes = 0;
  1120. WCHAR PrefixChar = CH_PREFIX;
  1121. SIZE size;
  1122. if (!(wFormat & DT_NOPREFIX) &&
  1123. (iPrefixCount = HIWORD(GetPrefixCount(lpchStr, cchCount, NULL, 0))))
  1124. {
  1125. // Kanji Windows has three shortcut prefixes...
  1126. if (IsOnDBCS())
  1127. {
  1128. // 16bit apps compatibility
  1129. cxPrefixes = KKGetPrefixWidth(hdc, lpchStr, cchCount) - (iPrefixCount * iOverhang);
  1130. }
  1131. else
  1132. {
  1133. cxPrefixes = FLGetTextExtentPoint32(hdc, &PrefixChar, 1, &size);
  1134. cxPrefixes = size.cx - iOverhang;
  1135. cxPrefixes *= iPrefixCount;
  1136. }
  1137. }
  1138. FLGetTextExtentPoint32(hdc, lpchStr, cchCount, &size);
  1139. return (size.cx - cxPrefixes);
  1140. }
  1141. // This will draw the given string in the given location without worrying
  1142. // about the left/right justification. Gets the extent and returns it.
  1143. // If fDraw is TRUE and if NOT DT_CALCRECT, this draws the text.
  1144. // NOTE: This returns the extent minus Overhang.
  1145. int DT_DrawStr(HDC hdc, int xLeft, int yTop, LPCWSTR lpchStr,
  1146. int cchCount, BOOL fDraw, UINT wFormat,
  1147. LPDRAWTEXTDATA lpDrawInfo)
  1148. {
  1149. LPCWSTR lpch;
  1150. int iLen;
  1151. int cxExtent;
  1152. int xOldLeft = xLeft; // Save the xLeft given to compute the extent later
  1153. int xTabLength = lpDrawInfo->cxTabLength;
  1154. int iTabOrigin = lpDrawInfo->rcFormat.left;
  1155. // Check if the tabs need to be expanded
  1156. if (wFormat & DT_EXPANDTABS)
  1157. {
  1158. while (cchCount)
  1159. {
  1160. // Look for a tab
  1161. for (iLen = 0, lpch = lpchStr; iLen < cchCount; iLen++)
  1162. if(*lpch++ == L'\t')
  1163. break;
  1164. // Draw text, if any, upto the tab
  1165. if (iLen)
  1166. {
  1167. // Draw the substring taking care of the prefixes.
  1168. if (fDraw && !(wFormat & DT_CALCRECT)) // Only if we need to draw text
  1169. PSMTextOut(hdc, xLeft, yTop, (LPWSTR)lpchStr, iLen, wFormat);
  1170. // Get the extent of this sub string and add it to xLeft.
  1171. xLeft += DT_GetExtentMinusPrefixes(hdc, lpchStr, iLen, wFormat, lpDrawInfo->cxOverhang) - lpDrawInfo->cxOverhang;
  1172. }
  1173. //if a TAB was found earlier, calculate the start of next sub-string.
  1174. if (iLen < cchCount)
  1175. {
  1176. iLen++; // Skip the tab
  1177. if (xTabLength) // Tab length could be zero
  1178. xLeft = (((xLeft - iTabOrigin)/xTabLength) + 1)*xTabLength + iTabOrigin;
  1179. }
  1180. // Calculate the details of the string that remains to be drawn.
  1181. cchCount -= iLen;
  1182. lpchStr = lpch;
  1183. }
  1184. cxExtent = xLeft - xOldLeft;
  1185. }
  1186. else
  1187. {
  1188. // If required, draw the text
  1189. if (fDraw && !(wFormat & DT_CALCRECT))
  1190. PSMTextOut(hdc, xLeft, yTop, (LPWSTR)lpchStr, cchCount, wFormat);
  1191. // Compute the extent of the text.
  1192. cxExtent = DT_GetExtentMinusPrefixes(hdc, lpchStr, cchCount, wFormat, lpDrawInfo->cxOverhang) - lpDrawInfo->cxOverhang;
  1193. }
  1194. return cxExtent;
  1195. }
  1196. // This function draws one complete line with proper justification
  1197. void DT_DrawJustifiedLine(HDC hdc, int yTop, LPCWSTR lpchLineSt, int cchCount, UINT wFormat, LPDRAWTEXTDATA lpDrawInfo)
  1198. {
  1199. LPRECT lprc;
  1200. int cxExtent;
  1201. int xLeft;
  1202. lprc = &(lpDrawInfo->rcFormat);
  1203. xLeft = lprc->left;
  1204. // Handle the special justifications (right or centered) properly.
  1205. if (wFormat & (DT_CENTER | DT_RIGHT))
  1206. {
  1207. cxExtent = DT_DrawStr(hdc, xLeft, yTop, lpchLineSt, cchCount, FALSE, wFormat, lpDrawInfo)
  1208. + lpDrawInfo->cxOverhang;
  1209. if(wFormat & DT_CENTER)
  1210. xLeft = lprc->left + (((lprc->right - lprc->left) - cxExtent) >> 1);
  1211. else
  1212. xLeft = lprc->right - cxExtent;
  1213. }
  1214. else
  1215. xLeft = lprc->left;
  1216. // Draw the whole line.
  1217. cxExtent = DT_DrawStr(hdc, xLeft, yTop, lpchLineSt, cchCount, TRUE, wFormat, lpDrawInfo)
  1218. + lpDrawInfo->cxOverhang;
  1219. if (cxExtent > lpDrawInfo->cxMaxExtent)
  1220. lpDrawInfo->cxMaxExtent = cxExtent;
  1221. }
  1222. // This is called at the begining of DrawText(); This initializes the
  1223. // DRAWTEXTDATA structure passed to this function with all the required info.
  1224. BOOL DT_InitDrawTextInfo(
  1225. HDC hdc,
  1226. LPRECT lprc,
  1227. UINT wFormat,
  1228. LPDRAWTEXTDATA lpDrawInfo,
  1229. LPDRAWTEXTPARAMS lpDTparams)
  1230. {
  1231. SIZE sizeViewPortExt = {0, 0}, sizeWindowExt = {0, 0};
  1232. TEXTMETRIC tm;
  1233. LPRECT lprcDest;
  1234. int iTabLength = 8; // Default Tab length is 8 characters.
  1235. int iLeftMargin;
  1236. int iRightMargin;
  1237. if (lpDTparams)
  1238. {
  1239. // Only if DT_TABSTOP flag is mentioned, we must use the iTabLength field.
  1240. if (wFormat & DT_TABSTOP)
  1241. iTabLength = lpDTparams->iTabLength;
  1242. iLeftMargin = lpDTparams->iLeftMargin;
  1243. iRightMargin = lpDTparams->iRightMargin;
  1244. }
  1245. else
  1246. iLeftMargin = iRightMargin = 0;
  1247. // Get the View port and Window extents for the given DC
  1248. // If this call fails, hdc must be invalid
  1249. if (!GetViewportExtEx(hdc, &sizeViewPortExt))
  1250. return FALSE;
  1251. GetWindowExtEx(hdc, &sizeWindowExt);
  1252. // For the current mapping mode, find out the sign of x from left to right.
  1253. lpDrawInfo->iXSign = (((sizeViewPortExt.cx ^ sizeWindowExt.cx) & 0x80000000) ? -1 : 1);
  1254. // For the current mapping mode, find out the sign of y from top to bottom.
  1255. lpDrawInfo->iYSign = (((sizeViewPortExt.cy ^ sizeWindowExt.cy) & 0x80000000) ? -1 : 1);
  1256. // Calculate the dimensions of the current font in this DC.
  1257. GetTextMetrics(hdc, &tm);
  1258. // cyLineHeight is in pixels (This will be signed).
  1259. lpDrawInfo->cyLineHeight = (tm.tmHeight +
  1260. ((wFormat & DT_EXTERNALLEADING) ? tm.tmExternalLeading : 0)) * lpDrawInfo->iYSign;
  1261. // cxTabLength is the tab length in pixels (This will not be signed)
  1262. lpDrawInfo->cxTabLength = tm.tmAveCharWidth * iTabLength;
  1263. // Set the cxOverhang
  1264. lpDrawInfo->cxOverhang = tm.tmOverhang;
  1265. // Set up the format rectangle based on the margins.
  1266. lprcDest = &(lpDrawInfo->rcFormat);
  1267. *lprcDest = *lprc;
  1268. // We need to do the following only if the margins are given
  1269. if (iLeftMargin | iRightMargin)
  1270. {
  1271. lprcDest->left += iLeftMargin * lpDrawInfo->iXSign;
  1272. lprcDest->right -= (lpDrawInfo->cxRightMargin = iRightMargin * lpDrawInfo->iXSign);
  1273. }
  1274. else
  1275. lpDrawInfo->cxRightMargin = 0; // Initialize to zero.
  1276. // cxMaxWidth is unsigned.
  1277. lpDrawInfo->cxMaxWidth = (lprcDest->right - lprcDest->left) * lpDrawInfo->iXSign;
  1278. lpDrawInfo->cxMaxExtent = 0; // Initialize this to zero.
  1279. return TRUE;
  1280. }
  1281. // In the case of WORDWRAP, we need to treat the white spaces at the
  1282. // begining/end of each line specially. This function does that.
  1283. // lpStNext = points to the begining of next line.
  1284. // lpiCount = points to the count of characters in the current line.
  1285. LPCWSTR DT_AdjustWhiteSpaces(LPCWSTR lpStNext, LPINT lpiCount, UINT wFormat)
  1286. {
  1287. switch (wFormat & DT_HFMTMASK)
  1288. {
  1289. case DT_LEFT:
  1290. // Prevent a white space at the begining of a left justfied text.
  1291. // Is there a white space at the begining of next line......
  1292. if ((*lpStNext == L' ') || (*lpStNext == L'\t'))
  1293. {
  1294. // ...then, exclude it from next line.
  1295. lpStNext++;
  1296. }
  1297. break;
  1298. case DT_RIGHT:
  1299. // Prevent a white space at the end of a RIGHT justified text.
  1300. // Is there a white space at the end of current line,.......
  1301. if ((*(lpStNext-1) == L' ') || (*(lpStNext - 1) == L'\t'))
  1302. {
  1303. // .....then, Skip the white space from the current line.
  1304. (*lpiCount)--;
  1305. }
  1306. break;
  1307. case DT_CENTER:
  1308. // Exclude white spaces from the begining and end of CENTERed lines.
  1309. // If there is a white space at the end of current line.......
  1310. if ((*(lpStNext-1) == L' ') || (*(lpStNext - 1) == L'\t'))
  1311. (*lpiCount)--; //...., don't count it for justification.
  1312. // If there is a white space at the begining of next line.......
  1313. if ((*lpStNext == L' ') || (*lpStNext == L'\t'))
  1314. lpStNext++; //...., exclude it from next line.
  1315. break;
  1316. }
  1317. return lpStNext;
  1318. }
  1319. // A word needs to be broken across lines and this finds out where to break it.
  1320. LPCWSTR DT_BreakAWord(HDC hdc, LPCWSTR lpchText, int iLength, int iWidth, UINT wFormat, int iOverhang)
  1321. {
  1322. int iLow = 0, iHigh = iLength;
  1323. int iNew;
  1324. while ((iHigh - iLow) > 1)
  1325. {
  1326. iNew = iLow + (iHigh - iLow)/2;
  1327. if(DT_GetExtentMinusPrefixes(hdc, lpchText, iNew, wFormat, iOverhang) > iWidth)
  1328. iHigh = iNew;
  1329. else
  1330. iLow = iNew;
  1331. }
  1332. // If the width is too low, we must print atleast one char per line.
  1333. // Else, we will be in an infinite loop.
  1334. if(!iLow && iLength)
  1335. iLow = 1;
  1336. return (lpchText+iLow);
  1337. }
  1338. // This finds out the location where we can break a line.
  1339. // Returns LPCSTR to the begining of next line.
  1340. // Also returns via lpiLineLength, the length of the current line.
  1341. // NOTE: (lpstNextLineStart - lpstCurrentLineStart) is not equal to the
  1342. // line length; This is because, we exclude some white spaces at the begining
  1343. // and/or end of lines; Also, CR/LF is excluded from the line length.
  1344. LPWSTR DT_GetLineBreak(
  1345. HDC hdc,
  1346. LPCWSTR lpchLineStart,
  1347. int cchCount,
  1348. DWORD dwFormat,
  1349. LPINT lpiLineLength,
  1350. LPDRAWTEXTDATA lpDrawInfo)
  1351. {
  1352. LPCWSTR lpchText, lpchEnd, lpch, lpchLineEnd;
  1353. int cxStart, cxExtent, cxNewExtent;
  1354. BOOL fAdjustWhiteSpaces = FALSE;
  1355. WCHAR ch;
  1356. cxStart = lpDrawInfo->rcFormat.left;
  1357. cxExtent = cxNewExtent = 0;
  1358. lpchText = lpchLineStart;
  1359. lpchEnd = lpchLineStart + cchCount;
  1360. lpch = lpchEnd;
  1361. lpchLineEnd = lpchEnd;
  1362. while(lpchText < lpchEnd)
  1363. {
  1364. lpchLineEnd = lpch = GetNextWordbreak(lpchText, lpchEnd, dwFormat, lpDrawInfo);
  1365. // DT_DrawStr does not return the overhang; Otherwise we will end up
  1366. // adding one overhang for every word in the string.
  1367. // For simulated Bold fonts, the summation of extents of individual
  1368. // words in a line is greater than the extent of the whole line. So,
  1369. // always calculate extent from the LineStart.
  1370. // BUGTAG: #6054 -- Win95B -- SANKAR -- 3/9/95 --
  1371. cxNewExtent = DT_DrawStr(hdc, cxStart, 0, lpchLineStart, (int)(((PBYTE)lpch - (PBYTE)lpchLineStart)/sizeof(WCHAR)),
  1372. FALSE, dwFormat, lpDrawInfo);
  1373. if ((dwFormat & DT_WORDBREAK) && ((cxNewExtent + lpDrawInfo->cxOverhang) > lpDrawInfo->cxMaxWidth))
  1374. {
  1375. // Are there more than one word in this line?
  1376. if (lpchText != lpchLineStart)
  1377. {
  1378. lpchLineEnd = lpch = lpchText;
  1379. fAdjustWhiteSpaces = TRUE;
  1380. }
  1381. else
  1382. {
  1383. //One word is longer than the maximum width permissible.
  1384. //See if we are allowed to break that single word.
  1385. if((dwFormat & DT_EDITCONTROL) && !(dwFormat & DT_WORD_ELLIPSIS))
  1386. {
  1387. lpchLineEnd = lpch = DT_BreakAWord(hdc, lpchText, (int)(((PBYTE)lpch - (PBYTE)lpchText)/sizeof(WCHAR)),
  1388. lpDrawInfo->cxMaxWidth - cxExtent, dwFormat, lpDrawInfo->cxOverhang); //Break that word
  1389. //Note: Since we broke in the middle of a word, no need to
  1390. // adjust for white spaces.
  1391. }
  1392. else
  1393. {
  1394. fAdjustWhiteSpaces = TRUE;
  1395. // Check if we need to end this line with ellipsis
  1396. if(dwFormat & DT_WORD_ELLIPSIS)
  1397. {
  1398. // Don't do this if already at the end of the string.
  1399. if (lpch < lpchEnd)
  1400. {
  1401. // If there are CR/LF at the end, skip them.
  1402. if ((ch = *lpch) == CR || ch == LF)
  1403. {
  1404. if ((++lpch < lpchEnd) && (*lpch == (WCHAR)(ch ^ (LF ^ CR))))
  1405. lpch++;
  1406. fAdjustWhiteSpaces = FALSE;
  1407. }
  1408. }
  1409. }
  1410. }
  1411. }
  1412. // Well! We found a place to break the line. Let us break from this loop;
  1413. break;
  1414. }
  1415. else
  1416. {
  1417. // Don't do this if already at the end of the string.
  1418. if (lpch < lpchEnd)
  1419. {
  1420. if ((ch = *lpch) == CR || ch == LF)
  1421. {
  1422. if ((++lpch < lpchEnd) && (*lpch == (WCHAR)(ch ^ (LF ^ CR))))
  1423. lpch++;
  1424. fAdjustWhiteSpaces = FALSE;
  1425. break;
  1426. }
  1427. }
  1428. }
  1429. // Point at the beginning of the next word.
  1430. lpchText = lpch;
  1431. cxExtent = cxNewExtent;
  1432. }
  1433. // Calculate the length of current line.
  1434. *lpiLineLength = (INT)((PBYTE)lpchLineEnd - (PBYTE)lpchLineStart)/sizeof(WCHAR);
  1435. // Adjust the line length and lpch to take care of spaces.
  1436. if(fAdjustWhiteSpaces && (lpch < lpchEnd))
  1437. lpch = DT_AdjustWhiteSpaces(lpch, lpiLineLength, dwFormat);
  1438. // return the begining of next line;
  1439. return (LPWSTR)lpch;
  1440. }
  1441. // This function checks whether the given string fits within the given
  1442. // width or we need to add end-ellipse. If it required end-ellipses, it
  1443. // returns TRUE and it returns the number of characters that are saved
  1444. // in the given string via lpCount.
  1445. BOOL NeedsEndEllipsis(
  1446. HDC hdc,
  1447. LPCWSTR lpchText,
  1448. LPINT lpCount,
  1449. LPDRAWTEXTDATA lpDTdata,
  1450. UINT wFormat)
  1451. {
  1452. int cchText;
  1453. int ichMin, ichMax, ichMid;
  1454. int cxMaxWidth;
  1455. int iOverhang;
  1456. int cxExtent;
  1457. SIZE size;
  1458. cchText = *lpCount; // Get the current count.
  1459. if (cchText == 0)
  1460. return FALSE;
  1461. cxMaxWidth = lpDTdata->cxMaxWidth;
  1462. iOverhang = lpDTdata->cxOverhang;
  1463. cxExtent = DT_GetExtentMinusPrefixes(hdc, lpchText, cchText, wFormat, iOverhang);
  1464. if (cxExtent <= cxMaxWidth)
  1465. return FALSE;
  1466. // Reserve room for the "..." ellipses;
  1467. // (Assumption: The ellipses don't have any prefixes!)
  1468. FLGetTextExtentPoint32(hdc, szEllipsis, CCHELLIPSIS, &size);
  1469. cxMaxWidth -= size.cx - iOverhang;
  1470. // If no room for ellipses, always show first character.
  1471. //
  1472. ichMax = 1;
  1473. if (cxMaxWidth > 0)
  1474. {
  1475. // Binary search to find characters that will fit.
  1476. ichMin = 0;
  1477. ichMax = cchText;
  1478. while (ichMin < ichMax)
  1479. {
  1480. // Be sure to round up, to make sure we make progress in
  1481. // the loop if ichMax == ichMin + 1.
  1482. ichMid = (ichMin + ichMax + 1) / 2;
  1483. cxExtent = DT_GetExtentMinusPrefixes(hdc, lpchText, ichMid, wFormat, iOverhang);
  1484. if (cxExtent < cxMaxWidth)
  1485. ichMin = ichMid;
  1486. else
  1487. {
  1488. if (cxExtent > cxMaxWidth)
  1489. ichMax = ichMid - 1;
  1490. else
  1491. {
  1492. // Exact match up up to ichMid: just exit.
  1493. ichMax = ichMid;
  1494. break;
  1495. }
  1496. }
  1497. }
  1498. // Make sure we always show at least the first character...
  1499. if (ichMax < 1)
  1500. ichMax = 1;
  1501. }
  1502. *lpCount = ichMax;
  1503. return TRUE;
  1504. }
  1505. // Returns a pointer to the last component of a path string.
  1506. //
  1507. // in:
  1508. // path name, either fully qualified or not
  1509. //
  1510. // returns:
  1511. // pointer into the path where the path is. if none is found
  1512. // returns a poiter to the start of the path
  1513. //
  1514. // c:\foo\bar -> bar
  1515. // c:\foo -> foo
  1516. // c:\foo\ -> c:\foo\ (REVIEW: is this case busted?)
  1517. // c:\ -> c:\ (REVIEW: this case is strange)
  1518. // c: -> c:
  1519. // foo -> foo
  1520. LPWSTR PathFindFileName(LPCWSTR pPath, int cchText)
  1521. {
  1522. LPCWSTR pT;
  1523. for (pT = pPath; cchText > 0 && *pPath; pPath++, cchText--)
  1524. {
  1525. if ((pPath[0] == L'\\' || pPath[0] == L':') && pPath[1])
  1526. pT = pPath + 1;
  1527. }
  1528. return (LPWSTR)pT;
  1529. }
  1530. // This adds a path ellipse to the given path name.
  1531. // Returns TRUE if the resultant string's extent is less the the
  1532. // cxMaxWidth. FALSE, if otherwise.
  1533. int AddPathEllipsis(
  1534. HDC hdc,
  1535. LPWSTR lpszPath,
  1536. int cchText,
  1537. UINT wFormat,
  1538. int cxMaxWidth,
  1539. int iOverhang)
  1540. {
  1541. int iLen;
  1542. UINT dxFixed, dxEllipsis;
  1543. LPWSTR lpEnd; /* end of the unfixed string */
  1544. LPWSTR lpFixed; /* start of text that we always display */
  1545. BOOL bEllipsisIn;
  1546. int iLenFixed;
  1547. SIZE size;
  1548. lpFixed = PathFindFileName(lpszPath, cchText);
  1549. if (lpFixed != lpszPath)
  1550. lpFixed--; // point at the slash
  1551. else
  1552. return cchText;
  1553. lpEnd = lpFixed;
  1554. bEllipsisIn = FALSE;
  1555. iLenFixed = cchText - (int)(lpFixed - lpszPath);
  1556. dxFixed = DT_GetExtentMinusPrefixes(hdc, lpFixed, iLenFixed, wFormat, iOverhang);
  1557. // It is assumed that the "..." string does not have any prefixes ('&').
  1558. FLGetTextExtentPoint32(hdc, szEllipsis, CCHELLIPSIS, &size);
  1559. dxEllipsis = size.cx - iOverhang;
  1560. while (TRUE)
  1561. {
  1562. iLen = dxFixed + DT_GetExtentMinusPrefixes(hdc, lpszPath, (int)((PBYTE)lpEnd - (PBYTE)lpszPath)/sizeof(WCHAR),
  1563. wFormat, iOverhang) - iOverhang;
  1564. if (bEllipsisIn)
  1565. iLen += dxEllipsis;
  1566. if (iLen <= cxMaxWidth)
  1567. break;
  1568. bEllipsisIn = TRUE;
  1569. if (lpEnd <= lpszPath)
  1570. {
  1571. // Things didn't fit.
  1572. lpEnd = lpszPath;
  1573. break;
  1574. }
  1575. // Step back a character.
  1576. lpEnd--;
  1577. }
  1578. if (bEllipsisIn && (lpEnd + CCHELLIPSIS < lpFixed))
  1579. {
  1580. // NOTE: the strings could over lap here. So, we use LCopyStruct.
  1581. MoveMemory((lpEnd + CCHELLIPSIS), lpFixed, iLenFixed * sizeof(WCHAR));
  1582. CopyMemory(lpEnd, szEllipsis, CCHELLIPSIS * sizeof(WCHAR));
  1583. cchText = (int)(lpEnd - lpszPath) + CCHELLIPSIS + iLenFixed;
  1584. // now we can NULL terminate the string
  1585. *(lpszPath + cchText) = L'\0';
  1586. }
  1587. return cchText;
  1588. }
  1589. // This function returns the number of characters actually drawn.
  1590. int AddEllipsisAndDrawLine(
  1591. HDC hdc,
  1592. int yLine,
  1593. LPCWSTR lpchText,
  1594. int cchText,
  1595. DWORD dwDTformat,
  1596. LPDRAWTEXTDATA lpDrawInfo)
  1597. {
  1598. LPWSTR pEllipsis = NULL;
  1599. WCHAR szTempBuff[MAXBUFFSIZE];
  1600. LPWSTR lpDest;
  1601. BOOL fAlreadyCopied = FALSE;
  1602. // Check if this is a filename with a path AND
  1603. // Check if the width is too narrow to hold all the text.
  1604. if ((dwDTformat & DT_PATH_ELLIPSIS) &&
  1605. ((DT_GetExtentMinusPrefixes(hdc, lpchText, cchText, dwDTformat, lpDrawInfo->cxOverhang)) > lpDrawInfo->cxMaxWidth))
  1606. {
  1607. // We need to add Path-Ellipsis. See if we can do it in-place.
  1608. if (!(dwDTformat & DT_MODIFYSTRING)) {
  1609. // NOTE: When you add Path-Ellipsis, the string could grow by
  1610. // CCHELLIPSIS bytes.
  1611. if((cchText + CCHELLIPSIS + 1) <= MAXBUFFSIZE)
  1612. lpDest = szTempBuff;
  1613. else
  1614. {
  1615. // Alloc the buffer from local heap.
  1616. if(!(pEllipsis = (LPWSTR)LocalAlloc(LPTR, (cchText+CCHELLIPSIS+1)*sizeof(WCHAR))))
  1617. return 0;
  1618. lpDest = (LPWSTR)pEllipsis;
  1619. }
  1620. // Source String may not be NULL terminated. So, copy just
  1621. // the given number of characters.
  1622. CopyMemory(lpDest, lpchText, cchText*sizeof(WCHAR));
  1623. lpchText = lpDest; // lpchText points to the copied buff.
  1624. fAlreadyCopied = TRUE; // Local copy has been made.
  1625. }
  1626. // Add the path ellipsis now!
  1627. cchText = AddPathEllipsis(hdc, (LPWSTR)lpchText, cchText, dwDTformat, lpDrawInfo->cxMaxWidth, lpDrawInfo->cxOverhang);
  1628. }
  1629. // Check if end-ellipsis are to be added.
  1630. if ((dwDTformat & (DT_END_ELLIPSIS | DT_WORD_ELLIPSIS)) &&
  1631. NeedsEndEllipsis(hdc, lpchText, &cchText, lpDrawInfo, dwDTformat))
  1632. {
  1633. // We need to add end-ellipsis; See if we can do it in-place.
  1634. if (!(dwDTformat & DT_MODIFYSTRING) && !fAlreadyCopied)
  1635. {
  1636. // See if the string is small enough for the buff on stack.
  1637. if ((cchText+CCHELLIPSIS+1) <= MAXBUFFSIZE)
  1638. lpDest = szTempBuff; // If so, use it.
  1639. else {
  1640. // Alloc the buffer from local heap.
  1641. if (!(pEllipsis = (LPWSTR)LocalAlloc(LPTR, (cchText+CCHELLIPSIS+1)*sizeof(WCHAR))))
  1642. return 0;
  1643. lpDest = pEllipsis;
  1644. }
  1645. // Make a copy of the string in the local buff.
  1646. CopyMemory(lpDest, lpchText, cchText*sizeof(WCHAR));
  1647. lpchText = lpDest;
  1648. }
  1649. // Add an end-ellipsis at the proper place.
  1650. CopyMemory((LPWSTR)(lpchText+cchText), szEllipsis, (CCHELLIPSIS+1)*sizeof(WCHAR));
  1651. cchText += CCHELLIPSIS;
  1652. }
  1653. // Draw the line that we just formed.
  1654. DT_DrawJustifiedLine(hdc, yLine, lpchText, cchText, dwDTformat, lpDrawInfo);
  1655. // Free the block allocated for End-Ellipsis.
  1656. if (pEllipsis)
  1657. LocalFree(pEllipsis);
  1658. return cchText;
  1659. }
  1660. BOOL IsComplexScriptPresent(LPWSTR lpchText, int cchText)
  1661. {
  1662. if (g_bComplexPlatform) {
  1663. for (int i = 0; i < cchText; i++) {
  1664. if (InRange(lpchText[i], 0x0590, 0x0FFF)) {
  1665. return TRUE;
  1666. }
  1667. }
  1668. }
  1669. return FALSE;
  1670. }
  1671. int FLDrawTextExPrivW(
  1672. HDC hdc,
  1673. LPWSTR lpchText,
  1674. int cchText,
  1675. LPRECT lprc,
  1676. UINT dwDTformat,
  1677. LPDRAWTEXTPARAMS lpDTparams)
  1678. {
  1679. DRAWTEXTDATA DrawInfo;
  1680. WORD wFormat = LOWORD(dwDTformat);
  1681. LPWSTR lpchTextBegin;
  1682. LPWSTR lpchEnd;
  1683. LPWSTR lpchNextLineSt;
  1684. int iLineLength;
  1685. int iySign;
  1686. int yLine;
  1687. int yLastLineHeight;
  1688. HRGN hrgnClip;
  1689. int iLineCount;
  1690. RECT rc;
  1691. BOOL fLastLine;
  1692. WCHAR ch;
  1693. UINT oldAlign;
  1694. // On NT5, we use system API behavior including fontlink
  1695. if (IsOnNT5())
  1696. return DrawTextExW(hdc, lpchText, cchText, lprc, dwDTformat, lpDTparams);
  1697. if ((cchText == 0) && lpchText && (*lpchText))
  1698. {
  1699. // infoview.exe passes lpchText that points to '\0'
  1700. // Lotus Notes doesn't like getting a zero return here
  1701. return 1;
  1702. }
  1703. if (cchText == -1)
  1704. cchText = lstrlenW(lpchText);
  1705. else if (lpchText[cchText - 1] == L'\0')
  1706. cchText--; // accommodate counting of NULLS for ME
  1707. // We got the string length, then check if it a complex string or not.
  1708. // If yes then call the system DrawTextEx API to do the job it knows how to
  1709. // handle the complex scripts.
  1710. if (IsComplexScriptPresent(lpchText, cchText))
  1711. {
  1712. if (IsOnNT())
  1713. {
  1714. //Call the system DrawtextExW
  1715. return DrawTextExW(hdc, lpchText, cchText, lprc, dwDTformat, lpDTparams);
  1716. }
  1717. HFONT hfont = NULL;
  1718. HFONT hfontSav = NULL;
  1719. int iRet;
  1720. if (hfont = GetBiDiFont(hdc))
  1721. hfontSav = (HFONT)SelectObject(hdc, hfont);
  1722. WtoA szText(lpchText, cchText);
  1723. iRet = DrawTextExA(hdc, szText, lstrlen(szText), lprc, dwDTformat, lpDTparams);
  1724. if (hfont)
  1725. {
  1726. SelectObject(hdc, hfontSav);
  1727. DeleteObject(hfont);
  1728. }
  1729. return iRet;
  1730. }
  1731. if ((lpDTparams) && (lpDTparams->cbSize != sizeof(DRAWTEXTPARAMS)))
  1732. {
  1733. ASSERT(0 && "DrawTextExWorker: cbSize is invalid");
  1734. return 0;
  1735. }
  1736. // If DT_MODIFYSTRING is specified, then check for read-write pointer.
  1737. if ((dwDTformat & DT_MODIFYSTRING) &&
  1738. (dwDTformat & (DT_END_ELLIPSIS | DT_PATH_ELLIPSIS)))
  1739. {
  1740. if(IsBadWritePtr(lpchText, cchText))
  1741. {
  1742. ASSERT(0 && "DrawTextExWorker: For DT_MODIFYSTRING, lpchText must be read-write");
  1743. return 0;
  1744. }
  1745. }
  1746. // Initialize the DrawInfo structure.
  1747. if (!DT_InitDrawTextInfo(hdc, lprc, dwDTformat, (LPDRAWTEXTDATA)&DrawInfo, lpDTparams))
  1748. return 0;
  1749. // If the rect is too narrow or the margins are too wide.....Just forget it!
  1750. //
  1751. // If wordbreak is specified, the MaxWidth must be a reasonable value.
  1752. // This check is sufficient because this will allow CALCRECT and NOCLIP
  1753. // cases. --SANKAR.
  1754. //
  1755. // This also fixed all of our known problems with AppStudio.
  1756. if (DrawInfo.cxMaxWidth <= 0)
  1757. {
  1758. if (wFormat & DT_WORDBREAK)
  1759. {
  1760. ASSERT(0 && "DrawTextExW: FAILURE DrawInfo.cxMaxWidth <= 0");
  1761. return 1;
  1762. }
  1763. }
  1764. // if we're not doing the drawing, initialise the lpk-dll
  1765. if (dwDTformat & DT_RTLREADING)
  1766. oldAlign = SetTextAlign(hdc, TA_RTLREADING | GetTextAlign(hdc));
  1767. // If we need to clip, let us do that.
  1768. if (!(wFormat & DT_NOCLIP))
  1769. {
  1770. // Save clipping region so we can restore it later.
  1771. hrgnClip = CreateRectRgn(0,0,0,0);
  1772. if (hrgnClip != NULL)
  1773. {
  1774. if (GetClipRgn(hdc, hrgnClip) != 1)
  1775. {
  1776. DeleteObject(hrgnClip);
  1777. hrgnClip = (HRGN)-1;
  1778. }
  1779. rc = *lprc;
  1780. IntersectClipRect(hdc, rc.left, rc.top, rc.right, rc.bottom);
  1781. }
  1782. }
  1783. else
  1784. hrgnClip = NULL;
  1785. lpchTextBegin = lpchText;
  1786. lpchEnd = lpchText + cchText;
  1787. ProcessDrawText:
  1788. iLineCount = 0; // Reset number of lines to 1.
  1789. yLine = lprc->top;
  1790. if (wFormat & DT_SINGLELINE)
  1791. {
  1792. iLineCount = 1; // It is a single line.
  1793. // Process single line DrawText.
  1794. switch (wFormat & DT_VFMTMASK)
  1795. {
  1796. case DT_BOTTOM:
  1797. yLine = lprc->bottom - DrawInfo.cyLineHeight;
  1798. break;
  1799. case DT_VCENTER:
  1800. yLine = lprc->top + ((lprc->bottom - lprc->top - DrawInfo.cyLineHeight) / 2);
  1801. break;
  1802. }
  1803. cchText = AddEllipsisAndDrawLine(hdc, yLine, lpchText, cchText, dwDTformat, &DrawInfo);
  1804. yLine += DrawInfo.cyLineHeight;
  1805. lpchText += cchText;
  1806. }
  1807. else
  1808. {
  1809. // Multiline
  1810. // If the height of the rectangle is not an integral multiple of the
  1811. // average char height, then it is possible that the last line drawn
  1812. // is only partially visible. However, if DT_EDITCONTROL style is
  1813. // specified, then we must make sure that the last line is not drawn if
  1814. // it is going to be partially visible. This will help imitate the
  1815. // appearance of an edit control.
  1816. if (wFormat & DT_EDITCONTROL)
  1817. yLastLineHeight = DrawInfo.cyLineHeight;
  1818. else
  1819. yLastLineHeight = 0;
  1820. iySign = DrawInfo.iYSign;
  1821. fLastLine = FALSE;
  1822. // Process multiline DrawText.
  1823. while ((lpchText < lpchEnd) && (!fLastLine))
  1824. {
  1825. // Check if the line we are about to draw is the last line that needs
  1826. // to be drawn.
  1827. // Let us check if the display goes out of the clip rect and if so
  1828. // let us stop here, as an optimisation;
  1829. if (!(wFormat & DT_CALCRECT) && // We don't need to calc rect?
  1830. !(wFormat & DT_NOCLIP) && // Must we clip the display ?
  1831. // Are we outside the rect?
  1832. ((yLine + DrawInfo.cyLineHeight + yLastLineHeight)*iySign > (lprc->bottom*iySign)))
  1833. {
  1834. fLastLine = TRUE; // Let us quit this loop
  1835. }
  1836. // We do the Ellipsis processing only for the last line.
  1837. if (fLastLine && (dwDTformat & (DT_END_ELLIPSIS | DT_PATH_ELLIPSIS)))
  1838. lpchText += AddEllipsisAndDrawLine(hdc, yLine, lpchText, cchText, dwDTformat, &DrawInfo);
  1839. else
  1840. {
  1841. lpchNextLineSt = (LPWSTR)DT_GetLineBreak(hdc, lpchText, cchText, dwDTformat, &iLineLength, &DrawInfo);
  1842. // Check if we need to put ellipsis at the end of this line.
  1843. // Also check if this is the last line.
  1844. if ((dwDTformat & DT_WORD_ELLIPSIS) ||
  1845. ((lpchNextLineSt >= lpchEnd) && (dwDTformat & (DT_END_ELLIPSIS | DT_PATH_ELLIPSIS))))
  1846. AddEllipsisAndDrawLine(hdc, yLine, lpchText, iLineLength, dwDTformat, &DrawInfo);
  1847. else
  1848. DT_DrawJustifiedLine(hdc, yLine, lpchText, iLineLength, dwDTformat, &DrawInfo);
  1849. cchText -= (int)((PBYTE)lpchNextLineSt - (PBYTE)lpchText) / sizeof(WCHAR);
  1850. lpchText = lpchNextLineSt;
  1851. }
  1852. iLineCount++; // We draw one more line.
  1853. yLine += DrawInfo.cyLineHeight;
  1854. }
  1855. // For Win3.1 and NT compatibility, if the last char is a CR or a LF
  1856. // then the height returned includes one more line.
  1857. if (!(dwDTformat & DT_EDITCONTROL) &&
  1858. (lpchEnd > lpchTextBegin) && // If zero length it will fault.
  1859. (((ch = (*(lpchEnd-1))) == CR) || (ch == LF)))
  1860. yLine += DrawInfo.cyLineHeight;
  1861. }
  1862. // If DT_CALCRECT, modify width and height of rectangle to include
  1863. // all of the text drawn.
  1864. if (wFormat & DT_CALCRECT)
  1865. {
  1866. DrawInfo.rcFormat.right = DrawInfo.rcFormat.left + DrawInfo.cxMaxExtent * DrawInfo.iXSign;
  1867. lprc->right = DrawInfo.rcFormat.right + DrawInfo.cxRightMargin;
  1868. // If the Width is more than what was provided, we have to redo all
  1869. // the calculations, because, the number of lines can be less now.
  1870. // (We need to do this only if we have more than one line).
  1871. if((iLineCount > 1) && (DrawInfo.cxMaxExtent > DrawInfo.cxMaxWidth))
  1872. {
  1873. DrawInfo.cxMaxWidth = DrawInfo.cxMaxExtent;
  1874. lpchText = lpchTextBegin;
  1875. cchText = (int)((PBYTE)lpchEnd - (PBYTE)lpchTextBegin) / sizeof(WCHAR);
  1876. goto ProcessDrawText; // Start all over again!
  1877. }
  1878. lprc->bottom = yLine;
  1879. }
  1880. if (hrgnClip != NULL)
  1881. {
  1882. if (hrgnClip == (HRGN)-1)
  1883. ExtSelectClipRgn(hdc, NULL, RGN_COPY);
  1884. else
  1885. {
  1886. ExtSelectClipRgn(hdc, hrgnClip, RGN_COPY);
  1887. DeleteObject(hrgnClip);
  1888. }
  1889. }
  1890. if (dwDTformat & DT_RTLREADING)
  1891. SetTextAlign(hdc, oldAlign);
  1892. // Copy the number of characters actually drawn
  1893. if(lpDTparams != NULL)
  1894. lpDTparams->uiLengthDrawn = (UINT)((PBYTE)lpchText - (PBYTE)lpchTextBegin) / sizeof(WCHAR);
  1895. if (yLine == lprc->top)
  1896. return 1;
  1897. return (yLine - lprc->top);
  1898. }
  1899. int FLDrawTextW(HDC hdc, LPCWSTR lpchText, int cchText, LPCRECT lprc, UINT format)
  1900. {
  1901. DRAWTEXTPARAMS DTparams;
  1902. LPDRAWTEXTPARAMS lpDTparams = NULL;
  1903. if (cchText < -1)
  1904. return(0);
  1905. if (format & DT_TABSTOP)
  1906. {
  1907. DTparams.cbSize = sizeof(DRAWTEXTPARAMS);
  1908. DTparams.iLeftMargin = DTparams.iRightMargin = 0;
  1909. DTparams.iTabLength = (format & 0xff00) >> 8;
  1910. lpDTparams = &DTparams;
  1911. format &= 0xffff00ff;
  1912. }
  1913. return FLDrawTextExPrivW(hdc, (LPWSTR)lpchText, cchText, (LPRECT)lprc, format, lpDTparams);
  1914. }