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.

1041 lines
28 KiB

  1. // unicode.cpp - Unicode functions that work on all 32-bit Window platforms
  2. //
  3. // Rules:
  4. // 1. if Windows NT, then just use the GDI APIs directly
  5. // 2. if Windows 95/98 call our Unicode functions to do the dirty deeds
  6. // needed for those pesky pre-compiled headers
  7. #include "header.h"
  8. #include <windows.h>
  9. #include <malloc.h>
  10. #include "unicode.h"
  11. #ifndef ASSERT
  12. #if defined(_DEBUG) || defined(DEBUG)
  13. #define ASSERT(b) if(b) MessageBox(NULL, "FAILED: #b", "ASSERT", MB_OK );
  14. #else
  15. #define ASSERT(b)
  16. #endif
  17. #endif
  18. //======================
  19. // From VS6 minar.cpp
  20. //======================
  21. void CBufImpl::Clear()
  22. {
  23. if (m_pData)
  24. free(m_pData);
  25. m_pData = NULL;
  26. m_cb = 0;
  27. }
  28. HRESULT CBufImpl::_SetByteSize (int cb)
  29. {
  30. if (cb > m_cb)
  31. {
  32. cb = ((cb + 64) & ~63);
  33. if (m_cb)
  34. {
  35. ASSERT(NULL != m_pData);
  36. BYTE * pb = (BYTE*)realloc(m_pData, cb);
  37. if (pb)
  38. {
  39. m_pData = pb;
  40. m_cb = cb;
  41. }
  42. else
  43. return E_OUTOFMEMORY;
  44. }
  45. else
  46. {
  47. ASSERT(NULL == m_pData);
  48. m_pData = (BYTE*)malloc(cb);
  49. if (m_pData)
  50. m_cb = cb;
  51. else
  52. return E_OUTOFMEMORY;
  53. }
  54. }
  55. #ifdef _DEBUG
  56. // fill unused area to aid debugging
  57. if (cb < m_cb)
  58. {
  59. memset(m_pData + cb, -1, m_cb - cb);
  60. }
  61. #endif
  62. return S_OK;
  63. }
  64. HRESULT CBufImpl::SetByteSizeShrink (int cb)
  65. {
  66. Clear();
  67. if (0 == cb)
  68. return S_OK;
  69. if (cb >= m_cb)
  70. return _SetByteSize(cb);
  71. m_pData = (BYTE*)malloc(cb);
  72. if (m_pData)
  73. m_cb = cb;
  74. else
  75. return E_OUTOFMEMORY;
  76. return S_OK;
  77. }
  78. //======================
  79. // From VS6 intlutil.cpp
  80. //======================
  81. /////////////////////////////////////////////////////////////////
  82. // if some component ever bypasses the GDI versions of these APIs
  83. // we will need to get the real APIs for GDI32.
  84. //
  85. //#define GET_REAL_GDI_PFNS
  86. #ifdef GET_REAL_GDI_PFNS
  87. typedef BOOL (WINAPI *PFN_GDI_ExtTextOutW) (HDC, int, int, UINT, CONST RECT *,LPCWSTR, UINT, CONST INT *);
  88. typedef BOOL (APIENTRY *PFN_GDI_GetTextExtentPoint32W) (HDC, LPCWSTR, int, LPSIZE);
  89. typedef BOOL (APIENTRY *PFN_GDI_GetTextExtentExPointW) (HDC, LPCWSTR, int, int, LPINT, LPINT, LPSIZE);
  90. PFN_GDI_ExtTextOutW GdiExtTextOutW = NULL;
  91. PFN_GDI_GetTextExtentPoint32W GdiGetTextExtentPoint32W = NULL;
  92. PFN_GDI_GetTextExtentExPointW GdiGetTextExtentExPointW = NULL;
  93. void LoadAPIs()
  94. {
  95. HMODULE hGDI = GetModuleHandleA("GDI32");
  96. ASSERT(hGDI);
  97. GdiExtTextOutW = (PFN_GDI_ExtTextOutW )GetProcAddress(hGDI, "ExtTextOutW");
  98. GdiGetTextExtentPoint32W = (PFN_GDI_GetTextExtentPoint32W)GetProcAddress(hGDI, "GetTextExtentPointW");
  99. GdiGetTextExtentExPointW = (PFN_GDI_GetTextExtentExPointW)GetProcAddress(hGDI, "GetTextExtentExPointW");
  100. ASSERT(NULL != GdiExtTextOutW);
  101. ASSERT(NULL != GdiGetTextExtentPoint32W);
  102. ASSERT(NULL != GdiGetTextExtentExPointW);
  103. }
  104. #define ENSUREAPIS { if (NULL == GdiExtTextOutW) LoadAPIs(); }
  105. #else // nothing is bypassing GDI so we can just use the real APIs directly
  106. #define GdiExtTextOutW ExtTextOutW
  107. #define GdiGetTextExtentPoint32W GetTextExtentPoint32W
  108. #define GdiGetTextExtentExPointW GetTextExtentExPointW
  109. #define ENSUREAPIS
  110. #endif // GET_REAL_GDI_PFNS
  111. /////////////////////////////////////////////////////////////////
  112. #define USE_WIDE_API_HACK FALSE // Invert Wide API hack flag
  113. #define VALIDATE_SIMULATED_GETTEXTEXTENTEXPOINT FALSE // Validate simulated GetTextExtentExPoint
  114. /////////////////////////////////////////////////////////////////
  115. // These strings are not localized
  116. static const char szFontENU[] = "Courier New";
  117. static const char szFontJPN[] = "\x82\x6c\x82\x72\x20\x83\x53\x83\x56\x83\x62\x83\x4e"; // MS Gothic
  118. static const char szFontKOR[] = "\xb5\xb8\xbf\xf2\xc3\xbc"; // Dotum Che
  119. static const char szFontCHS[] = "\xcb\xce\xcc\xe5"; // Song Ti
  120. static const char szFontCHT[] = "\xb2\xd3\xa9\xfa\xc5\xe9"; // Ming Li
  121. static const char szFontGenericFE[] = "FixedSys";
  122. typedef struct _FI {
  123. UINT uCharset;
  124. int iPtHeight;
  125. const char * szName;
  126. } FI;
  127. static const FI FontDefaults[] = {
  128. ANSI_CHARSET, 10, szFontENU, // First entry is default
  129. SHIFTJIS_CHARSET, 10, szFontJPN,
  130. GB2312_CHARSET, 9, szFontCHS,
  131. HANGEUL_CHARSET, 10, szFontKOR,
  132. JOHAB_CHARSET, 10, szFontKOR,
  133. CHINESEBIG5_CHARSET, 9, szFontCHT,
  134. THAI_CHARSET, 12, szFontGenericFE,
  135. DEFAULT_CHARSET, 10, "", // let fontmapper choose
  136. // These are the same as the default, so we just let them fall to
  137. // the default handling instead of defining/scanning more entries.
  138. // GREEK_CHARSET, 10, szFontENU, // Courier New has Greek
  139. // TURKISH_CHARSET, 10, szFontENU, // Courier New has Turkish
  140. // EASTEUROPE_CHARSET, 10, szFontENU, // Courier New has EE
  141. // RUSSIAN_CHARSET, 10, szFontENU, // Courier New has Cyrillic
  142. // BALTIC_CHARSET, 10, szFontENU, // Courier New has Baltic
  143. // not supported
  144. // OEM_CHARSET, 10, szFont???,
  145. // HEBREW_CHARSET, 10, szFont???,
  146. // ARABIC_CHARSET, 10, szFontENU, // Courier New has Arabic on Arabic systems
  147. // MAC_CHARSET, 10, szFont???,
  148. // nonsense for text
  149. // SYMBOL_CHARSET, 10, "Symbol",
  150. // End of table
  151. 0, 0, 0
  152. };
  153. //---------------------------------------------------------------
  154. // GetDefaultFont - Get default monospaced font for a codepage
  155. //
  156. // IN cp codepage
  157. // INOUT lf logfont
  158. //
  159. void GetDefaultFont(UINT cp, LOGFONT * plf, BYTE *pbySize)
  160. {
  161. int dpi;
  162. CHARSETINFO csi;
  163. BYTE bySize = 12;
  164. ASSERT(plf);
  165. memset(plf, 0, sizeof LOGFONT);
  166. { // get display resolution
  167. HDC dc = GetDC(NULL);
  168. dpi = dc ? GetDeviceCaps(dc, LOGPIXELSY) : 72;
  169. ReleaseDC(NULL,dc);
  170. }
  171. // check and normalize codepage
  172. ASSERT(0 == cp || IsValidCodePage(cp));
  173. if (!cp)
  174. cp = GetACP();
  175. // init to crudest defaults
  176. plf->lfCharSet = DEFAULT_CHARSET;
  177. plf->lfPitchAndFamily = FIXED_PITCH;
  178. // translate codepage to charset
  179. memset( &csi, 0, sizeof csi );
  180. if (TranslateCharsetInfo((DWORD*)(DWORD_PTR)cp, &csi, TCI_SRCCODEPAGE))
  181. plf->lfCharSet = (BYTE)csi.ciCharset;
  182. // lookup font that corresponds to the codepage
  183. int i;
  184. for (i = 0; FontDefaults[i].iPtHeight; i++)
  185. {
  186. if (FontDefaults[i].uCharset == plf->lfCharSet)
  187. goto L_Return;
  188. }
  189. i = 0; // first entry in table is the default
  190. L_Return:
  191. if (FontDefaults[i].szName)
  192. strncpy(plf->lfFaceName, FontDefaults[i].szName, LF_FACESIZE);
  193. if (FontDefaults[i].iPtHeight)
  194. bySize = (BYTE)FontDefaults[i].iPtHeight;
  195. plf->lfHeight = -MulDiv(bySize, dpi, 72);
  196. if (pbySize)
  197. *pbySize = bySize;
  198. }
  199. /////////////////////////////////////////////////////////////////
  200. //
  201. // This table derived from information in
  202. // "Developing International Software" by Nadine Kano, Microsoft Press
  203. // Appendix E: Codepage Support in Microsoft Windows
  204. //
  205. // Entries in ascending cp number.
  206. // Only valid ACP entires are listed
  207. //
  208. static const DWORD mapCPtoLCID[] =
  209. {
  210. 874, MAKELCID(MAKELANGID(LANG_THAI, SUBLANG_NEUTRAL), SORT_DEFAULT), // Thai
  211. 932, MAKELCID(MAKELANGID(LANG_JAPANESE, SUBLANG_NEUTRAL), SORT_DEFAULT), // Japanese
  212. 936, MAKELCID(MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL), SORT_DEFAULT), // Chinese Trad. (Hong Kong, Taiwan)
  213. 949, MAKELCID(MAKELANGID(LANG_KOREAN, SUBLANG_KOREAN), SORT_DEFAULT), // Korean (wansung)
  214. 950, MAKELCID(MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED), SORT_DEFAULT), // Chinese Simp. (PRC, Singapore)
  215. // 1200, MAKELCID(MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), SORT_DEFAULT), // Unicode
  216. 1250, MAKELCID(MAKELANGID(LANG_HUNGARIAN, SUBLANG_NEUTRAL), SORT_DEFAULT), // Eastern European
  217. 1251, MAKELCID(MAKELANGID(LANG_RUSSIAN, SUBLANG_NEUTRAL), SORT_DEFAULT), // Cyrillic
  218. 1252, MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_NEUTRAL), SORT_DEFAULT), // Western European (US)
  219. 1253, MAKELCID(MAKELANGID(LANG_GREEK, SUBLANG_NEUTRAL), SORT_DEFAULT), // Greek
  220. 1254, MAKELCID(MAKELANGID(LANG_TURKISH, SUBLANG_NEUTRAL), SORT_DEFAULT), // Turkish
  221. 1255, MAKELCID(MAKELANGID(LANG_HEBREW, SUBLANG_NEUTRAL), SORT_DEFAULT), // Hebrew
  222. 1256, MAKELCID(MAKELANGID(LANG_ARABIC, SUBLANG_NEUTRAL), SORT_DEFAULT), // Arabic
  223. 1257, MAKELCID(MAKELANGID(LANG_ESTONIAN, SUBLANG_NEUTRAL), SORT_DEFAULT), // Baltic: Estonian, Latvian, Lithuanian: Which is best default?
  224. #ifdef SUBLANG_KOREAN_JOHAB
  225. 1361, MAKELCID(MAKELANGID(LANG_KOREAN, SUBLANG_KOREAN_JOHAB), SORT_DEFAULT), // Korean Johab
  226. #endif // SUBLANG_KOREAN_JOHAB
  227. // CP_UTF7, MAKELCID(MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), SORT_DEFAULT), // Unicode UTF-7
  228. // CP_UTF8, MAKELCID(MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), SORT_DEFAULT), // Unicode UTF-8
  229. 0, 0
  230. };
  231. // LCIDFromCodePage - Given a codepage, return a reasonable LCID.
  232. //
  233. // Since there is not a 1-1 mapping, we'll have to choose a somewhat
  234. // arbitrary locale. If we match the current system locale, we'll use it.
  235. // Otherwise, we're looking at something from a different system, and
  236. // we'll have to make a guess. This means that all Western European codepages
  237. // come up as US English when you're not on a WE system.
  238. //
  239. // Currently, EBCDIC, OEM, and MAC codepages not supported.
  240. //
  241. int WINAPI LCIDFromCodePage( UINT cp, LCID * plcid )
  242. {
  243. if ((CP_ACP == cp) || (GetACP() == cp))
  244. {
  245. *plcid = GetUserDefaultLCID();
  246. return LCIDCP_CURRENT;
  247. }
  248. else
  249. {
  250. //lookup something somewhat reasonable
  251. for (int i = 0; mapCPtoLCID[i] > 0; i += 2)
  252. {
  253. if (mapCPtoLCID[i] == cp)
  254. {
  255. *plcid = mapCPtoLCID[i + 1];
  256. return LCIDCP_GUESSED;
  257. }
  258. if (mapCPtoLCID[i] > cp)
  259. break;
  260. }
  261. }
  262. // Unknown: give up
  263. return LCIDCP_UNKNOWN;
  264. }
  265. UINT WINAPI CodePageFromLCID(LCID lcid)
  266. {
  267. char wchLocale[10];
  268. UINT cp;
  269. if (GetLocaleInfoA(lcid, LOCALE_IDEFAULTANSICODEPAGE, wchLocale, sizeof wchLocale))
  270. {
  271. cp = strtoul(wchLocale, NULL, 10);
  272. if (cp)
  273. return cp;
  274. }
  275. #ifdef _DEBUG
  276. else
  277. {
  278. DWORD dwErr = GetLastError();
  279. }
  280. #endif
  281. return GetACP();
  282. }
  283. UINT WINAPI CodepageFromCharset(BYTE cs)
  284. {
  285. CHARSETINFO csi;
  286. TranslateCharsetInfo ((DWORD *)(DWORD_PTR)MAKELONG(cs, 0), &csi, TCI_SRCCHARSET);
  287. return csi.ciACP;
  288. }
  289. //---------------------------------------------------------------
  290. // GetFontCodePage -
  291. //
  292. // Returns the code page of the font selected into hdc
  293. //
  294. UINT WINAPI GetFontCodePage (HDC hdc)
  295. {
  296. TEXTMETRIC tm;
  297. CHARSETINFO cs;
  298. GetTextMetrics (hdc, &tm);
  299. TranslateCharsetInfo ((DWORD *)(DWORD_PTR)MAKELONG(tm.tmCharSet, 0), &cs, TCI_SRCCHARSET);
  300. return cs.ciACP;
  301. }
  302. //---------------------------------------------------------------
  303. // Returns non-zero if this is a DBCS version of GDI
  304. //
  305. BOOL WINAPI IsDbcsGdi()
  306. {
  307. static int iDbcs = -2;
  308. if (-2 != iDbcs)
  309. return iDbcs;
  310. WORD lang = PRIMARYLANGID(LOWORD(GetSystemDefaultLCID()));
  311. iDbcs = (LANG_JAPANESE == lang)
  312. || (LANG_CHINESE == lang)
  313. || (LANG_KOREAN == lang)
  314. || (LANG_ARABIC == lang)
  315. || (LANG_HEBREW == lang);
  316. return iDbcs;
  317. }
  318. BOOL WINAPI IsWin95OrLess()
  319. {
  320. static int iWin95 = -2;
  321. if (-2 != iWin95)
  322. return iWin95;
  323. OSVERSIONINFO osVerInfo;
  324. osVerInfo.dwOSVersionInfoSize = sizeof (osVerInfo);
  325. GetVersionEx (&osVerInfo);
  326. iWin95 = ((osVerInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) &&
  327. ((osVerInfo.dwMajorVersion < 4) ||
  328. ((osVerInfo.dwMajorVersion == 4) && (osVerInfo.dwMinorVersion < 1))) );
  329. return iWin95;
  330. }
  331. BOOL WINAPI IsNT()
  332. {
  333. static int iNT = -2;
  334. if (-2 != iNT)
  335. return iNT;
  336. OSVERSIONINFO osver;
  337. memset (&osver, 0, sizeof osver);
  338. osver.dwOSVersionInfoSize = sizeof osver;
  339. GetVersionEx(&osver);
  340. iNT = (osver.dwPlatformId == VER_PLATFORM_WIN32_NT);
  341. return iNT;
  342. }
  343. //---------------------------------------------------------------
  344. // WideAPIHack
  345. //
  346. // Returns non-zero if this version of Windows has bugs in UNICODE
  347. // API - GetTextExtentPoint32W, ExtTextOut, etc
  348. //
  349. inline BOOL WINAPI WideAPIHack()
  350. {
  351. static int iHack = -2;
  352. if (-2 == iHack)
  353. {
  354. iHack = IsDbcsGdi() && IsWin95OrLess();
  355. }
  356. if (USE_WIDE_API_HACK) // Use hack anyway?
  357. return !iHack;
  358. return iHack;
  359. }
  360. //---------------------------------------------------------------------------
  361. // Text utility services
  362. //---------------------------------------------------------------------------
  363. // conversion buffers
  364. static CMinimalArray<CHAR> INTL_arText;
  365. static CMinimalArray<int> INTL_arDx;
  366. // cached codepage info
  367. static CPINFO INTL_cpi;
  368. static UINT INTL_cp = (UINT)-1;
  369. inline BOOL WINAPI IsSupportedFontCodePage(UINT cp)
  370. {
  371. if ((cp == CP_ACP))
  372. return TRUE;
  373. return IsValidCodePage(cp)
  374. && (cp != CP_UNICODE)
  375. && (cp != CP_UTF7)
  376. && (cp != CP_UTF8)
  377. ;
  378. }
  379. BOOL IsLeadByte(BYTE ch, CPINFO * pcpi)
  380. {
  381. //if (pcpi->MaxCharSize < 2) return FALSE; // SBCS
  382. for (int i = 0; i < 10; i += 2) // max 5 lead byte ranges
  383. {
  384. if (!pcpi->LeadByte[i])
  385. return FALSE; // no more lead byte ranges
  386. if (IN_RANGE(ch, pcpi->LeadByte[i], pcpi->LeadByte[i+1]))
  387. return TRUE;
  388. }
  389. return FALSE;
  390. }
  391. CPINFO * GetCachedCPInfo(UINT cp)
  392. {
  393. ASSERT(IsSupportedFontCodePage(cp));
  394. if (cp == INTL_cp)
  395. return &INTL_cpi;
  396. memset(&INTL_cpi, 0, sizeof(CPINFO));
  397. if (!GetCPInfo(cp, &INTL_cpi))
  398. {
  399. ASSERT(0); // this should never fail!
  400. return NULL;
  401. }
  402. INTL_cp = cp;
  403. return &INTL_cpi;
  404. }
  405. UINT GetCodePage(HDC hdc, UINT *pCP)
  406. {
  407. UINT cp;
  408. if (!WideAPIHack())
  409. {
  410. cp = (pCP) ? *pCP : GetFontCodePage (hdc);
  411. if (IsSupportedFontCodePage(cp))
  412. return cp;
  413. }
  414. cp = CodePageFromLCID(GetThreadLocale());
  415. return cp;
  416. }
  417. //---------------------------------------------------------------
  418. // IntlGetTextExtentPoint32W
  419. //
  420. // This exists to work around bugs in the W API in pre-Memphis Win95
  421. //
  422. BOOL IntlGetTextExtentPoint32W (HDC hdc, LPCWSTR lpString, int cch, LPSIZE lpSize, UINT *pCP)
  423. {
  424. ENSUREAPIS
  425. BOOL fRet;
  426. if (!WideAPIHack())
  427. {
  428. fRet = GdiGetTextExtentPoint32W (hdc, lpString, cch, lpSize);
  429. if (fRet)
  430. return fRet;
  431. #ifdef _DEBUG
  432. DWORD e = GetLastError();
  433. #endif
  434. }
  435. if (FAILED(INTL_arText.SetSize(2*cch)))
  436. {
  437. return 0;
  438. }
  439. CHAR * psz;
  440. long cb;
  441. UINT cp;
  442. cp = GetCodePage(hdc, pCP);
  443. psz = INTL_arText;
  444. cb = WideCharToMultiByte (cp, 0, lpString, cch, psz, 2*cch, 0, 0);
  445. fRet = GetTextExtentPoint32A (hdc, psz, cb, lpSize);
  446. #ifdef _DEBUG
  447. if (!fRet)
  448. {
  449. DWORD e = GetLastError();
  450. ASSERT(0);
  451. }
  452. #endif
  453. return fRet;
  454. }
  455. //---------------------------------------------------------------
  456. // IntlExtTextOut
  457. //
  458. // Take in an MBCS string and do a Unicode Text out
  459. // this requires that the caller provide the proper codepage for the conversion
  460. //
  461. BOOL IntlExtTextOut (HDC hdc, int X, int Y, UINT fuOptions, CONST RECT *lprc, LPCSTR lpString, UINT cch, CONST INT *lpDx, UINT *pCP)
  462. {
  463. WCHAR* pwszString = new WCHAR[cch];
  464. if( MultiByteToWideChar( *pCP, 0, lpString, cch, pwszString, cch*sizeof(WCHAR) ) == 0 )
  465. return FALSE;
  466. BOOL bReturn = IntlExtTextOutW( hdc, X, Y, fuOptions, lprc, pwszString, cch, lpDx, pCP );
  467. if( pwszString )
  468. delete [] pwszString;
  469. return bReturn;
  470. }
  471. //---------------------------------------------------------------
  472. // IntlExtTextOutW
  473. //
  474. // This exists to work around bugs in the W API in pre-Memphis FE Win95
  475. //
  476. BOOL IntlExtTextOutW (HDC hdc, int X, int Y, UINT fuOptions, CONST RECT *lprc, LPCWSTR lpString, UINT cch, CONST INT *lpDx, UINT *pCP)
  477. {
  478. ENSUREAPIS
  479. BOOL fRet;
  480. if (!WideAPIHack())
  481. {
  482. fRet = GdiExtTextOutW (hdc, X, Y, fuOptions, lprc, lpString, cch, lpDx);
  483. if (fRet)
  484. return fRet;
  485. #ifdef _DEBUG
  486. DWORD e = GetLastError();
  487. #endif
  488. }
  489. if (FAILED(INTL_arText.SetSize(2*cch)))
  490. {
  491. return 0;
  492. }
  493. CHAR * psz;
  494. long cb;
  495. UINT cp;
  496. cp = GetCodePage(hdc, pCP);
  497. psz = INTL_arText;
  498. cb = WideCharToMultiByte (cp, 0, lpString, cch, psz, 2*cch, 0, 0);
  499. if (lpDx)
  500. {
  501. // Map delta info if we have a MBCS codepage
  502. //
  503. CPINFO * pcpi = GetCachedCPInfo(cp);
  504. if (!pcpi)
  505. {
  506. ASSERT(0);
  507. lpDx = NULL;
  508. goto _Eto;
  509. }
  510. if (pcpi->MaxCharSize > 1) // Multibyte
  511. {
  512. if (SUCCEEDED(INTL_arDx.SetSize(2*cch)))
  513. {
  514. LPINT pdx = INTL_arDx;
  515. CHAR *pch = psz;
  516. for (UINT i = 0; i < cch; i++)
  517. {
  518. if (IsLeadByte(*pch++, pcpi))
  519. {
  520. *pdx++ = *lpDx++;
  521. pch++;
  522. *pdx++ = 0;
  523. }
  524. else
  525. {
  526. *pdx++ = *lpDx++;
  527. }
  528. }
  529. lpDx = INTL_arDx;
  530. }
  531. else
  532. // OOM: just send it out without spacing info -- what else can we do?
  533. lpDx = NULL;
  534. }
  535. }
  536. _Eto:
  537. fRet = ExtTextOutA (hdc, X, Y, fuOptions, lprc, psz, cb, lpDx);
  538. #ifdef _DEBUG
  539. if (!fRet)
  540. {
  541. DWORD e = GetLastError();
  542. }
  543. #endif
  544. return fRet;
  545. }
  546. // SimulateGetTextExtentExPointW
  547. // Algorithm:
  548. // 1) Convert to multibyte with known replacement char
  549. // 2) Use GetTextExtentExPointA
  550. // 3) While mapping dx's back to wide dx's, use GetTextExtentPoint32W for replaced characters
  551. //
  552. // This is much faster than iterating GetTextExtentPoint32W.
  553. //
  554. BOOL SimulateGetTextExtentExPointW(HDC hdc, LPCWSTR lpString, int cch, int nMaxExtent, LPINT lpnFit, LPINT alpDx, LPSIZE lpSize, UINT * pCP)
  555. {
  556. #define SZDEFAULT "\1"
  557. #define CHDEFAULT '\1'
  558. if (FAILED(INTL_arText.SetSize(2*cch)))
  559. return 0;
  560. BOOL fRet;
  561. int * pdx;
  562. CPINFO * pcpi;
  563. CHAR * psz;
  564. long cb;
  565. UINT cp = (pCP) ? *pCP : GetFontCodePage (hdc);
  566. psz = INTL_arText;
  567. // Convert string
  568. cb = WideCharToMultiByte (cp, 0, lpString, cch, psz, 2*cch, SZDEFAULT, 0);
  569. #ifdef _DEBUG
  570. if (0 == cb)
  571. {
  572. DWORD e = GetLastError();
  573. }
  574. #endif
  575. pcpi = GetCachedCPInfo(cp);
  576. // Getting extents?
  577. if (NULL != alpDx)
  578. {
  579. // Map MBCS extents?
  580. if (pcpi && pcpi->MaxCharSize == 1)
  581. {
  582. // SBCS: no mapping required - use caller's array directly
  583. pdx = alpDx;
  584. }
  585. else
  586. {
  587. // MBCS: must map array
  588. if (FAILED(INTL_arDx.SetSize(cb)))
  589. {
  590. return 0;
  591. }
  592. pdx = INTL_arDx;
  593. }
  594. }
  595. else
  596. pdx = NULL;
  597. int nFit = cb;
  598. if (!lpnFit)
  599. nMaxExtent = 32750;
  600. // Measure!
  601. fRet = GetTextExtentExPointA (hdc, psz, cb, nMaxExtent, &nFit, pdx, lpSize);
  602. if (!fRet)
  603. {
  604. ASSERT(0);
  605. nFit = 0;
  606. #ifdef _DEBUG
  607. DWORD e = GetLastError();
  608. #endif
  609. }
  610. if ((NULL != alpDx) && fRet)
  611. {
  612. LPCWSTR pwch = lpString;
  613. int dxOut = 0;
  614. int dxChar;
  615. SIZE size;
  616. // Map MBCS extents?
  617. if (pcpi && pcpi->MaxCharSize > 1)
  618. {
  619. ASSERT(nFit >= 0 && nFit <= cb);
  620. ASSERT(!IsLeadByte(CHDEFAULT, pcpi));
  621. #ifdef _DEBUG
  622. int * pUDx = alpDx;
  623. #endif
  624. int nMB = 0;
  625. BOOL fDBCSGDI = IsDbcsGdi();
  626. for (int i = 0; i < nFit; i++)
  627. {
  628. if (IsLeadByte(*psz, pcpi))
  629. {
  630. if (!fDBCSGDI)
  631. dxChar = (i) ? ((*pdx) - (*(pdx-1))) : (*pdx);
  632. // advance to trail byte
  633. nMB++;
  634. pdx++;
  635. psz++;
  636. if (fDBCSGDI)
  637. dxChar = (i) ? ((*pdx) - (*(pdx-2))) : (*pdx);
  638. // advance to next char
  639. i++;
  640. psz++;
  641. pdx++;
  642. }
  643. else
  644. {
  645. if (CHDEFAULT == *psz)
  646. {
  647. GdiGetTextExtentPoint32W(hdc, pwch, 1, &size);
  648. dxChar = size.cx;
  649. }
  650. else
  651. {
  652. dxChar = (i) ? ((*pdx) - (*(pdx-1))) : (*pdx);
  653. }
  654. pdx++;
  655. psz++;
  656. }
  657. pwch++;
  658. dxOut += dxChar;
  659. ASSERT(alpDx-pUDx < cch); // if this fires, you aren't tracking the pointers correctly
  660. *alpDx++ = dxOut;
  661. }
  662. nFit -= nMB;
  663. }
  664. else
  665. {
  666. for (int i = 0; i < nFit; i++)
  667. {
  668. if (CHDEFAULT == *psz)
  669. {
  670. GdiGetTextExtentPoint32W(hdc, pwch, 1, &size);
  671. dxChar = size.cx;
  672. }
  673. else
  674. {
  675. dxChar = (i) ? ((*alpDx) - (*(alpDx-1))) : (*alpDx);
  676. }
  677. dxOut += dxChar;
  678. *alpDx++ = dxOut;
  679. psz++;
  680. pwch++;
  681. }
  682. }
  683. if (lpSize)
  684. lpSize->cx = dxOut;
  685. }
  686. if (lpnFit)
  687. *lpnFit = nFit;
  688. return fRet;
  689. }
  690. //---------------------------------------------------------------
  691. // IntlGetTextExtentExPointW
  692. //
  693. BOOL IntlGetTextExtentExPointW(HDC hdc, LPCWSTR lpString, int cch, int nMaxExtent, LPINT lpnFit, LPINT alpDx, LPSIZE lpSize, UINT *pCP)
  694. {
  695. ENSUREAPIS
  696. if (VALIDATE_SIMULATED_GETTEXTEXTENTEXPOINT)
  697. {
  698. return SimulateGetTextExtentExPointW(hdc, lpString, cch, nMaxExtent, lpnFit, alpDx, lpSize, pCP);
  699. }
  700. static BOOL fUseWideAPI = TRUE;
  701. DWORD err;
  702. BOOL fRet = FALSE;
  703. if (!WideAPIHack())
  704. {
  705. if (fUseWideAPI)
  706. {
  707. fRet = GdiGetTextExtentExPointW (hdc, lpString, cch, nMaxExtent, lpnFit, alpDx, lpSize);
  708. if (fRet)
  709. return fRet;
  710. err = GetLastError();
  711. if (ERROR_CALL_NOT_IMPLEMENTED == err)
  712. fUseWideAPI = FALSE;
  713. }
  714. fRet = SimulateGetTextExtentExPointW(hdc, lpString, cch, nMaxExtent, lpnFit, alpDx, lpSize, pCP);
  715. if (fRet)
  716. return fRet;
  717. }
  718. ASSERT(NULL != lpString);
  719. if (FAILED(INTL_arText.SetSize(2*cch)))
  720. {
  721. return 0;
  722. }
  723. UINT cp = (UINT)-1;
  724. int * pdx;
  725. CPINFO * pcpi;
  726. CHAR * psz;
  727. long cb;
  728. cp = GetCodePage(hdc, pCP);
  729. if (NULL == (pcpi = GetCachedCPInfo(cp)))
  730. cp = CodePageFromLCID(GetThreadLocale());
  731. psz = INTL_arText;
  732. // Convert string
  733. cb = WideCharToMultiByte (cp, 0, lpString, cch, psz, 2*cch, 0, 0);
  734. // Getting extents?
  735. if (NULL != alpDx)
  736. {
  737. // Map MBCS extents?
  738. if (pcpi && pcpi->MaxCharSize == 1)
  739. {
  740. // SBCS: no mapping required - use caller's array directly
  741. pdx = alpDx;
  742. }
  743. else
  744. {
  745. // MBCS: must map array
  746. if (FAILED(INTL_arDx.SetSize(cb)))
  747. {
  748. return 0;
  749. }
  750. pdx = INTL_arDx;
  751. }
  752. }
  753. else
  754. pdx = NULL;
  755. int nFit = cb;
  756. if (!lpnFit)
  757. nMaxExtent = 32750;
  758. // Measure!
  759. fRet = GetTextExtentExPointA (hdc, psz, cb, nMaxExtent, &nFit, pdx, lpSize);
  760. if (!fRet)
  761. {
  762. ASSERT(0);
  763. nFit = 0;
  764. #ifdef _DEBUG
  765. DWORD e = GetLastError();
  766. #endif
  767. }
  768. if ((NULL != alpDx) && fRet)
  769. {
  770. // Map MBCS extents?
  771. if (pcpi && pcpi->MaxCharSize > 1)
  772. {
  773. ASSERT(nFit >= 0 && nFit <= cb);
  774. #ifdef _DEBUG
  775. int * pUDx = alpDx;
  776. #endif
  777. int nMB = 0;
  778. for (int i = 0; i < nFit; i++)
  779. {
  780. if (IsLeadByte(*psz++, pcpi))
  781. {
  782. nMB++;
  783. pdx++;
  784. psz++;
  785. i++;
  786. }
  787. ASSERT(alpDx-pUDx < cch);
  788. *alpDx++ = *pdx++;
  789. }
  790. nFit -= nMB;
  791. }
  792. }
  793. if (lpnFit)
  794. *lpnFit = nFit;
  795. return fRet;
  796. }
  797. // IsStringDisplayable()
  798. //
  799. // This function computes if all characters are displayable under current system's
  800. // default codepage. It does this by converting the input string (ANSI/DBCS) to
  801. // Unicode using the string's native codepage and then convert it back to ANSI/DBCS
  802. // using the system default codepage. If unmappable characters are detected
  803. // during either conversion it means the string will not display properly under
  804. // the system's default codepage.
  805. //
  806. // This function returns a pointer to the string that sucessfully made it round trip.
  807. // This is necessary because the system sometimes normalizes character to make them
  808. // displayable (and we need to use the modified version).
  809. //
  810. // The caller is responsible for freeing this return string.
  811. //
  812. BOOL IsStringDisplayable(const char *pszString, UINT codepage)
  813. {
  814. if(!pszString)
  815. return FALSE;
  816. // allocate buffer for Unicode string
  817. //
  818. int cUnicodeLen = (int)(strlen(pszString) * 2) + 4;
  819. WCHAR *pszUnicodeBuffer = (WCHAR *) lcMalloc(cUnicodeLen);
  820. if(!pszUnicodeBuffer)
  821. return FALSE;
  822. // Convert string to Unicode
  823. int ret = MultiByteToWideChar(codepage, MB_ERR_INVALID_CHARS, pszString, -1, pszUnicodeBuffer, cUnicodeLen);
  824. // See if we had unmappable characters on our way to Unicode
  825. //
  826. if(!ret && GetLastError() == ERROR_NO_UNICODE_TRANSLATION)
  827. {
  828. lcFree(pszUnicodeBuffer);
  829. return FALSE;
  830. }
  831. // other failure (same return)
  832. if(!ret)
  833. {
  834. lcFree(pszUnicodeBuffer);
  835. return FALSE;
  836. }
  837. // allocate the return ANSI/DBCS buffer
  838. //
  839. char *pszAnsiBuffer = (char *) lcMalloc(cUnicodeLen + 2);
  840. if(!pszAnsiBuffer)
  841. {
  842. lcFree(pszUnicodeBuffer);
  843. return FALSE;
  844. }
  845. BOOL bDefaultChar = FALSE, bExactMatch = FALSE;
  846. // Convert back to ANSI/DBCS using default codepage
  847. //
  848. ret = WideCharToMultiByte(CP_ACP, 0, pszUnicodeBuffer, -1, pszAnsiBuffer, cUnicodeLen+2, ".", &bDefaultChar);
  849. if(!strcmp(pszAnsiBuffer,pszString))
  850. bExactMatch = TRUE;
  851. // free our buffers
  852. //
  853. lcFree(pszAnsiBuffer);
  854. lcFree(pszUnicodeBuffer);
  855. // check if default character was used
  856. //
  857. if(!ret || bDefaultChar || !bExactMatch)
  858. return FALSE;
  859. // success!
  860. //
  861. return TRUE;
  862. }
  863. ///////////////////////////////////////////////////////////////////////////////
  864. // MUI support
  865. //
  866. // This AppendMenu wrapper will (under NT5) get a Unicode resource and
  867. // then call AppendMenuW().
  868. //
  869. BOOL HxAppendMenu(HMENU hMenu, UINT uFlags, UINT uIDNewItem, LPCTSTR lpNewItem)
  870. {
  871. if(g_bWinNT5 && !uFlags && lpNewItem)
  872. {
  873. DWORD cp = CodePageFromLCID(MAKELCID(_Module.m_Language.GetUiLanguage(),SORT_DEFAULT));
  874. DWORD dwSize = (DWORD)(sizeof(WCHAR) * strlen(lpNewItem)) + 4;
  875. WCHAR *pwcString = (WCHAR *) lcMalloc(dwSize);
  876. if(!pwcString)
  877. return FALSE;
  878. MultiByteToWideChar(cp, MB_PRECOMPOSED, lpNewItem, -1, pwcString, dwSize);
  879. BOOL ret = AppendMenuW(hMenu, uFlags, uIDNewItem, pwcString);
  880. lcFree(pwcString);
  881. return ret;
  882. }
  883. else
  884. {
  885. return AppendMenu(hMenu, uFlags, uIDNewItem, lpNewItem);
  886. }
  887. }
  888. ///////////////////////////////////////////////////////////////////////////////
  889. // MUI support
  890. //
  891. // This SetWindowText wrapper will (under NT5) convert the string to Unicode
  892. // based on the MUI setting and then call SetWindowTextW().
  893. //
  894. BOOL HxSetWindowText(HWND hWnd, LPCTSTR lpString)
  895. {
  896. if(g_bWinNT5 && lpString)
  897. {
  898. DWORD cp = CodePageFromLCID(MAKELCID(_Module.m_Language.GetUiLanguage(),SORT_DEFAULT));
  899. DWORD dwSize = (DWORD)(sizeof(WCHAR) * strlen(lpString)) + 4;
  900. WCHAR *pwcString = (WCHAR *) lcMalloc(dwSize);
  901. if(!pwcString)
  902. return FALSE;
  903. MultiByteToWideChar(cp, MB_PRECOMPOSED, lpString, -1, pwcString, dwSize);
  904. BOOL ret = SetWindowTextW(hWnd, pwcString);
  905. lcFree(pwcString);
  906. return ret;
  907. }
  908. else
  909. {
  910. return SetWindowText(hWnd, lpString);
  911. }
  912. }