mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-31 21:48:20 -06:00
Text: the zh-hans Han order test replaces the skipping render test (#2039)
The CJK render test skipped wherever no Japanese face was installed, which is every CI runner, and on a Mac it drew glyphs with or without the fix, so it caught nothing. Replace it with a fallback-order test: fixture faces for `Hiragino Sans` and `Hiragino Sans GB` show that a zh-hans Han lookup tries the Chinese family first, that ja tries the Japanese one, and that a layout gets Hiragino Sans GB. Synthetic fonts in an isolated child process keep it independent of the host's fonts; with the Simplified list reverted it fails on `[7]` against `[8, 7]`. Delete `TEST_CJK_LOCALE`: the locale-mutating tests moved to an isolated child process, so nothing used the lock and the test-only static failed clippy's `-D warnings` gate. Trim the documentation edits to the single docs/gaps.md gap-11 row: revert the header and changelog edits in ROADMAP.md, docs/roadmap.md, docs/localization-parity.md and docs/target-app-parity.md, and the gaps.md header and changelog row, which conflict with other PRs. Tests: cargo clippy -p effectcraft-text -p effectcraft-render --all-targets -- -D warnings, cargo clippy --workspace --all-targets -- -D warnings, cargo test -p effectcraft-text (79 lib), cargo test -p effectcraft-render (264 passed, 1 ignored perf test), cargo fmt --all -- --check.
This commit is contained in:
@@ -767,12 +767,6 @@ const GEORGIAN_FAMILIES: &[&str] = &["Sylfaen", ".SF Georgian", "SF Georgian", "
|
||||
/// The CJK locale the host asked fallbacks to prefer, when it has one (see [`set_cjk_locale`]).
|
||||
static REQUESTED_CJK_LOCALE: RwLock<Option<String>> = RwLock::new(None);
|
||||
|
||||
/// Serialises the tests that change the process-global CJK locale with the tests that read it
|
||||
/// through [`fallback_for`] (the layout tests), which would otherwise cache the other order's
|
||||
/// families.
|
||||
#[cfg(test)]
|
||||
pub(crate) static TEST_CJK_LOCALE: std::sync::Mutex<()> = std::sync::Mutex::new(());
|
||||
|
||||
/// Ask CJK font fallbacks to prefer `tag`'s language for Han ideographs instead of the locale
|
||||
/// environment, so a Chinese interface is drawn with a Chinese face on a machine whose locale is
|
||||
/// Japanese (and the other way round). The interface sets this from its language setting; an empty
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
use super::*;
|
||||
|
||||
/// Run `test` again in a child process with `variable` set. The fixture faces below go into the
|
||||
/// process-global font database and its fallback caches, so only a process that runs one such test
|
||||
/// on its own can use them.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn run_in_child(variable: &str, test: &str) {
|
||||
let output = std::process::Command::new(std::env::current_exe().unwrap()).args(["--exact", test, "--nocapture"]).env(variable, "1").output().unwrap();
|
||||
assert!(output.status.success(), "{}\n{}", String::from_utf8_lossy(&output.stdout), String::from_utf8_lossy(&output.stderr));
|
||||
}
|
||||
|
||||
/// Concurrent language requests must neither choose another context's font nor reuse its
|
||||
/// cached family order. Synthetic fonts in a child process keep this independent of the host
|
||||
/// font installation and of other tests that exercise the process-global database/locale.
|
||||
@@ -8,12 +17,7 @@ use super::*;
|
||||
fn cjk_requests_keep_their_language_and_cache_entries() {
|
||||
const CHILD: &str = "EFFECTCRAFT_TEST_ISOLATED_CJK";
|
||||
if std::env::var_os(CHILD).is_none() {
|
||||
let output = std::process::Command::new(std::env::current_exe().unwrap())
|
||||
.args(["--exact", "fonts::tests::cjk_requests_keep_their_language_and_cache_entries", "--nocapture"])
|
||||
.env(CHILD, "1")
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(output.status.success(), "{}\n{}", String::from_utf8_lossy(&output.stdout), String::from_utf8_lossy(&output.stderr));
|
||||
run_in_child(CHILD, "fonts::tests::cjk_requests_keep_their_language_and_cache_entries");
|
||||
return;
|
||||
}
|
||||
let families = [("ja", "Yu Gothic"), ("zh-hans", "Microsoft YaHei"), ("zh-hant", "Microsoft JhengHei"), ("ko", "Malgun Gothic")];
|
||||
@@ -71,6 +75,52 @@ fn cjk_requests_keep_their_language_and_cache_entries() {
|
||||
}
|
||||
}
|
||||
|
||||
/// #453: with a Chinese interface, a Han ideograph that both a Chinese and a Japanese face cover
|
||||
/// must reach the installed Simplified Chinese family first, or Chinese text in a composition is
|
||||
/// drawn with Japanese glyph forms. Fixture faces named after the macOS families keep this
|
||||
/// independent of the host's fonts and of rendering. Before the fix the Simplified list named none
|
||||
/// of the macOS faces a stock install has, so the walk below reached the Japanese face - the
|
||||
/// regression this guards.
|
||||
#[test]
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn zh_hans_han_fallback_puts_the_chinese_family_first() {
|
||||
const CHILD: &str = "EFFECTCRAFT_TEST_ZH_HANS_ORDER";
|
||||
if std::env::var_os(CHILD).is_none() {
|
||||
run_in_child(CHILD, "fonts::tests::zh_hans_han_fallback_puts_the_chinese_family_first");
|
||||
return;
|
||||
}
|
||||
let mut ids = Vec::new();
|
||||
{
|
||||
let mut d = db().write().unwrap();
|
||||
d.scanned = true; // This test process uses only bundled and synthetic fonts.
|
||||
// The Japanese face is added first, so scan order cannot be what picks the Chinese one.
|
||||
for family in ["Hiragino Sans", "Hiragino Sans GB"] {
|
||||
let mut data = test_font(family);
|
||||
let table = data[12..12 + 8 * 16].as_chunks::<16>().0.iter().find(|t| &t[..4] == b"cmap").unwrap();
|
||||
let cmap = u32::from_be_bytes(table[8..12].try_into().unwrap()) as usize + 12;
|
||||
// Map 页 to the fixture's triangle glyph instead of its private-use range.
|
||||
for (offset, value) in [(16, '页' as u16), (24, '页' as u16), (30, 2u16.wrapping_sub('页' as u16))] {
|
||||
data[cmap + offset..cmap + offset + 2].copy_from_slice(&value.to_be_bytes());
|
||||
}
|
||||
let data = Arc::new(data);
|
||||
let names = read_faces_bytes(&data).into_iter().next().unwrap();
|
||||
ids.push(push(&mut d, info_from(names, FaceData::Shared(data), "test")));
|
||||
}
|
||||
}
|
||||
let &[japanese, chinese] = ids.as_slice() else { panic!("two fixture faces expected: {ids:?}") };
|
||||
let base = resolve("Inter", "Regular").face;
|
||||
assert!(face(japanese).has_char('页') && face(chinese).has_char('页'));
|
||||
// The fallback list itself, before any coverage check: the Chinese family comes first for
|
||||
// zh-hans and second for ja, whether or not either face can draw the probe.
|
||||
let zh_hans: Vec<FaceId> = family_faces(FallbackScript::Han, &face(base), CjkLocale::Simplified).to_vec();
|
||||
assert_eq!(zh_hans, [chinese, japanese], "zh-hans tries the Chinese family first");
|
||||
let ja: Vec<FaceId> = family_faces(FallbackScript::Han, &face(base), CjkLocale::Japanese).to_vec();
|
||||
assert_eq!(ja, [japanese, chinese], "ja keeps the Japanese family first");
|
||||
// ...and the face a layout actually gets for the shared ideograph.
|
||||
assert_eq!(face(fallback_for_locale('页', base, "zh-hans")).info.family, "Hiragino Sans GB");
|
||||
assert_eq!(face(fallback_for_locale('页', base, "ja")).info.family, "Hiragino Sans");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extra_font_dirs_come_from_a_path_list() {
|
||||
assert!(extra_dirs(None).is_empty());
|
||||
|
||||
Reference in New Issue
Block a user