FT_GlyphSlot: load hinted + lsb_delta - rsb_delta xadvance differs with unhinted

takase1121 <[email protected]> Sun, 07 Jul 2024 14:22:00 +0000
Newsgroups gmane.comp.fonts.freetype.user
Message-ID <43UMCwjFZ826gAYM67Ks8ZFNB3R3ZqhxN0kW0O7mOc7MLSU41Snt3UGiCJFakKG8SAkj62nNoljtsleWLWYbMEzxEPaJl9jKmL2szicDFkM=@proton.me>
Hi,

According to the=C2=A0documentation on lsb and rsb_delta:

https://freetype.org/freetype2/docs/reference/ft2-glyph_retrieval.html#ft_g=
lyphslotrec

The fields are described as the "difference between hinted and unhinted lef=
t/right-side bearing", and the code underneath describes how to calculate t=
he xadvance based on the values. However, the xadvance I got from using FT_=
Load_Glyph with FT_LOAD_TARGET_LIGHT & lsb/rsb_delta is sometimes different=
 than FT_Load_Glyph with FT_LOAD_NO_HINTING, especially with whitespace.

I tested this with a simple C program that prints a message if the hinted a=
nd unhinted xadvances are different. I tested this program with JetBrains M=
ono and Fira Sans, and both fonts have differing xadvances on U+000D (CR), =
U+0020 (Space), U+00A0 (No-Break Space), U+2007 (Figure Space), U+2008 (Pun=
ctuation Space). I guess that the lsb_delta and rsb_delta are not set becau=
se there is nothing to hint (they're all whitespaces). I would like to conf=
irm if this is a bug with FreeType or is to be expected. Any help would be =
greatly appreciated.

The C program:

---

// compile with: gcc -o test -I/usr/include/freetype2 test.c -lfreetype
#include <ft2build.h>

#include FT_FREETYPE_H

int main(int argc, const char **argv) {
=C2=A0 =C2=A0 FT_Library lib;
=C2=A0 =C2=A0 FT_Face face;

=C2=A0 =C2=A0 if (argc !=3D 2) return 1;

=C2=A0 =C2=A0 if (FT_Init_FreeType(&lib) !=3D 0)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 return 1;

=C2=A0 =C2=A0 if (FT_New_Face(lib, argv[1], 0, &face) !=3D 0)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 return 1;

=C2=A0 =C2=A0 if (FT_Set_Pixel_Sizes(face, 0, 14) !=3D 0)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 return 1;

=C2=A0 =C2=A0 printf("Family: %s\n", face->family_name);

for (int i =3D 0; i < 0x10FFFF; i++) {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 if (FT_Load_Char(face, i, FT_LOAD_TARGET_LIGHT =
| FT_LOAD_FORCE_AUTOHINT) !=3D 0)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return 1;

=C2=A0 =C2=A0 =C2=A0 =C2=A0 double hinted_xadv =3D (face->glyph->advance.x =
+ (face->glyph->lsb_delta - face->glyph->rsb_delta)) / 64.0f;

double bearing_delta =3D (face->glyph->lsb_delta - face->glyph->rsb_delta) =
/ 64.0f;


=C2=A0 =C2=A0 =C2=A0 =C2=A0 if (FT_Load_Char(face, i, FT_LOAD_BITMAP_METRIC=
S_ONLY | FT_LOAD_NO_HINTING) !=3D 0)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return 1;

=C2=A0 =C2=A0 =C2=A0 =C2=A0 double unhinted_xadv =3D (face->glyph->advance.=
x) / 64.0f;


=C2=A0 =C2=A0 =C2=A0 =C2=A0 if (hinted_xadv !=3D unhinted_xadv) {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 printf("different advances (U+%04=
X): %lf hinted, %lf unhinted\n", i, hinted_xadv, unhinted_xadv);
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 printf("lsb_delta - rsb_delta: %f=
\n", bearing_delta);
=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
=C2=A0 =C2=A0 }
=C2=A0 =C2=A0 printf("\n\n");

=C2=A0 =C2=A0 return 0;
}

---


Sample results:

---

Family: Fira Sansdifferent advances (U+000D): 4.000000 hinted, 3.703125 unh=
inted
lsb_delta - rsb_delta: 0.000000
different advances (U+0020): 4.000000 hinted, 3.703125 unhinted
lsb_delta - rsb_delta: 0.000000
different advances (U+00A0): 4.000000 hinted, 3.703125 unhinted
lsb_delta - rsb_delta: 0.000000
different advances (U+2007): 8.000000 hinted, 7.843750 unhinted
lsb_delta - rsb_delta: 0.000000
different advances (U+2008): 4.000000 hinted, 3.703125 unhinted
lsb_delta - rsb_delta: 0.000000

---

JetBrains Mono: https://www.jetbrains.com/lp/mono/
Fira Sans: https://fonts.google.com/specimen/Fira+Sans

Thanks,
Kelvin