#![cfg(any(target_os = "macos", target_os = "windows"))]
use slate_text::{FontHandle, ShapedLine, TEST_FONT, TextBackend};
#[cfg(target_os = "macos")]
fn shape(text: &str) -> ShapedLine {
use slate_text::CoreTextBackend;
let mut backend = CoreTextBackend::new().expect("CoreText backend init");
let font = backend
.load_font_from_bytes(TEST_FONT, 16.0, 2.0)
.expect("load bundled DejaVu Sans");
backend.shape_line(&font, text).expect("shape_line")
}
#[cfg(target_os = "windows")]
fn shape(text: &str) -> ShapedLine {
use slate_text::DirectWriteBackend;
let mut backend = DirectWriteBackend::new().expect("DirectWrite backend init");
let font = backend
.load_font_from_bytes(TEST_FONT, 16.0, 2.0)
.expect("load bundled DejaVu Sans");
backend.shape_line(&font, text).expect("shape_line")
}
fn assert_no_notdef(text: &str, label: &str) {
let line = shape(text);
assert!(
!line.glyphs.is_empty(),
"{label}: expected glyphs for non-empty input"
);
for (i, g) in line.glyphs.iter().enumerate() {
assert_ne!(
g.glyph_id, 0,
"{label}: glyph {i} is .notdef — native substitution did not cover it",
);
}
}
fn assert_clusters_monotonic(text: &str, label: &str) {
let line = shape(text);
let clusters: Vec<u32> = line.glyphs.iter().map(|g| g.cluster).collect();
let non_decreasing = clusters.windows(2).all(|w| w[1] >= w[0]);
let non_increasing = clusters.windows(2).all(|w| w[1] <= w[0]);
assert!(
non_decreasing || non_increasing,
"{label}: clusters interleave directions (not sorted): {clusters:?}",
);
}
#[test]
fn combining_mark_renders_without_tofu() {
assert_no_notdef("e\u{0301}", "combining acute");
assert_clusters_monotonic("e\u{0301}", "combining acute");
}
#[test]
fn arabic_word_renders_without_tofu() {
assert_no_notdef("مرحبا", "arabic word");
assert_clusters_monotonic("مرحبا", "arabic word");
}
#[test]
fn devanagari_conjunct_renders_without_tofu() {
assert_no_notdef("क्ष", "devanagari conjunct");
assert_clusters_monotonic("क्ष", "devanagari conjunct");
}
#[test]
fn zwj_emoji_renders_without_tofu() {
assert_no_notdef(
"\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F467}",
"zwj family emoji",
);
assert_clusters_monotonic(
"\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F467}",
"zwj family emoji",
);
}
#[test]
fn missing_codepoint_captures_substitute_handle() {
let line = shape("世界");
assert!(!line.glyphs.is_empty(), "expected glyphs for CJK input");
let substituted = line
.glyphs
.iter()
.any(|g| g.font_handle != FontHandle::default());
assert!(
substituted,
"expected at least one glyph tagged with a substitute font_handle \
(native fallback capture); none differed from the default sentinel",
);
for (i, g) in line.glyphs.iter().enumerate() {
assert_ne!(g.glyph_id, 0, "glyph {i} is .notdef despite substitution");
}
}