3

I have an MFC application which is support multi-language. To support multi-language, I have developed an API that can calculate drawing width of a String(CString). It works perfectly for English language only. For other unicode language like Russian,Hindi,Arabic(RTL) etc. it cannot calculate exact width of a String. Here below is the API code:

CRect MyUtil::GetTextRect(LPCTSTR str, CRect* rect, UINT format, MyFontClass *textFont /*, BOOL getActualRect*/)
{
    if (str == NULL || _tcslen(str) == 0 || rect == NULL || rect->Width() <= 0 || rect->Height() <= 0 || textFont == NULL)
        return CRect(0, 0, 0, 0);

    CFont *font = textFont->GetCFont();

    HDC textHDC = ::GetDC(NULL);
    if (textHDC == NULL) 
        return CRect(0, 0, 0, 0);

    HFONT hfont = (HFONT)(font->GetSafeHandle());
    HFONT hOldFont = (HFONT)::SelectObject(textHDC, hfont);
    CRect textRect(rect->left, rect->top, rect->Width(), rect->Height());
    int result = ::DrawText(textHDC, str, -1, &textRect, format | DT_CALCRECT);
    ::SelectObject(textHDC, hOldFont);
    ::ReleaseDC(NULL, textHDC);

    CRect retRect(textRect.left, textRect.top, textRect.Width() + 1, textRect.Height() + 1);

    //if(getActualRect == FALSE)
    //{
    //  retRect.SetRect(retRect.left, retRect.top
    //      , retRect.Width() / textFont->GetDPIEnlargeRate(), retRect.Height() / textFont->GetDPIEnlargeRate());
    //}

    return result == 0 ? CRect(0, 0, 0, 0) : retRect;
 }

Here below is the calling method: Suppose I have a string ID named: "SOFTWARE_LICENSE" in *.resx file having text below:

Text for English:
<data name="SOFTWARE_LICENSE" xml:space="preserve">
    <value>Software Licence</value>
</data>

Text for Russian:
<data name="SOFTWARE_LICENSE" xml:space="preserve">
    <value>Лицензия программного обеспечения</value>
</data> 

Calling Method:

CString strSL = AfxGetStrRes(_T("SOFTWARE_LICENSE"));
MyFontClass txtFont14Regular = MyFontTemplate::CreateFont(_T("Segoe UI"),  -14, FW_NORMAL);
int textWidth = MyUtil::GetTextRect(strSL, &CRect(0, 0, 1000, 1000), DT_LEFT | DT_VCENTER | DT_SINGLELINE,txtFont14Regular).Width();

I need this text width to set the Size of the UI controls like button, Checkbox etc for multi-language (All UI controls are customized).

MyUtil::GetTextRect can calculate the width for English language only. For other language calculated width is not perfect, either large or too small.

Is there any way to calculate the accurate text width for Unicode String?

yeasir007
  • 2,110
  • 2
  • 28
  • 43
  • As in RonTLV's answer there are APIs for this that might be more accurate. I'd also try getting a DC for your own window rather than the screen in case there's anything different about that, but I can't think what that would be. And I assume the font is correct, and your copy of Segoe UI has all of the characters you're trying to measure? – Rup Sep 19 '19 at 08:09
  • @Rup, Actually I'm not sure about that "Segoe UI" does support/print other language like Russian or Hindi or Arabic. Is there any specific font that can support all language? – yeasir007 Sep 19 '19 at 08:27
  • Windows API use Unicode internally, they return accurate result for Unicode strings. GDI functions use font substitution and have no trouble with common languages (unless it's older Windows XP, I am not sure...). Your code is way too long. Just try these two lines for reproducible result: `dc.DrawText(string, &rect, format | DT_CALCRECT); dc.DrawText(string, &rect, format);` make sure `DT_SINGLELINE` is set – Barmak Shemirani Sep 19 '19 at 12:38
  • @BarmakShemirani I have to ensure specific FONT also like font name,size etc. By only calling "dc.DrawText(...)" is it possible to ensure specific font? – yeasir007 Sep 19 '19 at 13:05
  • Is your program compiled as Unicode? Or is it using ANSI code page for different languages? Can you run this code `::MessageBox(0,L"Лицензия",0,0)`? If it's not Unicode then you are not using Unicode strings. If your program is ANSI then the error you are getting is expected. – Barmak Shemirani Sep 19 '19 at 13:47
  • @BarmakShemirani My application is compiled as Unicode. I have different resx files for different language. I'm using String ID instead of text. Localization resource file has been loaded based on "DWORD dwLocale = GetUserDefaultUILanguage();" this API. And I'm debugging the code by changing system language. – yeasir007 Sep 19 '19 at 14:09
  • My Application can draw different language text for UI controls like button text, hyperlink button text, label text etc. Problem is text width is different for different language for the same String ID. so I have to set UI control size based on text width. So before set the UI control size i have to calculate the width of the String ID that's why i need to calculate this width. – yeasir007 Sep 19 '19 at 14:15
  • The problem must be some place else. Maybe you are calculating the width of an invalid string, then printing a different valid string. `resx` files are usually UTF8, maybe there is something wrong there. Try this code in your dialog paint routine: `dc.SelectObject(font); dc.DrawText(L"Лицензия", &rect, format | DT_SINGLELINE | DT_CALCRECT); dc.DrawText(L"Лицензия", &rect, DT_SINGLELINE | format);` It should print correctly. – Barmak Shemirani Sep 19 '19 at 14:43
  • @Barmak, Can you please tell me, why i've to call DrawText two times ? – yeasir007 Sep 22 '19 at 15:24
  • The first one doesn't draw text, it only calculates the rectangle. The second one will draw the text in that rectangle which was calculated – Barmak Shemirani Sep 23 '19 at 01:57

1 Answers1

3

This used to work for me:

CDC *pDC = CDC::FromHandle(textHDC);
CSize size(pDC->GetTextExtent(text));
// use size.cx, size.cy
RonTLV
  • 2,376
  • 2
  • 24
  • 38
  • +1 should use the APIs for this, but from a quick glance it looks like this just calls [GetTextExtentPoint32](https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-gettextextentpoint32w) – Rup Sep 19 '19 at 08:07
  • @RonTLV MyUtil::GetTextRect(.....) , pDC->GetTextExtent(text) and GetTextExtentPoint32(textHDC,strSL,strSL.GetLength(),&retSize) return the same value which is small for Russian Language. – yeasir007 Sep 19 '19 at 08:54