9

I'm using Borland C++ Builder 2009 and I display the right and left pointing arrows like so:

Button2->Hint = L"Ctrl+\u2190" ;
Button3->Hint = L"Ctrl+\u2192" ; 

This works fine on Windows 7, the application uses font 'Segoe UI'.

On XP I get a square instead of the arrows, I use font 'Tahoma' on XP. In other words mentioned Unicode characters are not present in Tahoma on XP.

Is there an easy and fast way to simply check if the requested Unicode character is supported in the currently used font ? If so I could, for instance, replace the arrow with '>' or '<'. Not perfect, but good enough. I don't want to start changing fonts at this stage.

Your help appreciated.

Peter
  • 1,334
  • 12
  • 28
  • This is not related to a specific programming language, but the libraries. as these are language-specific, you should provide which language you actually use, C or C++. Note there is definitively not standard compliant way. – too honest for this site Dec 14 '15 at 23:18
  • 3
    @Olaf since a C solution could be used in C++ I don't think it's inappropriate to use both tags in this case. Not being in the standard is irrelevant since the question is clearly about a Windows specific solution, and the compiler is specified. – Mark Ransom Dec 14 '15 at 23:49
  • @MarkRansom: C is not C++. There are incompatible semantics and features C++ does not have. At least if you use standard C qwhich means C11 - or at least C99 (C11 mostly added features, but did not change the semantics). If your compiler does not follow the standard, it is broken or at least to be called outdated. Note you can very well use other compilers on Windows, too. – too honest for this site Dec 15 '15 at 03:10
  • @Olaf the Windows API is pure C, although there are C++ wrappers for it. *Most* C-compatible answers would also work in C++, including the one that currently has the checkmark. If the question were more about language features or standard libraries I'd be inclined to agree with you. – Mark Ransom Dec 15 '15 at 03:15
  • @MarkRansom: But it is not the other way around. So the C++ tag would be wrong. Still, it depends on how the code is compiled. If compiled as C, he cannot use C++ features. Heck, there should be a tag for the (smaller than many think) common subset of both languages. – too honest for this site Dec 15 '15 at 03:18

2 Answers2

8

You can use GetFontUnicodeRanges() to see which characters are supported by the font currently selected into the DC. Note that this API requires you to call it once to find out how big the buffer needs to be, and a second time to actually get the data.

DWORD dwSize = GetFontUnicodeRanges(hDC, nullptr);
BYTE* bBuffer = new BYTE[dwSize];
GLYPHSET* pGlyphSet = reinterpret_cast<GLYPHSET*>(bBuffer);
GetFontUnicodeRanges(hDC, pGlyphSet);
// use data in pGlyphSet, then free the buffer
delete[] bBuffer;

The GLYPHSET structure has a member array called ranges which lets you determine the range of characters supported by the font.

Jonathan Potter
  • 36,172
  • 4
  • 64
  • 79
  • 1
    You can use `std::vector` instead of `new/delete` for a variable size buffer. – Mark Ransom Dec 14 '15 at 23:51
  • @MarkRansom true dat. – Jonathan Potter Dec 14 '15 at 23:52
  • @Jonathan, what's the DC and how do I get a handle to it ? GetDC() ? – Peter Dec 14 '15 at 23:54
  • 1
    @Peter Sorry I assumed you'd have that code already. Yes, `GetDC()` and then select the font in question into it. – Jonathan Potter Dec 14 '15 at 23:55
  • @Jonathan, sorry for my ignorance, but ... select the font in it ? I'm sorry, I'm not with you on this one. In the context of C++ Builder, I suppose I can call `GetDC(Handle)`, Handle being the TForm's WinAPI handle. But then, how do I select a font in it ? – Peter Dec 15 '15 at 00:10
  • 1
    @Peter These are WinAPI concepts, if you're not familiar with them you'll need to do some research I'm afraid (or maybe someone who knows something about c++ builder can help). – Jonathan Potter Dec 15 '15 at 00:17
  • @Peter: [Device Contexts](https://msdn.microsoft.com/en-us/library/dd183553.aspx) is good introductory material. – IInspectable Dec 15 '15 at 00:59
  • 1
    `Canvas->Handle` is actually the hDC Handle that I need to get `GetFontUnicodeRanges()` to work properly ! I have working code now, confirmed on W7 and XP. Thanks. – Peter Dec 15 '15 at 01:32
  • @JonathanPotter: Just curious how to adjust this for UTF-32 characters, for example, like ''? – c00000fd Aug 17 '17 at 05:57
  • 1
    Also want to point out that `GetFontUnicodeRanges` didn't work for me on Windows 8.1 and Windows 10. It works for simple Unicode characters, but any new ones are not included by this API. For example, L'৳'. Additionally, a simpler API to do the same, i.e. `GetGlyphIndices`, doesn't seem to work either. So it would be nice if someone can post an updated way to resolve this? – c00000fd Mar 30 '18 at 04:19
3

Just for reference and the Google Gods:

bool UnicodeCharSupported(HWND Handle, wchar_t Char)
{
if (Handle)
    {
    DWORD dwSize = GetFontUnicodeRanges(Handle, NULL);
    if (dwSize)
        {
        bool Supported = false ;
        BYTE* bBuffer = new BYTE[dwSize];
        GLYPHSET* pGlyphSet = reinterpret_cast<GLYPHSET*>(bBuffer);
        if (GetFontUnicodeRanges(Handle, pGlyphSet))
            {
            for (DWORD x = 0 ; x < pGlyphSet->cRanges && !Supported ; x++)
                {
                Supported = (Char >= pGlyphSet->ranges[x].wcLow &&
                             Char < (pGlyphSet->ranges[x].wcLow + pGlyphSet->ranges[x].cGlyphs)) ;
                }
            }
        delete[] bBuffer;
        return Supported ;
        }
    }
return false ;
}

Example, relating to my Question:

if (!UnicodeCharSupported(Canvas->Handle, 0x2190))
    { /* Character not supported in current Font, use different character */ }
Peter
  • 1,334
  • 12
  • 28