Easy way to tell if unicode character is supported in current font?
Asked Answered
Y

2

10

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.

Yoakum answered 14/12, 2015 at 23:13 Comment(5)
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.Sosthina
@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.Milks
@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.Sosthina
@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.Milks
@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.Sosthina
T
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.

Trudeau answered 14/12, 2015 at 23:22 Comment(10)
You can use std::vector instead of new/delete for a variable size buffer.Milks
@MarkRansom true dat.Trudeau
@Jonathan, what's the DC and how do I get a handle to it ? GetDC() ?Yoakum
@Yoakum Sorry I assumed you'd have that code already. Yes, GetDC() and then select the font in question into it.Trudeau
@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 ?Yoakum
@Yoakum 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).Trudeau
@Peter: Device Contexts is good introductory material.Trinhtrini
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.Yoakum
@JonathanPotter: Just curious how to adjust this for UTF-32 characters, for example, like '🔍'?Unmanly
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?Unmanly
Y
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 */ }
Yoakum answered 15/12, 2015 at 1:41 Comment(1)
You might want to consider break-ing from your loop upon finding the match for the Char.Unmanly

© 2022 - 2024 — McMap. All rights reserved.