#include "pch.hxx" #include "shlwapi.h" #include "fontcash.h" #include "strconst.h" #include "inetreg.h" #include "oleutil.h" #include "msoedbg.h" #include #include "demand.h" HRESULT CreateFontCacheEntry(FONTCACHEENTRY **ppNewEntry) { HRESULT hr = S_OK; FONTCACHEENTRY* pNew; if (!MemAlloc((LPVOID *)&pNew, sizeof(FONTCACHEENTRY))) hr = E_OUTOFMEMORY; else { pNew->uiCodePage = 0; pNew->szFaceName[0] = TCHAR(0); for (int i = 0; i < FNT_SYS_LAST; i++) pNew->rgFonts[i] = 0; } *ppNewEntry = pNew; return S_OK; } void FreeFontsInEntry(FONTCACHEENTRY *pEntry) { for (int i = 0; i < FNT_SYS_LAST; i++) if (pEntry->rgFonts[i]) { DeleteObject(pEntry->rgFonts[i]); pEntry->rgFonts[i] = 0; } } HRESULT FreeFontCacheEntry(FONTCACHEENTRY *pEntry) { Assert(pEntry); FreeFontsInEntry(pEntry); MemFree(pEntry); return S_OK; } // ================================================================================= // Font Cache Implementation // ================================================================================= CFontCache::CFontCache(IUnknown *pUnkOuter) : CPrivateUnknown(pUnkOuter), m_pAdviseRegistry(NULL), m_pFontEntries(NULL), m_pSysCacheEntry(NULL), m_bISO_2022_JP_ESC_SIO_Control(false), m_uiSystemCodePage(0) { InitializeCriticalSection(&m_rFontCritSect); InitializeCriticalSection(&m_rAdviseCritSect); } //*************************************************** CFontCache::~CFontCache() { if (m_pAdviseRegistry) m_pAdviseRegistry->Release(); if (m_pFontEntries) m_pFontEntries->Release(); if (m_pSysCacheEntry) FreeFontCacheEntry(m_pSysCacheEntry); DeleteCriticalSection(&m_rFontCritSect); DeleteCriticalSection(&m_rAdviseCritSect); } //*************************************************** HRESULT CFontCache::InitSysFontEntry() { // Locals NONCLIENTMETRICS ncm; CHARSETINFO rCharsetInfo={0}; UINT nACP; HRESULT hr = S_OK; LOGFONT rSysLogFonts; Assert(m_pSysCacheEntry); // Get system ansi code page nACP = GetACP(); m_pSysCacheEntry->uiCodePage = nACP; m_uiSystemCodePage = nACP; // Get the charset for the current ANSI code page TranslateCharsetInfo((DWORD *)IntToPtr(MAKELONG(nACP, 0)), &rCharsetInfo, TCI_SRCCODEPAGE); // Get icon font metrics if (SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(LOGFONT), &rSysLogFonts, 0)) { StrCpyN(m_pSysCacheEntry->szFaceName, rSysLogFonts.lfFaceName, ARRAYSIZE(m_pSysCacheEntry->szFaceName)); // Reset lfCharset depending on the current ansi code page rSysLogFonts.lfCharSet = (BYTE) rCharsetInfo.ciCharset; //$HACK - This code is necessary to work around a bug in Windows. // If the icon font has never been changed from the default, // SystemParametersInfo returns the wrong height. We need // to select the font into a DC and get the textmetrics to // determine the correct height. (EricAn) HFONT hFont; if (hFont = CreateFontIndirect(&rSysLogFonts)) { HDC hdc; if (hdc = GetDC(NULL)) { TEXTMETRIC tm; HFONT hFontOld = SelectFont(hdc, hFont); GetTextMetrics(hdc, &tm); rSysLogFonts.lfHeight = -(tm.tmHeight - tm.tmInternalLeading); SelectFont(hdc, hFontOld); ReleaseDC(NULL, hdc); } DeleteObject(hFont); } if (m_pSysCacheEntry->rgFonts[FNT_SYS_ICON] == 0) m_pSysCacheEntry->rgFonts[FNT_SYS_ICON] = CreateFontIndirect(&rSysLogFonts); // Bold Icon Font if (m_pSysCacheEntry->rgFonts[FNT_SYS_ICON_BOLD] == 0) { LONG lOldWeight = rSysLogFonts.lfWeight; rSysLogFonts.lfWeight = (rSysLogFonts.lfWeight < 700) ? 700 : 1000; m_pSysCacheEntry->rgFonts[FNT_SYS_ICON_BOLD] = CreateFontIndirect(&rSysLogFonts); rSysLogFonts.lfWeight = lOldWeight; } if (m_pSysCacheEntry->rgFonts[FNT_SYS_ICON_STRIKEOUT] == 0) { rSysLogFonts.lfStrikeOut = TRUE; m_pSysCacheEntry->rgFonts[FNT_SYS_ICON_STRIKEOUT] = CreateFontIndirect(&rSysLogFonts); } } else { AssertSz (FALSE, "SystemParametersInfo (SPI_GETICONTITLELOGFONT) - Failed ---."); hr = E_FAIL; goto Exit; } if (m_pSysCacheEntry->rgFonts[FNT_SYS_MENU] == 0) { #ifndef WIN16 // WIN16FF - SPI_GETNONCLIENTMETRICS // Prepare to get icon metrics ncm.cbSize = sizeof(ncm); // Get system menu font if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0)) { CopyMemory((LPBYTE)&rSysLogFonts, (LPBYTE)&ncm.lfMenuFont, sizeof(LOGFONT)); m_pSysCacheEntry->rgFonts[FNT_SYS_MENU] = CreateFontIndirect(&rSysLogFonts); } else { AssertSz (FALSE, "SystemParametersInfo (SPI_GETNONCLIENTMETRICS) - Failed ---."); hr = E_FAIL; goto Exit; } #else m_pSysCacheEntry->rgFonts[FNT_SYS_MENU] = m_pSysCacheEntry->rgFonts[FNT_SYS_ICON]; #endif } Exit: return hr; } //*************************************************** HRESULT CFontCache::GetSysFont(FNTSYSTYPE fntType, HFONT *phFont) { // check params Assert(fntType < FNT_SYS_LAST); Assert(m_pSysCacheEntry); EnterCriticalSection(&m_rFontCritSect); // System Font if (m_pSysCacheEntry->rgFonts[fntType] == NULL) // This call might fail, but we can return NULL fonts, so is OK (void)InitSysFontEntry(); // Done *phFont = m_pSysCacheEntry->rgFonts[fntType]; LeaveCriticalSection(&m_rFontCritSect); return ((*phFont) ? S_OK : E_FAIL); } //*************************************************** HRESULT CFontCache::FreeResources() { m_pFontEntries->ClearList(); FreeFontsInEntry(m_pSysCacheEntry); return S_OK; } //*************************************************** HRESULT CFontCache::InitResources() { DWORD dummyCookie = 0; #ifdef DEBUG DWORD cCount; m_pFontEntries->GetCount(&cCount); Assert(cCount == 0); #endif HRESULT hr = InitSysFontEntry(); return hr; } //*************************************************** void CFontCache::SendPostChangeNotifications() { DWORD cookie = 0; IFontCacheNotify* pCurr; IUnknown* pTempCurr; while(SUCCEEDED(m_pAdviseRegistry->GetNext(LD_FORWARD, &pTempCurr, &cookie))) { if (pTempCurr->QueryInterface(IID_IFontCacheNotify, (LPVOID *)&pCurr)==S_OK) { pCurr->OnPostFontChange(); pCurr->Release(); } pTempCurr->Release(); } } //*************************************************** void CFontCache::SendPreChangeNotifications() { DWORD cookie = 0; IFontCacheNotify* pCurr; IUnknown* pTempCurr; while(SUCCEEDED(m_pAdviseRegistry->GetNext(LD_FORWARD, &pTempCurr, &cookie))) { if (pTempCurr->QueryInterface(IID_IFontCacheNotify, (LPVOID *)&pCurr)==S_OK) { pCurr->OnPreFontChange(); pCurr->Release(); } pTempCurr->Release(); } } //************************************ // IFontCache interface implementation //************************************ HRESULT CFontCache::Init(HKEY hkey, LPCSTR pszIntlKey, DWORD dwFlags) { HRESULT hr; DWORD dummyCookie, dw, cb; HKEY hTopkey; if (m_pSysCacheEntry) return E_UNEXPECTED; EnterCriticalSection(&m_rFontCritSect); EnterCriticalSection(&m_rAdviseCritSect); hr = IUnknownList_CreateInstance(&m_pAdviseRegistry); if (FAILED(hr)) goto Exit; hr = m_pAdviseRegistry->Init(NULL, 0, 0); if (FAILED(hr)) goto Exit; hr = IVoidPtrList_CreateInstance(&m_pFontEntries); if (FAILED(hr)) goto Exit; hr = m_pFontEntries->Init(NULL, 0, (IVPL_FREEITEMFUNCTYPE)(&FreeFontCacheEntry), 0); if (FAILED(hr)) goto Exit; hr = CreateFontCacheEntry(&m_pSysCacheEntry); if (FAILED(hr)) goto Exit; hr = InitResources(); if (FAILED(hr)) goto Exit; m_hkey = hkey; StrCpyN(m_szIntlKeyPath, pszIntlKey, ARRAYSIZE(m_szIntlKeyPath)); if (RegOpenKeyEx(m_hkey, m_szIntlKeyPath, NULL, KEY_READ, &hTopkey) == ERROR_SUCCESS) { cb = sizeof(dw); if (RegQueryValueEx(hTopkey, c_szISO2022JPControl, NULL, NULL, (LPBYTE)&dw, &cb) == ERROR_SUCCESS) m_bISO_2022_JP_ESC_SIO_Control = (BOOL) dw; else m_bISO_2022_JP_ESC_SIO_Control = false; RegCloseKey(hTopkey); } else m_bISO_2022_JP_ESC_SIO_Control = false; Exit: LeaveCriticalSection(&m_rAdviseCritSect); LeaveCriticalSection(&m_rFontCritSect); return hr; } //*************************************************** HRESULT CFontCache::GetFont(FNTSYSTYPE fntType, HCHARSET hCharset, HFONT *phFont) { INETCSETINFO CsetInfo; UINT uiCodePage = 0; FONTCACHEENTRY *pCurrEntry = NULL; DWORD cookie = 0; // check params Assert(fntType < FNT_SYS_LAST); Assert(m_pSysCacheEntry); if (hCharset == NULL) return GetSysFont(fntType, phFont); *phFont = 0; /* get CodePage from HCHARSET */ MimeOleGetCharsetInfo(hCharset,&CsetInfo); uiCodePage = (CP_JAUTODETECT == CsetInfo.cpiWindows) ? 932 : CsetInfo.cpiWindows; if ( uiCodePage == CP_KAUTODETECT ) uiCodePage = 949 ; // Don't want to duplicate the system codepage in the list. if (m_pSysCacheEntry && (uiCodePage == m_uiSystemCodePage)) return GetSysFont(fntType, phFont); EnterCriticalSection(&m_rFontCritSect); // Check to see if code page is in cache while (SUCCEEDED(m_pFontEntries->GetNext(LD_FORWARD, (LPVOID *)&pCurrEntry, &cookie))) if (pCurrEntry->uiCodePage == uiCodePage) break; // If code page not in cache, add it if (NULL == pCurrEntry) { if (FAILED(CreateFontCacheEntry(&pCurrEntry))) goto ErrorExit; if (FAILED(m_pFontEntries->AddItem(pCurrEntry, &cookie))) goto ErrorExit; pCurrEntry->uiCodePage = uiCodePage; } // See if desired font is available for code page. If not, create code page if (0 == pCurrEntry->rgFonts[fntType]) { // Locals LOGFONT lf; TCHAR szFaceName[LF_FACESIZE] = { TCHAR(0) } ; BYTE bGDICharset; // Get logfont for charset if (0 == GetObject(m_pSysCacheEntry->rgFonts[fntType], sizeof (LOGFONT), &lf)) goto ErrorExit; if (FAILED(SetGDIAndFaceNameInLF(uiCodePage, CsetInfo.cpiWindows, &lf))) goto ErrorExit; // Create the font if ((CP_UNICODE == uiCodePage) || IsValidCodePage(uiCodePage)) pCurrEntry->rgFonts[fntType] = CreateFontIndirect(&lf); else goto ErrorExit; } *phFont = pCurrEntry->rgFonts[fntType]; LeaveCriticalSection(&m_rFontCritSect); return S_OK; ErrorExit: LeaveCriticalSection(&m_rFontCritSect); return GetSysFont(fntType, phFont); } //*************************************************** HRESULT CFontCache::OnOptionChange() { HRESULT hr; EnterCriticalSection(&m_rAdviseCritSect); SendPreChangeNotifications(); EnterCriticalSection(&m_rFontCritSect); FreeResources(); // Even if this fails, still need to send notifications InitResources(); LeaveCriticalSection(&m_rFontCritSect); SendPostChangeNotifications(); LeaveCriticalSection(&m_rAdviseCritSect); return S_OK; } //*************************************************** HRESULT CFontCache::GetJP_ISOControl(BOOL *pfUseSIO) { // 0 means use ESC, 1 means use SIO *pfUseSIO = m_bISO_2022_JP_ESC_SIO_Control; return S_OK; } //****************************************** // IConnectionPoint interface implementation // // The only functions we care about right now // are the Advise and Unadvise functions. The // others return E_NOTIMPL //****************************************** HRESULT CFontCache::Advise(IUnknown *pUnkSink, DWORD *pdwCookie) { EnterCriticalSection(&m_rAdviseCritSect); HRESULT hr = m_pAdviseRegistry->AddItem(pUnkSink, pdwCookie); LeaveCriticalSection(&m_rAdviseCritSect); return hr; } //*************************************************** HRESULT CFontCache::Unadvise(DWORD dwCookie) { EnterCriticalSection(&m_rAdviseCritSect); HRESULT hr = m_pAdviseRegistry->RemoveItem(dwCookie); LeaveCriticalSection(&m_rAdviseCritSect); return hr; } //*************************************************** HRESULT CFontCache::GetConnectionInterface(IID *pIID) { return E_NOTIMPL; } //*************************************************** HRESULT CFontCache::GetConnectionPointContainer(IConnectionPointContainer **ppCPC) { *ppCPC = NULL; return E_NOTIMPL; } //*************************************************** HRESULT CFontCache::EnumConnections(IEnumConnections **ppEnum) { *ppEnum = NULL; return E_NOTIMPL; } //*************************************************** HRESULT CFontCache::PrivateQueryInterface(REFIID riid, LPVOID *lplpObj) { TraceCall("CFontCache::PrivateQueryInterface"); if(!lplpObj) return E_INVALIDARG; *lplpObj = NULL; if (IsEqualIID(riid, IID_IFontCache)) *lplpObj = (LPVOID)(IFontCache *)this; else if (IsEqualIID(riid, IID_IConnectionPoint)) *lplpObj = (LPVOID)(IConnectionPoint *)this; else { *lplpObj = NULL; return E_NOINTERFACE; } AddRef(); return NOERROR; } //*************************************************** // szFaceName is assumed to be from LOGFONT->lfFaceName HRESULT CFontCache::SetFaceNameFromReg(UINT uiCodePage, LPTSTR szFaceName, DWORD cchFaceName) { HKEY hkey, hTopkey; DWORD cb, dw, i = 0; TCHAR szName[LF_FACESIZE]; szFaceName[0] = TCHAR(0); if (RegOpenKeyEx(m_hkey, m_szIntlKeyPath, NULL, KEY_READ, &hTopkey) == ERROR_SUCCESS) { cb = sizeof(szName); while (ERROR_NO_MORE_ITEMS != RegEnumKeyEx(hTopkey, i++, szName, &cb, 0, NULL, NULL, NULL)) { UINT uiTempCodePage = StrToInt(szName); if (uiTempCodePage == uiCodePage) { if (RegOpenKeyEx(hTopkey, szName, NULL, KEY_READ, &hkey) == ERROR_SUCCESS) { cb = sizeof(TCHAR)*cchFaceName; RegQueryValueEx(hkey, REGSTR_VAL_PROP_FONT, NULL, NULL, (LPBYTE)szFaceName, &cb); RegCloseKey(hkey); break; } } cb = sizeof(szName); } RegCloseKey(hTopkey); } if (TCHAR(0) == szFaceName[0]) return E_FAIL; return S_OK; } // ================================================================================= // EnumFontFamExProc // ================================================================================= INT CALLBACK EnumFontFamExProc (ENUMLOGFONTEX *lpelfe, NEWTEXTMETRICEX *lpntme, INT FontType, LPARAM lParam) { // Check Param Assert (lpelfe && lpntme && lParam); // NOTE: Assuming size of buffer is LF_FACESIZE. If it changes below, change // here also. StrCpyN((LPTSTR)lParam, lpelfe->elfLogFont.lfFaceName, LF_FACESIZE); // End the enumeration by return 0 return 0; } //*************************************************** // szFaceName is assumed to be from LOGFONT->lfFaceName HRESULT CFontCache::SetFaceNameFromGDI(BYTE bGDICharSet, LPTSTR szFaceName, DWORD cchFaceName) { HDC hdc; LOGFONT rSysLogFont; // I know these charsets support Arial if (bGDICharSet == ANSI_CHARSET || bGDICharSet == EASTEUROPE_CHARSET || bGDICharSet == RUSSIAN_CHARSET || bGDICharSet == BALTIC_CHARSET || bGDICharSet == GREEK_CHARSET || bGDICharSet == TURKISH_CHARSET) { StrCpyN(szFaceName, TEXT("Arial"), cchFaceName); goto Exit; } if (0 == GetObject(m_pSysCacheEntry->rgFonts[FNT_SYS_ICON], sizeof (LOGFONT), &rSysLogFont)) { StrCpyN(szFaceName, rSysLogFont.lfFaceName, cchFaceName); if (TCHAR(0) != szFaceName[0]) goto Exit; } // Get an hdc from the hwnd hdc = GetDC (NULL); TCHAR szName[LF_FACESIZE]; // Note: If we change size, change callback above. // EnumFontFamilies EnumFontFamiliesEx(hdc, &rSysLogFont, (FONTENUMPROC)EnumFontFamExProc, (LPARAM)szName, 0); StrCpyN(szFaceName, szName, cchFaceName); // Done ReleaseDC (NULL, hdc); Exit: return (0 != *szFaceName) ? S_OK : E_FAIL; } //*************************************************** // szFaceName is assumed to be from LOGFONT->lfFaceName HRESULT CFontCache::SetFaceNameFromCPID(UINT cpID, LPTSTR szFaceName, DWORD cchFaceName) { CODEPAGEINFO CodePageInfo ; /* get CodePageInfo from HCHARSET */ MimeOleGetCodePageInfo(cpID,&CodePageInfo); if ( CodePageInfo.szVariableFont[0] != '\0' ) StrCpyN(szFaceName, CodePageInfo.szVariableFont, cchFaceName); else StrCpyN(szFaceName, CodePageInfo.szFixedFont, cchFaceName); if (szFaceName[0] == '\0') return E_FAIL; return S_OK; } //*************************************************** HRESULT CFontCache::SetGDIAndFaceNameInLF(UINT uiCodePage, CODEPAGEID cpID, LOGFONT *lpLF) { HRESULT hr = S_OK; BOOL fDoLastChance = false; CHARSETINFO rCharsetInfo; if (FAILED(SetFaceNameFromReg(uiCodePage, lpLF->lfFaceName, ARRAYSIZE(lpLF->lfFaceName)))) if (FAILED(SetFaceNameFromCPID(cpID, lpLF->lfFaceName, ARRAYSIZE(lpLF->lfFaceName)))) fDoLastChance = true; if ( TranslateCharsetInfo((LPDWORD) IntToPtr(uiCodePage), &rCharsetInfo, TCI_SRCCODEPAGE)) lpLF->lfCharSet = (BYTE) rCharsetInfo.ciCharset; else lpLF->lfCharSet = DEFAULT_CHARSET; if (fDoLastChance) hr = SetFaceNameFromGDI(lpLF->lfCharSet, lpLF->lfFaceName, ARRAYSIZE(lpLF->lfFaceName)); return hr; } //*************************************************** HRESULT CFontCache::CreateInstance(IUnknown* pUnkOuter, IUnknown** ppUnknown) { // Invalid Arg Assert(ppUnknown); // Initialize *ppUnknown = NULL; // Create me CFontCache *pNew = new CFontCache(pUnkOuter); if (NULL == pNew) return (E_OUTOFMEMORY); // Cast to unknown *ppUnknown = (IFontCache*)pNew; // Done return S_OK; }