|
|
// 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); }
}
|