Bug in Windows Font API Wrapper


#1

Please take a glimpse at this, Jules. Thanks for your attention in advance.

While the Font class is woeing again (Font woes again (chinese)), I found myself stuck in a worse situation of not being able to display a subset of CJK characters even with a pan-CJK font loaded (e.g. Noto Sans CJK).

Here’s the issue, which I believe is a bug and have somehow traced it down it in the JUCE library source code. In my application’s initialise() function, there’s a snippet that loads NotoSansCJKjp-Regular.otf into memory and sets it to be the default typeface. Despite its name being “Noto Sans CJK JP”, the font really has all Chinese, Japanese and Korean glyphs in it.

    MemoryBlock fontData;
    if(fontFile.loadFileAsData(fontData)) {
      Typeface::Ptr fontTF = Typeface::createSystemTypefaceFor(
        fontData.getData(), fontData.getSize());
      if(fontTF != nullptr) {
        visuals.setDefaultSansSerifTypeface(fontTF);
        externalFontLoaded = true;
      }
    }

It has been verified that this program displays CJK characters without a glitch on Linux, but on Windows some Chinese (both traditional and simplified) characters (e.g. 雨 and the full-width comma ,) become full-width spaces.

With some couting I found that all these missing characters are above U+8000 and Font::getGlyphPositions is giving me negative glyph numbers, which looks super fishy. After spending a while debugging the negative number is traced back to juce_graphics/native/juce_win32_Fonts.cpp: in WindowsTypeface::getStringWidth and WindowsTypeface::getGlyphPositions, two HeapBlock objects are assigned int16 type instead of uin16, which causes all > 0x8000 unicodes to be folded back to negative values upon access; the negative values are expanded to int type when returned to the Array<int>& resultGlyphs argument.

I don’t know what happens afterwards - do those negative values somehow recover when converted back to wchar? Seems not to be the case. At least the missing glyph issue appears to be solved when I replace int16 with uint16. I’m writing this post to confirm that this indeed is a bug and what I did was indeed the right way to fix it.

Btw, it still will be great if the font fallback mechanism can work again on Windows.


#2

Thank you for the detailed bug report. I’ll get this fixed…


#3

OK this is now fixed on develop with commit 269c1f2.


#4

Hi,

Wouldn’t it make sense to use this opportunity to extend it to uint32, to allow the full possible glyph range instead of limiting it to the first 65536 glyphs? Or is 65536 an upper limit that can never be breached?

Regards,
Mike


#5

Hello reFX,

AFAIK TrueType and OpenType fonts only support up to 65535 glyphs. I don’t think uint32 will be much helpful in this case without having some functioning font fallback mechanism as the premise. Slowing down text drawing is bad, but needing to embed a 15 MB font into a 5 MB program is also bad (maybe worse). I wish there could be a macro for swapping all drawText and drawFittedText calls with TextLayout::draw.

Regards,
Kanru


#6

Exactly, OpenType and TrueType only have 65535 glyphs.