mirror of
https://github.com/penpot/penpot.git
synced 2026-11-04 17:10:39 +00:00
🐛 Keep kinsoku and ruby bases in narrow Japanese text boxes
The narrow-box overflow plan, which lets an indivisible unit overflow a box narrower than one character, ran for Chinese and Korean only. In a Japanese box of one character width Skia broke anywhere: a line started with "、" and a ruby base split over two lines. Run the plan for Japanese too, and refuse emergency breaks before a character that may not start a line or after one that may not end it. AI-assisted-by: claude-opus-5-5
This commit is contained in:
parent
8cbe3728f6
commit
b8a922b4f0
@ -19,7 +19,9 @@ impl HorizontalParagraphPlan {
|
||||
if !(policy.is_chinese()
|
||||
|| matches!(
|
||||
policy,
|
||||
TextLayoutPolicy::KoreanWords | TextLayoutPolicy::KoreanSyllable
|
||||
TextLayoutPolicy::Japanese
|
||||
| TextLayoutPolicy::KoreanWords
|
||||
| TextLayoutPolicy::KoreanSyllable
|
||||
))
|
||||
|| !width.is_finite()
|
||||
|| width <= 0.0
|
||||
|
||||
@ -1876,7 +1876,7 @@ fn horizontal_annotations_attach_to_the_ideographic_em_box() {
|
||||
#[test]
|
||||
fn horizontal_indivisible_punctuation_unit_overflows_a_narrow_box() {
|
||||
use crate::shapes::text_policy::KoreanWrap;
|
||||
for language in ["zh-Hant-TW", "zh-Hans-CN", "zh-Hant-HK", "ko"] {
|
||||
for language in ["zh-Hant-TW", "zh-Hans-CN", "zh-Hant-HK", "ko", "ja"] {
|
||||
let (_, plan, layout) =
|
||||
cjk_horizontal_layout(&["文", "。"], language, KoreanWrap::KeepWords, 20.0);
|
||||
let starts: Vec<_> = layout
|
||||
@ -1896,6 +1896,55 @@ fn horizontal_indivisible_punctuation_unit_overflows_a_narrow_box() {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn japanese_narrow_box_keeps_ruby_bases_whole_and_respects_kinsoku() {
|
||||
use crate::shapes::kinsoku::forbidden_at_line_start;
|
||||
use crate::shapes::text_vertical::test_support::provider;
|
||||
init_state();
|
||||
let mut fonts = skia::textlayout::FontCollection::new();
|
||||
fonts.set_asset_font_manager(Some(
|
||||
provider(include_bytes!("../../fonts/cjk-locl-test.ttf")).into(),
|
||||
));
|
||||
let mut resources =
|
||||
crate::render::RenderResources::try_new_headless().expect("headless resources");
|
||||
let _guard = crate::globals::TestRenderResourcesGuard::install(&mut resources);
|
||||
let mut base = make_span("文文", 0.0);
|
||||
base.font_size = 20.0;
|
||||
base.ruby = "ぶんぶん".to_string();
|
||||
let mut paragraph = make_paragraph(
|
||||
vec![make_span("「", 0.0), base, make_span("」、文文。", 0.0)],
|
||||
0.0,
|
||||
);
|
||||
paragraph.set_language("ja".to_string());
|
||||
let source: Vec<char> = "「文文」、文文。".chars().collect();
|
||||
let (plan, layout) = cjk_layout_paragraph(¶graph, 20.0, &fonts);
|
||||
let starts: Vec<usize> = layout
|
||||
.get_line_metrics()
|
||||
.iter()
|
||||
.map(|line| plan.offsets.builder_to_source(line.start_index))
|
||||
.collect();
|
||||
for start in &starts {
|
||||
assert!(
|
||||
!forbidden_at_line_start(source[*start]),
|
||||
"line {starts:?} starts with {}",
|
||||
source[*start]
|
||||
);
|
||||
}
|
||||
let targets = horizontal_ruby_targets(¶graph, &plan.offsets);
|
||||
assert_eq!(targets.len(), 1);
|
||||
let rects = layout.get_rects_for_range(
|
||||
targets[0].1.clone(),
|
||||
RectHeightStyle::Tight,
|
||||
RectWidthStyle::Tight,
|
||||
);
|
||||
assert!(
|
||||
rects
|
||||
.iter()
|
||||
.all(|r| (r.rect.top() - rects[0].rect.top()).abs() < 0.01),
|
||||
"the ruby base splits over lines {starts:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn narrow_atomic_overflow_retains_normal_wrap_budget_and_explicit_newlines() {
|
||||
use crate::shapes::text_policy::KoreanWrap;
|
||||
|
||||
@ -142,6 +142,9 @@ impl TextLayoutPolicy {
|
||||
&& !opening(before)
|
||||
&& !closing(after)
|
||||
&& !(self.is_korean() && "‐‑–-・·々〻ー".contains(after))
|
||||
&& !(self == Self::Japanese
|
||||
&& (crate::shapes::kinsoku::forbidden_at_line_start(after)
|
||||
|| crate::shapes::kinsoku::forbidden_at_line_end(before)))
|
||||
&& !(before == after && matches!(after, '…' | '‥' | '—' | '―'))
|
||||
&& !("$$£€₩¥¥₽₺₫++--−".contains(before) && numeric(after))
|
||||
&& !(numeric(before) && "%%‰℃℉°".contains(after))
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user