Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include <Windows.h>
- #include <string>
- #include <usp10.h> //Uniscribe support
- #pragma comment(lib, "Usp10.lib")
- enum RES_YES_NO_ERR{
- RYNE_YES = 1,
- RYNE_NO = 0,
- RYNE_ERROR = -1,
- };
- RES_YES_NO_ERR isWindowTextRenderedCorrectly(HWND hWnd, const WCHAR* pText)
- {
- //Checks if the text within 'hWnd' window is rendered correctly
- //INFO: Some Unicode characters may not be supported by the font.
- //'hWnd' = window handle to use for rendering -- must be some simple window, like a button or a label. Do not use it for an edit control!
- //'pText' = pointer to text to check, or NULL to get it from the 'hWnd' window
- //RETURN:
- // RYNE_YES = if text is rendered correctly
- // RYNE_NO = if no, text is not rendered correctly
- // RYNE_ERROR = if error determining (check GetLastError() for info)
- RES_YES_NO_ERR res = RYNE_ERROR;
- int nOSError = 0;
- //SOURCE:
- // https://stackoverflow.com/questions/54050095/how-to-tell-if-a-surrogate-pair-unicode-character-is-supported-by-the-font
- //
- WCHAR* pTextBuff = NULL;
- int nLnTxt;
- if(hWnd &&
- ::IsWindow(hWnd))
- {
- //Get text?
- if(!pText)
- {
- //Get text from the window
- nLnTxt = ::GetWindowTextLength(hWnd);
- if(!nLnTxt)
- {
- nOSError = ::GetLastError();
- goto lbl_cleanup;
- }
- pTextBuff = new (std::nothrow) WCHAR[nLnTxt + 1];
- if(!pTextBuff)
- {
- nOSError = ERROR_OUTOFMEMORY;
- goto lbl_cleanup;
- }
- if(::GetWindowText(hWnd, pTextBuff, nLnTxt + 1) != nLnTxt)
- {
- nOSError = ::GetLastError();
- goto lbl_cleanup;
- }
- pTextBuff[nLnTxt] = 0; //Safety null
- pText = pTextBuff;
- }
- //We need to remove all whitespaces
- std::wstring strTxt;
- const WCHAR* pS = pText;
- for(;; pS++)
- {
- WCHAR z = *pS;
- if(!z)
- break;
- RES_YES_NO_ERR rzWs = isCharWhitespace(z);
- if(rzWs == RYNE_NO)
- {
- strTxt += z;
- }
- else if(rzWs != RYNE_YES)
- {
- //Error
- nOSError = ::GetLastError();
- goto lbl_cleanup;
- }
- }
- //We must have something to work with
- nLnTxt = (int)strTxt.size();
- if(nLnTxt != 0)
- {
- pText = strTxt.c_str();
- //Get font from the window
- HFONT hFont = (HFONT)::SendMessage(hWnd, WM_GETFONT, NULL, NULL);
- if(!hFont)
- {
- //You have some sketchy window here....
- ASSERT(NULL);
- hFont = (HFONT)::GetStockObject(DEFAULT_GUI_FONT);
- if(!hFont)
- hFont = (HFONT)::GetStockObject(SYSTEM_FONT);
- }
- if(hFont)
- {
- //Get the font that will be used to render text
- hFont = _getFallbackFont(pText, nLnTxt, hFont);
- if(hFont)
- {
- //Get DC from the window
- HDC hDC = ::GetDC(hWnd);
- if(hDC)
- {
- HRESULT hr;
- //Select our font
- HGDIOBJ hOldFont = ::SelectObject(hDC, hFont);
- //Find the characters not supported in this font
- //Note that some of them may be rendered as blanks
- //(thus we did not allow whitespaces in the string in the beginning of this function.)
- //Also make sure to check the code that you copy-and-pasted from StackOverflow!
- SCRIPT_CACHE sc = NULL;
- SCRIPT_FONTPROPERTIES fp = { sizeof(fp) };
- if(SUCCEEDED(hr = ::ScriptGetFontProperties(hDC, &sc, &fp)))
- {
- //Get glyph indices for the string
- GCP_RESULTS gcp_results = { sizeof(GCP_RESULTS) };
- gcp_results.nGlyphs = nLnTxt;
- WORD* pGlyphs = new (std::nothrow) WORD[nLnTxt + 1];
- if(pGlyphs)
- {
- memset(pGlyphs, 0, sizeof(WORD) * (nLnTxt + 1));
- gcp_results.lpGlyphs = (LPWSTR)pGlyphs;
- if(SUCCEEDED(hr = ::GetCharacterPlacement(hDC, pText, nLnTxt, 0, &gcp_results, GCP_GLYPHSHAPE)))
- {
- //At this poinst assume that we have no bad glyphs
- res = RYNE_YES;
- //Check resulting glyphs
- for(UINT i = 0; i < gcp_results.nGlyphs; i++)
- {
- WORD g = pGlyphs[i];
- //See if it's a blank or an invalid glyph
- if(g == fp.wgBlank ||
- g == fp.wgInvalid ||
- g == fp.wgDefault)
- {
- //Found one, no need to go any further
- res = RYNE_NO;
- break;
- }
- }
- }
- else
- nOSError = (int)hr;
- //Free mem
- delete[] pGlyphs;
- pGlyphs = NULL;
- }
- else
- nOSError = ERROR_NOT_ENOUGH_MEMORY;
- }
- else
- nOSError = (int)hr;
- //Free resources
- VERIFY(SUCCEEDED(hr = ::ScriptFreeCache(&sc)));
- ::SelectObject(hDC, hOldFont);
- VERIFY(::ReleaseDC(hWnd, hDC) == 1);
- }
- else
- nOSError = 1425;
- //Free font
- VERIFY(::DeleteObject(hFont));
- }
- else
- nOSError = ::GetLastError();
- }
- else
- nOSError = ERROR_UNABLE_TO_LOAD_MEDIUM;
- }
- else
- nOSError = ERROR_EMPTY;
- }
- else
- nOSError = ERROR_INVALID_HANDLE;
- lbl_cleanup:
- if(pTextBuff)
- {
- delete[] pTextBuff;
- pTextBuff = NULL;
- }
- ::SetLastError(nOSError);
- return res;
- }
- RES_YES_NO_ERR isCharWhitespace(WCHAR ch)
- {
- //RETURN:
- // RYNE_YES = if 'ch' is a whitespace
- // RYNE_NO = if 'ch' is not a whitespace
- // RYNE_ERROR = if error determining (check GetLastError() for info)
- WORD chType = 0;
- if(::GetStringTypeEx(LOCALE_USER_DEFAULT, CT_CTYPE1, &ch, 1, &chType))
- {
- return (chType & C1_SPACE) ? RYNE_YES : RYNE_NO;
- }
- return RYNE_ERROR;
- }
- int _enumMetafileFallbackFont(HDC, HANDLETABLE*, const ENHMETARECORD *record, int, LPARAM lParam)
- {
- ASSERT(record);
- if(record->iType == EMR_EXTCREATEFONTINDIRECTW)
- {
- LOGFONT** ppLF = (LOGFONT**)lParam;
- ASSERT(ppLF);
- //Should not be called repeatedly!
- if(!*ppLF)
- {
- LOGFONT* pLF = new (std::nothrow) LOGFONT;
- if(pLF)
- {
- *pLF = ((EMREXTCREATEFONTINDIRECTW*)record)->elfw.elfLogFont;
- *ppLF = pLF;
- }
- else
- ASSERT(NULL);
- }
- else
- ASSERT(NULL);
- }
- return TRUE;
- }
- HFONT _getFallbackFont(const WCHAR* pText, int nchLnText, HFONT hOriginalFont)
- {
- //Use metafile to find the fallback font for 'hOriginalFont'
- //'pText' = text to render with the font we're looking for the fallback font for
- //'nchLnText' = length of 'pText' in WCHARs
- //RETURN:
- // = Font handle (must be removed with DeleteObject() call!)
- // = NULL if error (check GetLastError() for info)
- ASSERT(hOriginalFont);
- ASSERT(pText);
- ASSERT(nchLnText != 0);
- HFONT hResFont = NULL;
- int nOSError = 0;
- HRESULT hr;
- HDC hMetafileDC = ::CreateEnhMetaFile(NULL, NULL, NULL, NULL);
- if(hMetafileDC)
- {
- HGDIOBJ hOldFont = ::SelectObject(hMetafileDC, hOriginalFont);
- //Render text into the metafile (specify an option for use of a fallback font)
- BOOL bRenderedOK = FALSE;
- SCRIPT_STRING_ANALYSIS ssa = NULL;
- hr = ::ScriptStringAnalyse(hMetafileDC, pText, nchLnText, 0, -1,
- SSA_METAFILE | SSA_FALLBACK | SSA_GLYPHS | SSA_LINK,
- 0, NULL, NULL, NULL, NULL, NULL, &ssa);
- if(SUCCEEDED(hr))
- {
- if(SUCCEEDED(hr = ::ScriptStringOut(ssa, 0, 0, 0, NULL, 0, 0, FALSE)))
- {
- //OK, got it
- bRenderedOK = TRUE;
- }
- else
- nOSError = (int)hr;
- }
- else
- nOSError = (int)hr;
- //Clean-up
- if(ssa)
- VERIFY(SUCCEEDED(hr = ::ScriptStringFree(&ssa)));
- ::SelectObject(hMetafileDC, hOldFont);
- //Get the meta-file
- HENHMETAFILE hMetaFile = ::CloseEnhMetaFile(hMetafileDC);
- if(hMetaFile)
- {
- //Only if we rendered correctly into the meta-file
- if(bRenderedOK)
- {
- //Get the font from the meta-file
- LOGFONT* pLF = NULL;
- if(::EnumEnhMetaFile(NULL, hMetaFile, _enumMetafileFallbackFont, &pLF, NULL))
- {
- if(pLF)
- {
- //And get a font handle
- hResFont = ::CreateFontIndirect(pLF);
- if(!hResFont)
- nOSError = ERROR_INVALID_DATA;
- //Free mem
- delete pLF;
- pLF = NULL;
- }
- else
- nOSError = 626;
- }
- else
- nOSError = 1781;
- }
- //Free resource
- VERIFY(::DeleteEnhMetaFile(hMetaFile));
- }
- else
- nOSError = ERROR_BAD_ARGUMENTS;
- }
- else
- nOSError = ERROR_METAFILE_NOT_SUPPORTED;
- ::SetLastError(nOSError);
- return hResFont;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement