text-renderingharfbuzz

Correct combining characters positions with Harfbuzz


I am trying to render text with Harfbuzz and a signed distance field atlas.

The code is basically this:

void drawText(const std::wstring &str, Vec2 pos)
{
    // Init harfbuzz
    hb_buffer_t *hbBuf = hb_buffer_create();
    hb_buffer_set_direction(hbBuf, HB_DIRECTION_LTR);
    hb_buffer_set_script(hbBuf, HB_SCRIPT_LATIN);
    hb_buffer_set_language(hbBuf, hb_language_from_string("en", 2));

    // Process string
    hb_buffer_add_utf32(hbBuf, reinterpret_cast<const uint32_t*>(str.c_str()), -1, 0, -1);
    hb_shape(font.hb, hbBuf, nullptr, 0);

    // Display string
    unsigned int nbGlyphs;
    hb_glyph_info_t *glyphInfos = hb_buffer_get_glyph_infos(hbBuf, &nbGlyphs);
    hb_glyph_position_t *glyphPos = hb_buffer_get_glyph_positions(hbBuf, &nbGlyphs);

    for(unsigned int i = 0; i < nbGlyphs; i++)
    {
        Vec2 drawPos = pos + Vec2(glyphPos[i].x_offset, glyphPos[i].y_offset) / 64.f;
        drawGlyph(glyphInfos[i].codepoint, drawPos);
        pos.x += glyphPos[i].x_advance / 64.f;
        pos.y += glyphPos[i].y_advance / 64.f;
    }
}

The text looks correctly shaped for an English phrase, but when I test it with diacritics, they look misplaced. I am testing it with aâa aâ̈a bb̂b bb̂̈b bb̧b bb͜b bb︠︡b. The Unicode string does not contain precombined characters. Harfbuzz uses the precombined character â, which makes this one look good. Most other diacritics are off.

Text with diacritics on the left of where they should be

When I multiply x_offset by 0.5, the combining characters are better placed. The accents and the cedilla are at the right x position. The accents do not stack and are too low on the b. The arc under BBB (U+035C) should join the two last letters instead of being centered on the 2nd b.

I also tried with U+FE20 and U+FE21 on the previous group of b. In my tests, U+FE21 is on the 2nd b, but it looks like it should be on the 3rd.

Test with glyphPos[i].x_offset * 0.5f, better but still wrong

I tried with several fonts, but of those fonts, only NotoSansDisplay-Regular.ttf had combining characters. I did not manage to make a program display that string as expected on my Debian system (testing, with HarfBuzz 2.6.4-1).

With Windows, I got better results. Here is what I expect: the accents are stacked, the combining double breve below it at the right place, the cedilla is off.

Text rendering closer to what I expect

Am I doing something wrong with HarfBuzz, or I am testing to niche cases that HarfBuzz does support yet?

EDIT:

The actual problem was not described above. I loaded a font with FreeType FT_New_Face then created a hb_font_t with hb_ft_font_create. For every string drawn, I called FT_Set_Pixel_Sizes but kept that hb_font_t.


Solution

  • You should try shaping the same text and font with hb-view / hb-shape. That would help you narrow down where the problem is. I'm making a wild guess that the problem is in how / whether you are accounting for glyph origin in your atlas.