windows-nt/Source/XPSP1/NT/enduser/stuff/hhctrl/unicode.cpp
2020-09-26 16:20:57 +08:00

1042 lines
28 KiB
C++

// unicode.cpp - Unicode functions that work on all 32-bit Window platforms
//
// Rules:
// 1. if Windows NT, then just use the GDI APIs directly
// 2. if Windows 95/98 call our Unicode functions to do the dirty deeds
// needed for those pesky pre-compiled headers
#include "header.h"
#include <windows.h>
#include <malloc.h>
#include "unicode.h"
#ifndef ASSERT
#if defined(_DEBUG) || defined(DEBUG)
#define ASSERT(b) if(b) MessageBox(NULL, "FAILED: #b", "ASSERT", MB_OK );
#else
#define ASSERT(b)
#endif
#endif
//======================
// From VS6 minar.cpp
//======================
void CBufImpl::Clear()
{
if (m_pData)
free(m_pData);
m_pData = NULL;
m_cb = 0;
}
HRESULT CBufImpl::_SetByteSize (int cb)
{
if (cb > m_cb)
{
cb = ((cb + 64) & ~63);
if (m_cb)
{
ASSERT(NULL != m_pData);
BYTE * pb = (BYTE*)realloc(m_pData, cb);
if (pb)
{
m_pData = pb;
m_cb = cb;
}
else
return E_OUTOFMEMORY;
}
else
{
ASSERT(NULL == m_pData);
m_pData = (BYTE*)malloc(cb);
if (m_pData)
m_cb = cb;
else
return E_OUTOFMEMORY;
}
}
#ifdef _DEBUG
// fill unused area to aid debugging
if (cb < m_cb)
{
memset(m_pData + cb, -1, m_cb - cb);
}
#endif
return S_OK;
}
HRESULT CBufImpl::SetByteSizeShrink (int cb)
{
Clear();
if (0 == cb)
return S_OK;
if (cb >= m_cb)
return _SetByteSize(cb);
m_pData = (BYTE*)malloc(cb);
if (m_pData)
m_cb = cb;
else
return E_OUTOFMEMORY;
return S_OK;
}
//======================
// From VS6 intlutil.cpp
//======================
/////////////////////////////////////////////////////////////////
// if some component ever bypasses the GDI versions of these APIs
// we will need to get the real APIs for GDI32.
//
//#define GET_REAL_GDI_PFNS
#ifdef GET_REAL_GDI_PFNS
typedef BOOL (WINAPI *PFN_GDI_ExtTextOutW) (HDC, int, int, UINT, CONST RECT *,LPCWSTR, UINT, CONST INT *);
typedef BOOL (APIENTRY *PFN_GDI_GetTextExtentPoint32W) (HDC, LPCWSTR, int, LPSIZE);
typedef BOOL (APIENTRY *PFN_GDI_GetTextExtentExPointW) (HDC, LPCWSTR, int, int, LPINT, LPINT, LPSIZE);
PFN_GDI_ExtTextOutW GdiExtTextOutW = NULL;
PFN_GDI_GetTextExtentPoint32W GdiGetTextExtentPoint32W = NULL;
PFN_GDI_GetTextExtentExPointW GdiGetTextExtentExPointW = NULL;
void LoadAPIs()
{
HMODULE hGDI = GetModuleHandleA("GDI32");
ASSERT(hGDI);
GdiExtTextOutW = (PFN_GDI_ExtTextOutW )GetProcAddress(hGDI, "ExtTextOutW");
GdiGetTextExtentPoint32W = (PFN_GDI_GetTextExtentPoint32W)GetProcAddress(hGDI, "GetTextExtentPointW");
GdiGetTextExtentExPointW = (PFN_GDI_GetTextExtentExPointW)GetProcAddress(hGDI, "GetTextExtentExPointW");
ASSERT(NULL != GdiExtTextOutW);
ASSERT(NULL != GdiGetTextExtentPoint32W);
ASSERT(NULL != GdiGetTextExtentExPointW);
}
#define ENSUREAPIS { if (NULL == GdiExtTextOutW) LoadAPIs(); }
#else // nothing is bypassing GDI so we can just use the real APIs directly
#define GdiExtTextOutW ExtTextOutW
#define GdiGetTextExtentPoint32W GetTextExtentPoint32W
#define GdiGetTextExtentExPointW GetTextExtentExPointW
#define ENSUREAPIS
#endif // GET_REAL_GDI_PFNS
/////////////////////////////////////////////////////////////////
#define USE_WIDE_API_HACK FALSE // Invert Wide API hack flag
#define VALIDATE_SIMULATED_GETTEXTEXTENTEXPOINT FALSE // Validate simulated GetTextExtentExPoint
/////////////////////////////////////////////////////////////////
// These strings are not localized
static const char szFontENU[] = "Courier New";
static const char szFontJPN[] = "\x82\x6c\x82\x72\x20\x83\x53\x83\x56\x83\x62\x83\x4e"; // MS Gothic
static const char szFontKOR[] = "\xb5\xb8\xbf\xf2\xc3\xbc"; // Dotum Che
static const char szFontCHS[] = "\xcb\xce\xcc\xe5"; // Song Ti
static const char szFontCHT[] = "\xb2\xd3\xa9\xfa\xc5\xe9"; // Ming Li
static const char szFontGenericFE[] = "FixedSys";
typedef struct _FI {
UINT uCharset;
int iPtHeight;
const char * szName;
} FI;
static const FI FontDefaults[] = {
ANSI_CHARSET, 10, szFontENU, // First entry is default
SHIFTJIS_CHARSET, 10, szFontJPN,
GB2312_CHARSET, 9, szFontCHS,
HANGEUL_CHARSET, 10, szFontKOR,
JOHAB_CHARSET, 10, szFontKOR,
CHINESEBIG5_CHARSET, 9, szFontCHT,
THAI_CHARSET, 12, szFontGenericFE,
DEFAULT_CHARSET, 10, "", // let fontmapper choose
// These are the same as the default, so we just let them fall to
// the default handling instead of defining/scanning more entries.
// GREEK_CHARSET, 10, szFontENU, // Courier New has Greek
// TURKISH_CHARSET, 10, szFontENU, // Courier New has Turkish
// EASTEUROPE_CHARSET, 10, szFontENU, // Courier New has EE
// RUSSIAN_CHARSET, 10, szFontENU, // Courier New has Cyrillic
// BALTIC_CHARSET, 10, szFontENU, // Courier New has Baltic
// not supported
// OEM_CHARSET, 10, szFont???,
// HEBREW_CHARSET, 10, szFont???,
// ARABIC_CHARSET, 10, szFontENU, // Courier New has Arabic on Arabic systems
// MAC_CHARSET, 10, szFont???,
// nonsense for text
// SYMBOL_CHARSET, 10, "Symbol",
// End of table
0, 0, 0
};
//---------------------------------------------------------------
// GetDefaultFont - Get default monospaced font for a codepage
//
// IN cp codepage
// INOUT lf logfont
//
void GetDefaultFont(UINT cp, LOGFONT * plf, BYTE *pbySize)
{
int dpi;
CHARSETINFO csi;
BYTE bySize = 12;
ASSERT(plf);
memset(plf, 0, sizeof LOGFONT);
{ // get display resolution
HDC dc = GetDC(NULL);
dpi = dc ? GetDeviceCaps(dc, LOGPIXELSY) : 72;
ReleaseDC(NULL,dc);
}
// check and normalize codepage
ASSERT(0 == cp || IsValidCodePage(cp));
if (!cp)
cp = GetACP();
// init to crudest defaults
plf->lfCharSet = DEFAULT_CHARSET;
plf->lfPitchAndFamily = FIXED_PITCH;
// translate codepage to charset
memset( &csi, 0, sizeof csi );
if (TranslateCharsetInfo((DWORD*)(DWORD_PTR)cp, &csi, TCI_SRCCODEPAGE))
plf->lfCharSet = (BYTE)csi.ciCharset;
// lookup font that corresponds to the codepage
int i;
for (i = 0; FontDefaults[i].iPtHeight; i++)
{
if (FontDefaults[i].uCharset == plf->lfCharSet)
goto L_Return;
}
i = 0; // first entry in table is the default
L_Return:
if (FontDefaults[i].szName)
strncpy(plf->lfFaceName, FontDefaults[i].szName, LF_FACESIZE);
if (FontDefaults[i].iPtHeight)
bySize = (BYTE)FontDefaults[i].iPtHeight;
plf->lfHeight = -MulDiv(bySize, dpi, 72);
if (pbySize)
*pbySize = bySize;
}
/////////////////////////////////////////////////////////////////
//
// This table derived from information in
// "Developing International Software" by Nadine Kano, Microsoft Press
// Appendix E: Codepage Support in Microsoft Windows
//
// Entries in ascending cp number.
// Only valid ACP entires are listed
//
static const DWORD mapCPtoLCID[] =
{
874, MAKELCID(MAKELANGID(LANG_THAI, SUBLANG_NEUTRAL), SORT_DEFAULT), // Thai
932, MAKELCID(MAKELANGID(LANG_JAPANESE, SUBLANG_NEUTRAL), SORT_DEFAULT), // Japanese
936, MAKELCID(MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL), SORT_DEFAULT), // Chinese Trad. (Hong Kong, Taiwan)
949, MAKELCID(MAKELANGID(LANG_KOREAN, SUBLANG_KOREAN), SORT_DEFAULT), // Korean (wansung)
950, MAKELCID(MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED), SORT_DEFAULT), // Chinese Simp. (PRC, Singapore)
// 1200, MAKELCID(MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), SORT_DEFAULT), // Unicode
1250, MAKELCID(MAKELANGID(LANG_HUNGARIAN, SUBLANG_NEUTRAL), SORT_DEFAULT), // Eastern European
1251, MAKELCID(MAKELANGID(LANG_RUSSIAN, SUBLANG_NEUTRAL), SORT_DEFAULT), // Cyrillic
1252, MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_NEUTRAL), SORT_DEFAULT), // Western European (US)
1253, MAKELCID(MAKELANGID(LANG_GREEK, SUBLANG_NEUTRAL), SORT_DEFAULT), // Greek
1254, MAKELCID(MAKELANGID(LANG_TURKISH, SUBLANG_NEUTRAL), SORT_DEFAULT), // Turkish
1255, MAKELCID(MAKELANGID(LANG_HEBREW, SUBLANG_NEUTRAL), SORT_DEFAULT), // Hebrew
1256, MAKELCID(MAKELANGID(LANG_ARABIC, SUBLANG_NEUTRAL), SORT_DEFAULT), // Arabic
1257, MAKELCID(MAKELANGID(LANG_ESTONIAN, SUBLANG_NEUTRAL), SORT_DEFAULT), // Baltic: Estonian, Latvian, Lithuanian: Which is best default?
#ifdef SUBLANG_KOREAN_JOHAB
1361, MAKELCID(MAKELANGID(LANG_KOREAN, SUBLANG_KOREAN_JOHAB), SORT_DEFAULT), // Korean Johab
#endif // SUBLANG_KOREAN_JOHAB
// CP_UTF7, MAKELCID(MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), SORT_DEFAULT), // Unicode UTF-7
// CP_UTF8, MAKELCID(MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), SORT_DEFAULT), // Unicode UTF-8
0, 0
};
// LCIDFromCodePage - Given a codepage, return a reasonable LCID.
//
// Since there is not a 1-1 mapping, we'll have to choose a somewhat
// arbitrary locale. If we match the current system locale, we'll use it.
// Otherwise, we're looking at something from a different system, and
// we'll have to make a guess. This means that all Western European codepages
// come up as US English when you're not on a WE system.
//
// Currently, EBCDIC, OEM, and MAC codepages not supported.
//
int WINAPI LCIDFromCodePage( UINT cp, LCID * plcid )
{
if ((CP_ACP == cp) || (GetACP() == cp))
{
*plcid = GetUserDefaultLCID();
return LCIDCP_CURRENT;
}
else
{
//lookup something somewhat reasonable
for (int i = 0; mapCPtoLCID[i] > 0; i += 2)
{
if (mapCPtoLCID[i] == cp)
{
*plcid = mapCPtoLCID[i + 1];
return LCIDCP_GUESSED;
}
if (mapCPtoLCID[i] > cp)
break;
}
}
// Unknown: give up
return LCIDCP_UNKNOWN;
}
UINT WINAPI CodePageFromLCID(LCID lcid)
{
char wchLocale[10];
UINT cp;
if (GetLocaleInfoA(lcid, LOCALE_IDEFAULTANSICODEPAGE, wchLocale, sizeof wchLocale))
{
cp = strtoul(wchLocale, NULL, 10);
if (cp)
return cp;
}
#ifdef _DEBUG
else
{
DWORD dwErr = GetLastError();
}
#endif
return GetACP();
}
UINT WINAPI CodepageFromCharset(BYTE cs)
{
CHARSETINFO csi;
TranslateCharsetInfo ((DWORD *)(DWORD_PTR)MAKELONG(cs, 0), &csi, TCI_SRCCHARSET);
return csi.ciACP;
}
//---------------------------------------------------------------
// GetFontCodePage -
//
// Returns the code page of the font selected into hdc
//
UINT WINAPI GetFontCodePage (HDC hdc)
{
TEXTMETRIC tm;
CHARSETINFO cs;
GetTextMetrics (hdc, &tm);
TranslateCharsetInfo ((DWORD *)(DWORD_PTR)MAKELONG(tm.tmCharSet, 0), &cs, TCI_SRCCHARSET);
return cs.ciACP;
}
//---------------------------------------------------------------
// Returns non-zero if this is a DBCS version of GDI
//
BOOL WINAPI IsDbcsGdi()
{
static int iDbcs = -2;
if (-2 != iDbcs)
return iDbcs;
WORD lang = PRIMARYLANGID(LOWORD(GetSystemDefaultLCID()));
iDbcs = (LANG_JAPANESE == lang)
|| (LANG_CHINESE == lang)
|| (LANG_KOREAN == lang)
|| (LANG_ARABIC == lang)
|| (LANG_HEBREW == lang);
return iDbcs;
}
BOOL WINAPI IsWin95OrLess()
{
static int iWin95 = -2;
if (-2 != iWin95)
return iWin95;
OSVERSIONINFO osVerInfo;
osVerInfo.dwOSVersionInfoSize = sizeof (osVerInfo);
GetVersionEx (&osVerInfo);
iWin95 = ((osVerInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) &&
((osVerInfo.dwMajorVersion < 4) ||
((osVerInfo.dwMajorVersion == 4) && (osVerInfo.dwMinorVersion < 1))) );
return iWin95;
}
BOOL WINAPI IsNT()
{
static int iNT = -2;
if (-2 != iNT)
return iNT;
OSVERSIONINFO osver;
memset (&osver, 0, sizeof osver);
osver.dwOSVersionInfoSize = sizeof osver;
GetVersionEx(&osver);
iNT = (osver.dwPlatformId == VER_PLATFORM_WIN32_NT);
return iNT;
}
//---------------------------------------------------------------
// WideAPIHack
//
// Returns non-zero if this version of Windows has bugs in UNICODE
// API - GetTextExtentPoint32W, ExtTextOut, etc
//
inline BOOL WINAPI WideAPIHack()
{
static int iHack = -2;
if (-2 == iHack)
{
iHack = IsDbcsGdi() && IsWin95OrLess();
}
if (USE_WIDE_API_HACK) // Use hack anyway?
return !iHack;
return iHack;
}
//---------------------------------------------------------------------------
// Text utility services
//---------------------------------------------------------------------------
// conversion buffers
static CMinimalArray<CHAR> INTL_arText;
static CMinimalArray<int> INTL_arDx;
// cached codepage info
static CPINFO INTL_cpi;
static UINT INTL_cp = (UINT)-1;
inline BOOL WINAPI IsSupportedFontCodePage(UINT cp)
{
if ((cp == CP_ACP))
return TRUE;
return IsValidCodePage(cp)
&& (cp != CP_UNICODE)
&& (cp != CP_UTF7)
&& (cp != CP_UTF8)
;
}
BOOL IsLeadByte(BYTE ch, CPINFO * pcpi)
{
//if (pcpi->MaxCharSize < 2) return FALSE; // SBCS
for (int i = 0; i < 10; i += 2) // max 5 lead byte ranges
{
if (!pcpi->LeadByte[i])
return FALSE; // no more lead byte ranges
if (IN_RANGE(ch, pcpi->LeadByte[i], pcpi->LeadByte[i+1]))
return TRUE;
}
return FALSE;
}
CPINFO * GetCachedCPInfo(UINT cp)
{
ASSERT(IsSupportedFontCodePage(cp));
if (cp == INTL_cp)
return &INTL_cpi;
memset(&INTL_cpi, 0, sizeof(CPINFO));
if (!GetCPInfo(cp, &INTL_cpi))
{
ASSERT(0); // this should never fail!
return NULL;
}
INTL_cp = cp;
return &INTL_cpi;
}
UINT GetCodePage(HDC hdc, UINT *pCP)
{
UINT cp;
if (!WideAPIHack())
{
cp = (pCP) ? *pCP : GetFontCodePage (hdc);
if (IsSupportedFontCodePage(cp))
return cp;
}
cp = CodePageFromLCID(GetThreadLocale());
return cp;
}
//---------------------------------------------------------------
// IntlGetTextExtentPoint32W
//
// This exists to work around bugs in the W API in pre-Memphis Win95
//
BOOL IntlGetTextExtentPoint32W (HDC hdc, LPCWSTR lpString, int cch, LPSIZE lpSize, UINT *pCP)
{
ENSUREAPIS
BOOL fRet;
if (!WideAPIHack())
{
fRet = GdiGetTextExtentPoint32W (hdc, lpString, cch, lpSize);
if (fRet)
return fRet;
#ifdef _DEBUG
DWORD e = GetLastError();
#endif
}
if (FAILED(INTL_arText.SetSize(2*cch)))
{
return 0;
}
CHAR * psz;
long cb;
UINT cp;
cp = GetCodePage(hdc, pCP);
psz = INTL_arText;
cb = WideCharToMultiByte (cp, 0, lpString, cch, psz, 2*cch, 0, 0);
fRet = GetTextExtentPoint32A (hdc, psz, cb, lpSize);
#ifdef _DEBUG
if (!fRet)
{
DWORD e = GetLastError();
ASSERT(0);
}
#endif
return fRet;
}
//---------------------------------------------------------------
// IntlExtTextOut
//
// Take in an MBCS string and do a Unicode Text out
// this requires that the caller provide the proper codepage for the conversion
//
BOOL IntlExtTextOut (HDC hdc, int X, int Y, UINT fuOptions, CONST RECT *lprc, LPCSTR lpString, UINT cch, CONST INT *lpDx, UINT *pCP)
{
WCHAR* pwszString = new WCHAR[cch];
if( MultiByteToWideChar( *pCP, 0, lpString, cch, pwszString, cch*sizeof(WCHAR) ) == 0 )
return FALSE;
BOOL bReturn = IntlExtTextOutW( hdc, X, Y, fuOptions, lprc, pwszString, cch, lpDx, pCP );
if( pwszString )
delete [] pwszString;
return bReturn;
}
//---------------------------------------------------------------
// IntlExtTextOutW
//
// This exists to work around bugs in the W API in pre-Memphis FE Win95
//
BOOL IntlExtTextOutW (HDC hdc, int X, int Y, UINT fuOptions, CONST RECT *lprc, LPCWSTR lpString, UINT cch, CONST INT *lpDx, UINT *pCP)
{
ENSUREAPIS
BOOL fRet;
if (!WideAPIHack())
{
fRet = GdiExtTextOutW (hdc, X, Y, fuOptions, lprc, lpString, cch, lpDx);
if (fRet)
return fRet;
#ifdef _DEBUG
DWORD e = GetLastError();
#endif
}
if (FAILED(INTL_arText.SetSize(2*cch)))
{
return 0;
}
CHAR * psz;
long cb;
UINT cp;
cp = GetCodePage(hdc, pCP);
psz = INTL_arText;
cb = WideCharToMultiByte (cp, 0, lpString, cch, psz, 2*cch, 0, 0);
if (lpDx)
{
// Map delta info if we have a MBCS codepage
//
CPINFO * pcpi = GetCachedCPInfo(cp);
if (!pcpi)
{
ASSERT(0);
lpDx = NULL;
goto _Eto;
}
if (pcpi->MaxCharSize > 1) // Multibyte
{
if (SUCCEEDED(INTL_arDx.SetSize(2*cch)))
{
LPINT pdx = INTL_arDx;
CHAR *pch = psz;
for (UINT i = 0; i < cch; i++)
{
if (IsLeadByte(*pch++, pcpi))
{
*pdx++ = *lpDx++;
pch++;
*pdx++ = 0;
}
else
{
*pdx++ = *lpDx++;
}
}
lpDx = INTL_arDx;
}
else
// OOM: just send it out without spacing info -- what else can we do?
lpDx = NULL;
}
}
_Eto:
fRet = ExtTextOutA (hdc, X, Y, fuOptions, lprc, psz, cb, lpDx);
#ifdef _DEBUG
if (!fRet)
{
DWORD e = GetLastError();
}
#endif
return fRet;
}
// SimulateGetTextExtentExPointW
// Algorithm:
// 1) Convert to multibyte with known replacement char
// 2) Use GetTextExtentExPointA
// 3) While mapping dx's back to wide dx's, use GetTextExtentPoint32W for replaced characters
//
// This is much faster than iterating GetTextExtentPoint32W.
//
BOOL SimulateGetTextExtentExPointW(HDC hdc, LPCWSTR lpString, int cch, int nMaxExtent, LPINT lpnFit, LPINT alpDx, LPSIZE lpSize, UINT * pCP)
{
#define SZDEFAULT "\1"
#define CHDEFAULT '\1'
if (FAILED(INTL_arText.SetSize(2*cch)))
return 0;
BOOL fRet;
int * pdx;
CPINFO * pcpi;
CHAR * psz;
long cb;
UINT cp = (pCP) ? *pCP : GetFontCodePage (hdc);
psz = INTL_arText;
// Convert string
cb = WideCharToMultiByte (cp, 0, lpString, cch, psz, 2*cch, SZDEFAULT, 0);
#ifdef _DEBUG
if (0 == cb)
{
DWORD e = GetLastError();
}
#endif
pcpi = GetCachedCPInfo(cp);
// Getting extents?
if (NULL != alpDx)
{
// Map MBCS extents?
if (pcpi && pcpi->MaxCharSize == 1)
{
// SBCS: no mapping required - use caller's array directly
pdx = alpDx;
}
else
{
// MBCS: must map array
if (FAILED(INTL_arDx.SetSize(cb)))
{
return 0;
}
pdx = INTL_arDx;
}
}
else
pdx = NULL;
int nFit = cb;
if (!lpnFit)
nMaxExtent = 32750;
// Measure!
fRet = GetTextExtentExPointA (hdc, psz, cb, nMaxExtent, &nFit, pdx, lpSize);
if (!fRet)
{
ASSERT(0);
nFit = 0;
#ifdef _DEBUG
DWORD e = GetLastError();
#endif
}
if ((NULL != alpDx) && fRet)
{
LPCWSTR pwch = lpString;
int dxOut = 0;
int dxChar;
SIZE size;
// Map MBCS extents?
if (pcpi && pcpi->MaxCharSize > 1)
{
ASSERT(nFit >= 0 && nFit <= cb);
ASSERT(!IsLeadByte(CHDEFAULT, pcpi));
#ifdef _DEBUG
int * pUDx = alpDx;
#endif
int nMB = 0;
BOOL fDBCSGDI = IsDbcsGdi();
for (int i = 0; i < nFit; i++)
{
if (IsLeadByte(*psz, pcpi))
{
if (!fDBCSGDI)
dxChar = (i) ? ((*pdx) - (*(pdx-1))) : (*pdx);
// advance to trail byte
nMB++;
pdx++;
psz++;
if (fDBCSGDI)
dxChar = (i) ? ((*pdx) - (*(pdx-2))) : (*pdx);
// advance to next char
i++;
psz++;
pdx++;
}
else
{
if (CHDEFAULT == *psz)
{
GdiGetTextExtentPoint32W(hdc, pwch, 1, &size);
dxChar = size.cx;
}
else
{
dxChar = (i) ? ((*pdx) - (*(pdx-1))) : (*pdx);
}
pdx++;
psz++;
}
pwch++;
dxOut += dxChar;
ASSERT(alpDx-pUDx < cch); // if this fires, you aren't tracking the pointers correctly
*alpDx++ = dxOut;
}
nFit -= nMB;
}
else
{
for (int i = 0; i < nFit; i++)
{
if (CHDEFAULT == *psz)
{
GdiGetTextExtentPoint32W(hdc, pwch, 1, &size);
dxChar = size.cx;
}
else
{
dxChar = (i) ? ((*alpDx) - (*(alpDx-1))) : (*alpDx);
}
dxOut += dxChar;
*alpDx++ = dxOut;
psz++;
pwch++;
}
}
if (lpSize)
lpSize->cx = dxOut;
}
if (lpnFit)
*lpnFit = nFit;
return fRet;
}
//---------------------------------------------------------------
// IntlGetTextExtentExPointW
//
BOOL IntlGetTextExtentExPointW(HDC hdc, LPCWSTR lpString, int cch, int nMaxExtent, LPINT lpnFit, LPINT alpDx, LPSIZE lpSize, UINT *pCP)
{
ENSUREAPIS
if (VALIDATE_SIMULATED_GETTEXTEXTENTEXPOINT)
{
return SimulateGetTextExtentExPointW(hdc, lpString, cch, nMaxExtent, lpnFit, alpDx, lpSize, pCP);
}
static BOOL fUseWideAPI = TRUE;
DWORD err;
BOOL fRet = FALSE;
if (!WideAPIHack())
{
if (fUseWideAPI)
{
fRet = GdiGetTextExtentExPointW (hdc, lpString, cch, nMaxExtent, lpnFit, alpDx, lpSize);
if (fRet)
return fRet;
err = GetLastError();
if (ERROR_CALL_NOT_IMPLEMENTED == err)
fUseWideAPI = FALSE;
}
fRet = SimulateGetTextExtentExPointW(hdc, lpString, cch, nMaxExtent, lpnFit, alpDx, lpSize, pCP);
if (fRet)
return fRet;
}
ASSERT(NULL != lpString);
if (FAILED(INTL_arText.SetSize(2*cch)))
{
return 0;
}
UINT cp = (UINT)-1;
int * pdx;
CPINFO * pcpi;
CHAR * psz;
long cb;
cp = GetCodePage(hdc, pCP);
if (NULL == (pcpi = GetCachedCPInfo(cp)))
cp = CodePageFromLCID(GetThreadLocale());
psz = INTL_arText;
// Convert string
cb = WideCharToMultiByte (cp, 0, lpString, cch, psz, 2*cch, 0, 0);
// Getting extents?
if (NULL != alpDx)
{
// Map MBCS extents?
if (pcpi && pcpi->MaxCharSize == 1)
{
// SBCS: no mapping required - use caller's array directly
pdx = alpDx;
}
else
{
// MBCS: must map array
if (FAILED(INTL_arDx.SetSize(cb)))
{
return 0;
}
pdx = INTL_arDx;
}
}
else
pdx = NULL;
int nFit = cb;
if (!lpnFit)
nMaxExtent = 32750;
// Measure!
fRet = GetTextExtentExPointA (hdc, psz, cb, nMaxExtent, &nFit, pdx, lpSize);
if (!fRet)
{
ASSERT(0);
nFit = 0;
#ifdef _DEBUG
DWORD e = GetLastError();
#endif
}
if ((NULL != alpDx) && fRet)
{
// Map MBCS extents?
if (pcpi && pcpi->MaxCharSize > 1)
{
ASSERT(nFit >= 0 && nFit <= cb);
#ifdef _DEBUG
int * pUDx = alpDx;
#endif
int nMB = 0;
for (int i = 0; i < nFit; i++)
{
if (IsLeadByte(*psz++, pcpi))
{
nMB++;
pdx++;
psz++;
i++;
}
ASSERT(alpDx-pUDx < cch);
*alpDx++ = *pdx++;
}
nFit -= nMB;
}
}
if (lpnFit)
*lpnFit = nFit;
return fRet;
}
// IsStringDisplayable()
//
// This function computes if all characters are displayable under current system's
// default codepage. It does this by converting the input string (ANSI/DBCS) to
// Unicode using the string's native codepage and then convert it back to ANSI/DBCS
// using the system default codepage. If unmappable characters are detected
// during either conversion it means the string will not display properly under
// the system's default codepage.
//
// This function returns a pointer to the string that sucessfully made it round trip.
// This is necessary because the system sometimes normalizes character to make them
// displayable (and we need to use the modified version).
//
// The caller is responsible for freeing this return string.
//
BOOL IsStringDisplayable(const char *pszString, UINT codepage)
{
if(!pszString)
return FALSE;
// allocate buffer for Unicode string
//
int cUnicodeLen = (int)(strlen(pszString) * 2) + 4;
WCHAR *pszUnicodeBuffer = (WCHAR *) lcMalloc(cUnicodeLen);
if(!pszUnicodeBuffer)
return FALSE;
// Convert string to Unicode
int ret = MultiByteToWideChar(codepage, MB_ERR_INVALID_CHARS, pszString, -1, pszUnicodeBuffer, cUnicodeLen);
// See if we had unmappable characters on our way to Unicode
//
if(!ret && GetLastError() == ERROR_NO_UNICODE_TRANSLATION)
{
lcFree(pszUnicodeBuffer);
return FALSE;
}
// other failure (same return)
if(!ret)
{
lcFree(pszUnicodeBuffer);
return FALSE;
}
// allocate the return ANSI/DBCS buffer
//
char *pszAnsiBuffer = (char *) lcMalloc(cUnicodeLen + 2);
if(!pszAnsiBuffer)
{
lcFree(pszUnicodeBuffer);
return FALSE;
}
BOOL bDefaultChar = FALSE, bExactMatch = FALSE;
// Convert back to ANSI/DBCS using default codepage
//
ret = WideCharToMultiByte(CP_ACP, 0, pszUnicodeBuffer, -1, pszAnsiBuffer, cUnicodeLen+2, ".", &bDefaultChar);
if(!strcmp(pszAnsiBuffer,pszString))
bExactMatch = TRUE;
// free our buffers
//
lcFree(pszAnsiBuffer);
lcFree(pszUnicodeBuffer);
// check if default character was used
//
if(!ret || bDefaultChar || !bExactMatch)
return FALSE;
// success!
//
return TRUE;
}
///////////////////////////////////////////////////////////////////////////////
// MUI support
//
// This AppendMenu wrapper will (under NT5) get a Unicode resource and
// then call AppendMenuW().
//
BOOL HxAppendMenu(HMENU hMenu, UINT uFlags, UINT uIDNewItem, LPCTSTR lpNewItem)
{
if(g_bWinNT5 && !uFlags && lpNewItem)
{
DWORD cp = CodePageFromLCID(MAKELCID(_Module.m_Language.GetUiLanguage(),SORT_DEFAULT));
DWORD dwSize = (DWORD)(sizeof(WCHAR) * strlen(lpNewItem)) + 4;
WCHAR *pwcString = (WCHAR *) lcMalloc(dwSize);
if(!pwcString)
return FALSE;
MultiByteToWideChar(cp, MB_PRECOMPOSED, lpNewItem, -1, pwcString, dwSize);
BOOL ret = AppendMenuW(hMenu, uFlags, uIDNewItem, pwcString);
lcFree(pwcString);
return ret;
}
else
{
return AppendMenu(hMenu, uFlags, uIDNewItem, lpNewItem);
}
}
///////////////////////////////////////////////////////////////////////////////
// MUI support
//
// This SetWindowText wrapper will (under NT5) convert the string to Unicode
// based on the MUI setting and then call SetWindowTextW().
//
BOOL HxSetWindowText(HWND hWnd, LPCTSTR lpString)
{
if(g_bWinNT5 && lpString)
{
DWORD cp = CodePageFromLCID(MAKELCID(_Module.m_Language.GetUiLanguage(),SORT_DEFAULT));
DWORD dwSize = (DWORD)(sizeof(WCHAR) * strlen(lpString)) + 4;
WCHAR *pwcString = (WCHAR *) lcMalloc(dwSize);
if(!pwcString)
return FALSE;
MultiByteToWideChar(cp, MB_PRECOMPOSED, lpString, -1, pwcString, dwSize);
BOOL ret = SetWindowTextW(hWnd, pwcString);
lcFree(pwcString);
return ret;
}
else
{
return SetWindowText(hWnd, lpString);
}
}