Advertisement
ahmd0

Surrogate pair Unicode character is supported by font

Jan 6th, 2019
134
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 8.05 KB | None | 0 0
  1. #include <Windows.h>
  2. #include <string>
  3. #include <usp10.h>                  //Uniscribe support
  4. #pragma comment(lib, "Usp10.lib")
  5.  
  6. enum RES_YES_NO_ERR{
  7.     RYNE_YES = 1,
  8.     RYNE_NO = 0,
  9.     RYNE_ERROR = -1,
  10. };
  11.  
  12.  
  13. RES_YES_NO_ERR isWindowTextRenderedCorrectly(HWND hWnd, const WCHAR* pText)
  14. {
  15.     //Checks if the text within 'hWnd' window is rendered correctly
  16.     //INFO: Some Unicode characters may not be supported by the font.
  17.     //'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!
  18.     //'pText' = pointer to text to check, or NULL to get it from the 'hWnd' window
  19.     //RETURN:
  20.     //      RYNE_YES    = if text is rendered correctly
  21.     //      RYNE_NO     = if no, text is not rendered correctly
  22.     //      RYNE_ERROR  = if error determining (check GetLastError() for info)
  23.     RES_YES_NO_ERR res = RYNE_ERROR;
  24.     int nOSError = 0;
  25.  
  26.     //SOURCE:
  27.     //      https://stackoverflow.com/questions/54050095/how-to-tell-if-a-surrogate-pair-unicode-character-is-supported-by-the-font
  28.     //
  29.  
  30.     WCHAR* pTextBuff = NULL;
  31.     int nLnTxt;
  32.  
  33.     if(hWnd &&
  34.         ::IsWindow(hWnd))
  35.     {
  36.         //Get text?
  37.         if(!pText)
  38.         {
  39.             //Get text from the window
  40.             nLnTxt = ::GetWindowTextLength(hWnd);
  41.             if(!nLnTxt)
  42.             {
  43.                 nOSError = ::GetLastError();
  44.                 goto lbl_cleanup;
  45.             }
  46.  
  47.             pTextBuff = new (std::nothrow) WCHAR[nLnTxt + 1];
  48.             if(!pTextBuff)
  49.             {
  50.                 nOSError = ERROR_OUTOFMEMORY;
  51.                 goto lbl_cleanup;
  52.             }
  53.  
  54.             if(::GetWindowText(hWnd, pTextBuff, nLnTxt + 1) != nLnTxt)
  55.             {
  56.                 nOSError = ::GetLastError();
  57.                 goto lbl_cleanup;
  58.             }
  59.  
  60.             pTextBuff[nLnTxt] = 0;      //Safety null
  61.  
  62.             pText = pTextBuff;
  63.         }
  64.  
  65.         //We need to remove all whitespaces
  66.         std::wstring strTxt;
  67.         const WCHAR* pS = pText;
  68.         for(;; pS++)
  69.         {
  70.             WCHAR z = *pS;
  71.             if(!z)
  72.                 break;
  73.  
  74.             RES_YES_NO_ERR rzWs = isCharWhitespace(z);
  75.             if(rzWs == RYNE_NO)
  76.             {
  77.                 strTxt += z;
  78.             }
  79.             else if(rzWs != RYNE_YES)
  80.             {
  81.                 //Error
  82.                 nOSError = ::GetLastError();
  83.                 goto lbl_cleanup;
  84.             }
  85.         }
  86.  
  87.         //We must have something to work with
  88.         nLnTxt = (int)strTxt.size();
  89.         if(nLnTxt != 0)
  90.         {
  91.             pText = strTxt.c_str();
  92.            
  93.             //Get font from the window
  94.             HFONT hFont = (HFONT)::SendMessage(hWnd, WM_GETFONT, NULL, NULL);
  95.             if(!hFont)
  96.             {
  97.                 //You have some sketchy window here....
  98.                 ASSERT(NULL);
  99.                 hFont = (HFONT)::GetStockObject(DEFAULT_GUI_FONT);
  100.                 if(!hFont)
  101.                     hFont = (HFONT)::GetStockObject(SYSTEM_FONT);
  102.             }
  103.  
  104.             if(hFont)
  105.             {
  106.  
  107.                 //Get the font that will be used to render text
  108.                 hFont = _getFallbackFont(pText, nLnTxt, hFont);
  109.                 if(hFont)
  110.                 {
  111.                     //Get DC from the window
  112.                     HDC hDC = ::GetDC(hWnd);
  113.                     if(hDC)
  114.                     {
  115.                         HRESULT hr;
  116.  
  117.                         //Select our font
  118.                         HGDIOBJ hOldFont = ::SelectObject(hDC, hFont);
  119.  
  120.                         //Find the characters not supported in this font
  121.                         //Note that some of them may be rendered as blanks
  122.                         //(thus we did not allow whitespaces in the string in the beginning of this function.)
  123.                         //Also make sure to check the code that you copy-and-pasted from StackOverflow!
  124.                         SCRIPT_CACHE sc = NULL;
  125.                         SCRIPT_FONTPROPERTIES fp = { sizeof(fp) };
  126.                         if(SUCCEEDED(hr = ::ScriptGetFontProperties(hDC, &sc, &fp)))
  127.                         {
  128.                             //Get glyph indices for the string
  129.                             GCP_RESULTS gcp_results = { sizeof(GCP_RESULTS) };
  130.                             gcp_results.nGlyphs = nLnTxt;
  131.  
  132.                             WORD* pGlyphs = new (std::nothrow) WORD[nLnTxt + 1];
  133.                             if(pGlyphs)
  134.                             {
  135.                                 memset(pGlyphs, 0, sizeof(WORD) * (nLnTxt + 1));
  136.                                 gcp_results.lpGlyphs = (LPWSTR)pGlyphs;
  137.  
  138.                                 if(SUCCEEDED(hr = ::GetCharacterPlacement(hDC, pText, nLnTxt, 0, &gcp_results, GCP_GLYPHSHAPE)))
  139.                                 {
  140.                                     //At this poinst assume that we have no bad glyphs
  141.                                     res = RYNE_YES;
  142.  
  143.                                     //Check resulting glyphs
  144.                                     for(UINT i = 0; i < gcp_results.nGlyphs; i++)
  145.                                     {
  146.                                         WORD g = pGlyphs[i];
  147.  
  148.                                         //See if it's a blank or an invalid glyph
  149.                                         if(g == fp.wgBlank ||
  150.                                             g == fp.wgInvalid ||
  151.                                             g == fp.wgDefault)
  152.                                         {
  153.                                             //Found one, no need to go any further
  154.                                             res = RYNE_NO;
  155.  
  156.                                             break;
  157.                                         }
  158.                                     }
  159.                                 }
  160.                                 else
  161.                                     nOSError = (int)hr;
  162.  
  163.                                 //Free mem
  164.                                 delete[] pGlyphs;
  165.                                 pGlyphs = NULL;
  166.                             }
  167.                             else
  168.                                 nOSError = ERROR_NOT_ENOUGH_MEMORY;
  169.                         }
  170.                         else
  171.                             nOSError = (int)hr;
  172.  
  173.                         //Free resources
  174.                         VERIFY(SUCCEEDED(hr = ::ScriptFreeCache(&sc)));
  175.                         ::SelectObject(hDC, hOldFont);
  176.                         VERIFY(::ReleaseDC(hWnd, hDC) == 1);
  177.                     }
  178.                     else
  179.                         nOSError = 1425;
  180.  
  181.                     //Free font
  182.                     VERIFY(::DeleteObject(hFont));
  183.                 }
  184.                 else
  185.                     nOSError = ::GetLastError();
  186.             }
  187.             else
  188.                 nOSError = ERROR_UNABLE_TO_LOAD_MEDIUM;
  189.         }
  190.         else
  191.             nOSError = ERROR_EMPTY;
  192.     }
  193.     else
  194.         nOSError = ERROR_INVALID_HANDLE;
  195.  
  196.  
  197. lbl_cleanup:
  198.  
  199.     if(pTextBuff)
  200.     {
  201.         delete[] pTextBuff;
  202.         pTextBuff = NULL;
  203.     }
  204.  
  205.     ::SetLastError(nOSError);
  206.     return res;
  207. }
  208.  
  209. RES_YES_NO_ERR isCharWhitespace(WCHAR ch)
  210. {
  211.     //RETURN:
  212.     //      RYNE_YES    = if 'ch' is a whitespace
  213.     //      RYNE_NO     = if 'ch' is not a whitespace
  214.     //      RYNE_ERROR  = if error determining (check GetLastError() for info)
  215.     WORD chType = 0;
  216.     if(::GetStringTypeEx(LOCALE_USER_DEFAULT, CT_CTYPE1, &ch, 1, &chType))
  217.     {
  218.         return (chType & C1_SPACE) ? RYNE_YES : RYNE_NO;
  219.     }
  220.  
  221.     return RYNE_ERROR;
  222. }
  223.  
  224. int _enumMetafileFallbackFont(HDC, HANDLETABLE*, const ENHMETARECORD *record, int, LPARAM lParam)
  225. {
  226.     ASSERT(record);
  227.     if(record->iType == EMR_EXTCREATEFONTINDIRECTW)
  228.     {
  229.         LOGFONT** ppLF = (LOGFONT**)lParam;
  230.         ASSERT(ppLF);
  231.  
  232.         //Should not be called repeatedly!
  233.         if(!*ppLF)
  234.         {
  235.             LOGFONT* pLF = new (std::nothrow) LOGFONT;
  236.             if(pLF)
  237.             {
  238.                 *pLF = ((EMREXTCREATEFONTINDIRECTW*)record)->elfw.elfLogFont;
  239.                 *ppLF = pLF;
  240.             }
  241.             else
  242.                 ASSERT(NULL);
  243.         }
  244.         else
  245.             ASSERT(NULL);
  246.     }
  247.  
  248.     return TRUE;
  249. }
  250.  
  251. HFONT _getFallbackFont(const WCHAR* pText, int nchLnText, HFONT hOriginalFont)
  252. {
  253.     //Use metafile to find the fallback font for 'hOriginalFont'
  254.     //'pText' = text to render with the font we're looking for the fallback font for
  255.     //'nchLnText' = length of 'pText' in WCHARs
  256.     //RETURN:
  257.     //      = Font handle (must be removed with DeleteObject() call!)
  258.     //      = NULL if error (check GetLastError() for info)
  259.     ASSERT(hOriginalFont);
  260.     ASSERT(pText);
  261.     ASSERT(nchLnText != 0);
  262.     HFONT hResFont = NULL;
  263.     int nOSError = 0;
  264.  
  265.     HRESULT hr;
  266.  
  267.     HDC hMetafileDC = ::CreateEnhMetaFile(NULL, NULL, NULL, NULL);
  268.     if(hMetafileDC)
  269.     {
  270.         HGDIOBJ hOldFont = ::SelectObject(hMetafileDC, hOriginalFont);
  271.  
  272.         //Render text into the metafile (specify an option for use of a fallback font)
  273.         BOOL bRenderedOK = FALSE;
  274.         SCRIPT_STRING_ANALYSIS ssa = NULL;
  275.         hr = ::ScriptStringAnalyse(hMetafileDC, pText, nchLnText, 0, -1,
  276.             SSA_METAFILE | SSA_FALLBACK | SSA_GLYPHS | SSA_LINK,
  277.             0, NULL, NULL, NULL, NULL, NULL, &ssa);
  278.         if(SUCCEEDED(hr))
  279.         {
  280.             if(SUCCEEDED(hr = ::ScriptStringOut(ssa, 0, 0, 0, NULL, 0, 0, FALSE)))
  281.             {
  282.                 //OK, got it
  283.                 bRenderedOK = TRUE;
  284.             }
  285.             else
  286.                 nOSError = (int)hr;
  287.         }
  288.         else
  289.             nOSError = (int)hr;
  290.  
  291.         //Clean-up
  292.         if(ssa)
  293.             VERIFY(SUCCEEDED(hr = ::ScriptStringFree(&ssa)));
  294.  
  295.         ::SelectObject(hMetafileDC, hOldFont);
  296.  
  297.         //Get the meta-file
  298.         HENHMETAFILE hMetaFile = ::CloseEnhMetaFile(hMetafileDC);
  299.         if(hMetaFile)
  300.         {
  301.             //Only if we rendered correctly into the meta-file
  302.             if(bRenderedOK)
  303.             {
  304.                 //Get the font from the meta-file
  305.                 LOGFONT* pLF = NULL;
  306.                 if(::EnumEnhMetaFile(NULL, hMetaFile, _enumMetafileFallbackFont, &pLF, NULL))
  307.                 {
  308.                     if(pLF)
  309.                     {
  310.                         //And get a font handle
  311.                         hResFont = ::CreateFontIndirect(pLF);
  312.                         if(!hResFont)
  313.                             nOSError = ERROR_INVALID_DATA;
  314.  
  315.                         //Free mem
  316.                         delete pLF;
  317.                         pLF = NULL;
  318.                     }
  319.                     else
  320.                         nOSError = 626;
  321.                 }
  322.                 else
  323.                     nOSError = 1781;
  324.             }
  325.  
  326.             //Free resource
  327.             VERIFY(::DeleteEnhMetaFile(hMetaFile));
  328.         }
  329.         else
  330.             nOSError = ERROR_BAD_ARGUMENTS;
  331.     }
  332.     else
  333.         nOSError = ERROR_METAFILE_NOT_SUPPORTED;
  334.  
  335.     ::SetLastError(nOSError);
  336.     return hResFont;
  337. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement