fonts: Improve font fallback (#32286)

- Better detect situations where emoji is necessary by looking ahead one
  character while laying out. This allow processing Unicode presentation
  selectors. When detecting emoji, put emoji fonts at the front of
  fallback lists for all platforms.

  This enables monochrome emoji on Windows. Full-color emoji on Windows
  probably needs full support for processing the COLR table and drawing
  separate glyph color layers.

- Improve the font fallback list on FreeType platforms. Ideally, Servo
  would be able to look through the entire font list to find the best
  font for a certain character, but until that time we can make sure the
  font list contains the "Noto Sans" fonts which cover most situations.

Fixes #31664.
Fixes #12944.
This commit is contained in:
Martin Robinson 2024-05-27 12:02:26 +02:00 committed by GitHub
parent 5f0866379a
commit 43a3c9c319
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 610 additions and 292 deletions

View file

@ -316,10 +316,11 @@ impl TextRun {
} else {
Box::new(collapsed)
};
let char_iterator = TwoCharsAtATimeIterator::new(char_iterator);
let mut next_byte_index = 0;
let text = char_iterator
.map(|character| {
.map(|(character, next_character)| {
let current_byte_index = next_byte_index;
next_byte_index += character.len_utf8();
@ -338,10 +339,11 @@ impl TextRun {
return character;
}
let font = match font_group
.write()
.find_by_codepoint(font_context, character)
{
let font = match font_group.write().find_by_codepoint(
font_context,
character,
next_character,
) {
Some(font) => font,
None => return character,
};
@ -791,3 +793,40 @@ fn capitalize_string(string: &str, allow_word_at_start: bool) -> String {
output_string
}
pub struct TwoCharsAtATimeIterator<InputIterator> {
/// The input character iterator.
iterator: InputIterator,
/// The first character to produce in the next run of the iterator.
next_character: Option<char>,
}
impl<InputIterator> TwoCharsAtATimeIterator<InputIterator> {
fn new(iterator: InputIterator) -> Self {
Self {
iterator,
next_character: None,
}
}
}
impl<InputIterator> Iterator for TwoCharsAtATimeIterator<InputIterator>
where
InputIterator: Iterator<Item = char>,
{
type Item = (char, Option<char>);
fn next(&mut self) -> Option<Self::Item> {
// If the iterator isn't initialized do that now.
if self.next_character.is_none() {
self.next_character = self.iterator.next();
}
let Some(character) = self.next_character else {
return None;
};
self.next_character = self.iterator.next();
return Some((character, self.next_character.clone()));
}
}