Seperate freetype face and named instance indices (#39213)

In servo, each `LocalFontIdentifier` has an `fn index() -> u32`, which
returns the index of the font within the font file in case it is a font
collection (`.ttc` instead of `.ttf`). The way this index is obtained is
platform-dependent.

On systems using `fontconfig`, we get the index by querying `FC_INDEX`:


d2c78db981/components/fonts/platform/freetype/font_list.rs (L109-L112)

There is a sneaky bug here: In addition to the aforementioned face
index, the value for `FC_INDEX` contains the index of the named instance
of the face in the upper 16 bits. This behaviour is completely
undocumented, but FreeType uses the same format:
https://freetype.org/freetype2/docs/reference/ft2-face_creation.html#ft_open_face.

This wasn't a problem for the longest time, because the only consumer of
the `index` value coming from fontconfig was FreeType. However, sometime
after
29e618dcf7,
we started also passing it to`read-fonts` . `read-fonts` expects only
the face index, causing it to return errors as seen in
https://github.com/servo/servo/issues/39209.

The fix is to seperate the two indices when storing them in a
`LocalFontDescriptor` and pass only the lower 16 bits to `read-fonts`.
I'm unsure whether we should continue passing the named instance index
to FreeType since we don't actually support variable fonts, but I've
opted to keep the current behaviour for now.

Fixes: https://github.com/servo/servo/issues/39209

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
This commit is contained in:
Simon Wülker 2025-09-09 00:29:33 +02:00 committed by GitHub
parent d2c78db981
commit fcf4beeac0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 36 additions and 9 deletions

View file

@ -432,7 +432,8 @@ where
let mut produce_font = |font: &Font| {
let local_font_identifier = LocalFontIdentifier {
path: Atom::from(FontList::font_absolute_path(&font.filename)),
variation_index: 0,
face_index: 0,
named_instance_index: 0,
};
let stretch = StyleFontStretch::NORMAL;
let weight = font

View file

@ -96,7 +96,11 @@ impl PlatformFontMethods for PlatformFont {
let library = FreeTypeLibraryHandle::get().lock();
let filename = CString::new(&*font_identifier.path).expect("filename contains NUL byte!");
let face = FreeTypeFace::new_from_file(&library, &filename, font_identifier.index())?;
let face = FreeTypeFace::new_from_file(
&library,
&filename,
font_identifier.face_index_for_freetype(),
)?;
let normalized_variations = face.set_variations_for_font(variations, &library)?;

View file

@ -123,7 +123,8 @@ where
let local_font_identifier = LocalFontIdentifier {
path: Atom::from(c_str_to_string(path as *const c_char)),
variation_index: index as i32,
face_index: (index & 0xFFFF) as u16,
named_instance_index: (index >> 16) as u16,
};
let descriptor = FontTemplateDescriptor::new(weight, stretch, style);

View file

@ -444,7 +444,8 @@ where
let mut produce_font = |font: &Font| {
let local_font_identifier = LocalFontIdentifier {
path: Atom::from(font.filepath.clone()),
variation_index: 0,
face_index: 0,
named_instance_index: 0,
};
let stretch = font.width.into();
let weight = font