mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-30 13:08:07 -06:00
Merge pull request #2039 from midasdf/jp/cjk-composition-rendering
Text: Chinese Han text uses the installed Chinese face (#453)
This commit is contained in:
@@ -707,14 +707,28 @@ const JAPANESE_FAMILIES: &[&str] = &[
|
||||
];
|
||||
/// Han ideograph families for Simplified Chinese, in the order the platforms' own fallback tables
|
||||
/// prefer them (Windows, macOS, then the Linux/Noto names). Missing ones cost one name lookup each.
|
||||
const SIMPLIFIED_CHINESE_FAMILIES: &[&str] =
|
||||
&["Microsoft YaHei", "SimSun", "PingFang SC", "Noto Sans CJK SC", "Noto Sans SC", "Source Han Sans SC", "WenQuanYi Micro Hei"];
|
||||
/// `Hiragino Sans GB`, `Heiti SC` and `Songti SC` are the Simplified faces a macOS install without
|
||||
/// the on-demand PingFang has (macOS 27 on an English install ships them, not PingFang): without
|
||||
/// them Chinese Han text fell through to the Japanese face and drew Japanese glyph forms (#453).
|
||||
const SIMPLIFIED_CHINESE_FAMILIES: &[&str] = &[
|
||||
"Microsoft YaHei",
|
||||
"SimSun",
|
||||
"PingFang SC",
|
||||
"Hiragino Sans GB",
|
||||
"Heiti SC",
|
||||
"Songti SC",
|
||||
"STSong",
|
||||
"Noto Sans CJK SC",
|
||||
"Noto Sans SC",
|
||||
"Source Han Sans SC",
|
||||
"WenQuanYi Micro Hei",
|
||||
];
|
||||
/// The same for Traditional Chinese. The two scripts share almost all of their code points but not
|
||||
/// the glyph forms (`东` against `東`'s neighbours, `直`, `骨`), so one list for both drew a
|
||||
/// Traditional interface with Simplified forms - the faces do not differ in *coverage*, which is
|
||||
/// what the fallback search checks, so the script has to pick the list.
|
||||
const TRADITIONAL_CHINESE_FAMILIES: &[&str] =
|
||||
&["Microsoft JhengHei", "MingLiU", "PingFang TC", "Heiti TC", "Hiragino Sans CNS", "Noto Sans CJK TC", "Noto Sans TC", "Source Han Sans TC"];
|
||||
&["Microsoft JhengHei", "MingLiU", "PingFang TC", "Heiti TC", "Hiragino Sans CNS", "Songti TC", "Noto Sans CJK TC", "Noto Sans TC", "Source Han Sans TC"];
|
||||
const KOREAN_FAMILIES: &[&str] = &["Malgun Gothic", "Gulim", "Apple SD Gothic Neo", "Noto Sans CJK KR", "Noto Sans KR", "Source Han Sans KR", "NanumGothic"];
|
||||
/// The platforms' plain text families for the other scripts the bundled fonts lack, in the same
|
||||
/// order (Windows, macOS, then the Linux/Noto names).
|
||||
|
||||
@@ -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());
|
||||
|
||||
+1
-1
@@ -67,7 +67,7 @@ are grouped by area; the issue tracker has the rest.
|
||||
| 8 | **Mask and shape bugs** | Open: #1004 mask vertex multi-select, #999 auto-pan, #1042 / #1033 vertex nodes under the bounding box, #959 / #960 shape group toggles and operators. Now closed: #510, #513, #527, #509, #303, #553, #932 (Convert To Bezier Path on a shape layer's parametric path) | Masks are core compositing | 10–20 h | G2 · [target-app-parity.md](target-app-parity.md) |
|
||||
| 9 | **Effect behaviour bugs** | #580, #463 Curl Noise, #369 Motion Tile output size, #943 Turbulent Displace Evolution, #473 Cryptomatte selection (quoted names with commas fixed, 3b7f1ace; UI and GPU time open), #474 multi-layer EXR (decode shared, faffad3a; f16 storage and cache sizing open). Closed: #526, #514, #533–#536 | Each one breaks a look someone built | 10–20 h | G1/G2 · [effects-parity.md](effects-parity.md) |
|
||||
| 10 | **Colour pipeline differences** | #340 ProRes 4444 tagged bt709 but sRGB-encoded (open); ACES 2.0 / OCIO 2.5 configs (26.5) missing; now closed: #494 32-bpc blend modes, #495 and #750 Working Gamma 2.4, #546 / #547 viewer colour | Wrong colour in delivery is a client-visible failure | 15–30 h | G1 · [file-format-parity.md](file-format-parity.md) |
|
||||
| 11 | **Text rendering and layout** | Open: #453 CJK text in comps, #316 paragraph alignment inverted (maintainers' tests show Left/Right correct; asked for a project), #417 soft at Fit (mostly fixed by #444), #550 toolbar font options. Closed: #483, #583 | Text is in almost every motion graphic | 10–20 h | G2 · [ui-parity.md](ui-parity.md) |
|
||||
| 11 | **Text rendering and layout** | #453 CJK text in comps: the Han fallback now reaches the installed Chinese faces; open: #316 paragraph alignment inverted (maintainers' tests show Left/Right correct; asked for a project), #417 soft at Fit (mostly fixed by #444), #550 toolbar font options. Closed: #483, #583 | Text is in almost every motion graphic | 10–20 h | G2 · [ui-parity.md](ui-parity.md) |
|
||||
| 12 | **3D interaction and depth** | No 3D transform gizmos (#392, #347); camera aperture (#260); reflections (#261); extruded surface materials beyond Front / Bevel / Side / Back Color (fb5a5e9a; #315 closed); #307 reopened (the viewer's renderer button now shows and switches the renderer, #645); Z position (#489) closed | Advanced 3D is After Effects' growth area since 2023 | 20–35 h | G2 · [ui-parity.md](ui-parity.md) |
|
||||
| 13 | **3D model formats and materials** | After Effects 26 imports FBX, USD/USDZ, STL besides glTF and OBJ (`usd_plugins`), and Substance 3D materials with mesh displacement (26.0–26.2); we read glTF, OBJ, STL (binary and ASCII, with facet colours) and FBX 7.x (binary and ASCII: meshes, hierarchy, Lambert/Phong materials and textures; no FBX animation or skinning yet) | Product shots and 3D scenes come as FBX/USDZ | 25–45 h | — · [file-format-parity.md](file-format-parity.md) |
|
||||
| 14 | **Object Matte** (26.2, disk cache in 26.5) | First slice (#783): the Object Matte effect (Effect ▸ Matte) and `objectMatte.apply` turn a point (and optional box) on a reference frame into a matte and follow the object up to 600 frames each way, or segment every frame on its own; Roto Brush's engine, cache and model (MobileSAM from Settings ▸ Roto Brush, classical graph cut without it). Since then: the Object Matte tool in the viewer (click / drag a box, hover crosshair), the box as the model's box prompt, background tracking with progress and cancel, Freeze, and segmentations kept in the disk cache (26.5). Missing: a comparison with After Effects ([plan](fidelity/object-matte.md)) | The new one-click roto | 10–20 h | G7 · [target-app-parity.md](target-app-parity.md) |
|
||||
|
||||
Reference in New Issue
Block a user