mirror of
https://github.com/ZSeven-W/openpencil.git
synced 2026-06-01 03:14:29 +07:00
fix(host): render Korean (Hangul) text in chrome
Hangul codepoints were routed to the shared CJK typeface, which is resolved from a Han ideograph (a Chinese font with no Hangul glyphs), so 한국어 painted as blank .notdef boxes in the locale picker while Chinese/Japanese/Hindi/Thai/Vietnamese rendered fine. Add a dedicated `is_hangul_codepoint` split + a cached `korean_typeface` (resolved from '한' → Apple SD Gothic Neo / Noto Sans KR) and route Hangul there in both typeface-resolution paths.
This commit is contained in:
parent
1d06be7f0d
commit
e2f0e4e1a0
3 changed files with 66 additions and 2 deletions
|
|
@ -144,6 +144,11 @@ pub struct NativeBackend {
|
|||
typeface_tried: bool,
|
||||
cjk_typeface: Option<skia_safe::Typeface>,
|
||||
cjk_typeface_tried: bool,
|
||||
/// Korean (Hangul) face — resolved separately from `cjk_typeface`
|
||||
/// because the Han-ideograph match returns a Chinese font without
|
||||
/// Hangul coverage.
|
||||
korean_typeface: Option<skia_safe::Typeface>,
|
||||
korean_typeface_tried: bool,
|
||||
/// Default-family per-codepoint typeface cache, keyed by
|
||||
/// `(codepoint, weight)`.
|
||||
char_typeface_cache: std::collections::HashMap<(i32, u16), Option<skia_safe::Typeface>>,
|
||||
|
|
@ -221,6 +226,8 @@ impl NativeBackend {
|
|||
typeface_tried: false,
|
||||
cjk_typeface: None,
|
||||
cjk_typeface_tried: false,
|
||||
korean_typeface: None,
|
||||
korean_typeface_tried: false,
|
||||
char_typeface_cache: std::collections::HashMap::new(),
|
||||
family_typeface_cache: std::collections::HashMap::new(),
|
||||
image_cache: std::collections::HashMap::new(),
|
||||
|
|
@ -255,7 +262,11 @@ impl NativeBackend {
|
|||
if c.is_ascii() && weight == 400 {
|
||||
return self.ensure_typeface().cloned();
|
||||
}
|
||||
if font_script::is_east_asian_codepoint(c) {
|
||||
if font_script::is_hangul_codepoint(c) {
|
||||
if let Some(tf) = self.ensure_korean_typeface().cloned() {
|
||||
return Some(tf);
|
||||
}
|
||||
} else if font_script::is_east_asian_codepoint(c) {
|
||||
return self.ensure_cjk_typeface().cloned();
|
||||
}
|
||||
let cp = c as i32;
|
||||
|
|
@ -287,7 +298,11 @@ impl NativeBackend {
|
|||
let Some(primary) = primary_font_family(family) else {
|
||||
return self.typeface_for_char(c, weight);
|
||||
};
|
||||
if font_script::is_east_asian_codepoint(c) {
|
||||
if font_script::is_hangul_codepoint(c) {
|
||||
if let Some(tf) = self.ensure_korean_typeface().cloned() {
|
||||
return Some(tf);
|
||||
}
|
||||
} else if font_script::is_east_asian_codepoint(c) {
|
||||
return self.ensure_cjk_typeface().cloned();
|
||||
}
|
||||
let key = (primary.to_string(), c as i32, weight);
|
||||
|
|
@ -358,6 +373,23 @@ impl NativeBackend {
|
|||
self.cjk_typeface.as_ref()
|
||||
}
|
||||
|
||||
/// Lazy-init the cached Korean (Hangul) typeface, resolved from a
|
||||
/// Hangul syllable so the OS picks a Hangul-covering face (e.g.
|
||||
/// Apple SD Gothic Neo / Noto Sans KR) rather than the Chinese
|
||||
/// font the Han-ideograph match returns.
|
||||
fn ensure_korean_typeface(&mut self) -> Option<&skia_safe::Typeface> {
|
||||
if !self.korean_typeface_tried {
|
||||
self.korean_typeface = self.font_mgr.match_family_style_character(
|
||||
"",
|
||||
skia_safe::FontStyle::default(),
|
||||
&[],
|
||||
'한' as i32,
|
||||
);
|
||||
self.korean_typeface_tried = true;
|
||||
}
|
||||
self.korean_typeface.as_ref()
|
||||
}
|
||||
|
||||
/// Convenience constructor for tests and the basic-window demo.
|
||||
pub fn with_dpi(dpi: f32) -> Self {
|
||||
Self::new(jian_skia::SkiaBackend::new(), dpi)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,16 @@
|
|||
/// Hangul (Korean): Jamo, Compatibility Jamo, Jamo Extended-A/B, and
|
||||
/// the precomposed syllable block. Routed to a dedicated Korean
|
||||
/// typeface — the shared CJK face is resolved from a Han ideograph
|
||||
/// (a Chinese font) and does NOT cover Hangul, so without this split
|
||||
/// Korean text renders as blank `.notdef` boxes.
|
||||
pub(super) fn is_hangul_codepoint(c: char) -> bool {
|
||||
let cp = c as u32;
|
||||
matches!(
|
||||
cp,
|
||||
0x1100..=0x11FF | 0x3130..=0x318F | 0xA960..=0xA97F | 0xAC00..=0xD7A3 | 0xD7B0..=0xD7FF
|
||||
)
|
||||
}
|
||||
|
||||
pub(super) fn is_east_asian_codepoint(c: char) -> bool {
|
||||
let cp = c as u32;
|
||||
matches!(
|
||||
|
|
|
|||
|
|
@ -380,3 +380,22 @@ fn image_draw_respects_node_opacity() {
|
|||
c.b()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn korean_hangul_resolves_to_a_covering_typeface() {
|
||||
// Regression: Hangul routed to the shared CJK face (resolved from
|
||||
// a Chinese ideograph) which lacks Hangul glyphs, so 한국어 painted
|
||||
// blank. The resolved face for '한' must actually cover '한'.
|
||||
let mut be = NativeBackend::with_dpi(1.0);
|
||||
let tf = be.typeface_for_char('한', 400).expect("a typeface for 한");
|
||||
assert_ne!(
|
||||
tf.unichar_to_glyph('한' as i32),
|
||||
0,
|
||||
"the resolved Hangul face must have a glyph for 한"
|
||||
);
|
||||
// Every char of the Korean locale name resolves to a covering face.
|
||||
for c in "한국어".chars() {
|
||||
let tf = be.typeface_for_char(c, 400).expect("typeface");
|
||||
assert_ne!(tf.unichar_to_glyph(c as i32), 0, "missing glyph for {c}");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue