Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions crates/gpui_ce_parley/src/text_system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1463,6 +1463,34 @@ impl ParleyTextSystem {
self
}

/// Appends installed `families` after the operating system's own fallback for `script`.
///
/// Some platforms answer a script fallback with a single face, chosen from one sample
/// character, so a character that face lacks has no candidate at all. Families that are
/// not installed are skipped, and the system's pick keeps its place first.
pub fn with_script_fallback_families(
self,
script: [u8; 4],
families: impl IntoIterator<Item = impl AsRef<str>>,
) -> Self {
{
let mut state = self.parley.lock();
let collection = &mut state.font_context.collection;
let key = fontique::FallbackKey::new(fontique::Script::from_bytes(script), None);
// Resolve the system's pick first: a key that already has families never asks it.
let mut ids = collection.fallback_families(key).collect::<Vec<_>>();
for family in families {
if let Some(id) = collection.family_id(family.as_ref())
&& !ids.contains(&id)
{
ids.push(id);
}
}
collection.set_fallbacks(key, ids.into_iter());
}
self
}

fn font_families<'a>(
&'a self,
descriptor: &'a Font,
Expand Down
45 changes: 44 additions & 1 deletion crates/gpui_windows/src/direct_write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,19 @@ impl DirectWriteTextSystem {
"Segoe UI",
BitmapFallbackGlyphRasterizer::new(SharedDirectWriteRenderer(renderer.clone())),
)
.with_fallback_families(["Lilex", "IBM Plex Sans", "Arial"]);
.with_fallback_families(["Lilex", "IBM Plex Sans", "Arial"])
// DirectWrite picks one Han face from a sample character; on a Japanese or Korean
// pick, Simplified-only characters such as 简 would otherwise draw as .notdef.
.with_script_fallback_families(
*b"Hani",
[
"Microsoft YaHei UI",
"Microsoft JhengHei UI",
"Yu Gothic UI",
"Malgun Gothic",
"SimSun-ExtB",
],
);

Ok(Self { parley, renderer })
}
Expand Down Expand Up @@ -959,6 +971,37 @@ mod tests {
use gpui_parley::FontSynthesis;
use std::collections::BTreeMap;

#[test]
fn han_fallback_covers_simplified_only_characters() -> Result<()> {
let system = DirectWriteTextSystem::new_headless()?;
// 简 is Simplified-only and absent from Japanese and Korean UI fonts; the rest
// are shared Han, Japanese and Hangul text that must keep a real glyph too.
for text in ["简体中文", "繁體中文", "日本語", "한국어"] {
let runs = [gpui::TextRun {
len: text.len(),
font: font("Segoe UI"),
..Default::default()
}];
let layout = system.layout_text(TextLayoutRequest {
text,
font_size: px(16.0),
runs: &runs,
options: Default::default(),
});
let glyphs = layout
.paint_fragments
.iter()
.flat_map(|fragment| fragment.glyphs.iter())
.collect::<Vec<_>>();
assert_eq!(glyphs.len(), text.chars().count(), "{text}");
assert!(
glyphs.iter().all(|glyph| glyph.id != GlyphId(0)),
"{text} shaped a .notdef glyph"
);
}
Ok(())
}

#[test]
fn fixed_fonts_cover_native_modes_instances_and_empty_glyphs() -> Result<()> {
let system = DirectWriteTextSystem::new_headless()?;
Expand Down
Loading