mirror of
https://github.com/penpot/penpot.git
synced 2026-11-06 01:50:39 +00:00
🐛 Let vertical Chinese and Korean ruby overhang its neighbours
The pairwise vertical path (Chinese, Korean and plain text) grew ruby bases without first computing overhang rooms, so a long Pinyin reading always spread its base. Horizontal text, and vertical Japanese, let the reading hang over an adjacent kana, comma, full stop or middle dot. Run set_ruby_overhang_rooms before grow_ruby_bases on that path too. It uses ruby_overhang_room, the rule horizontal applies to every language, so Han neighbours still take no overhang and ruby-overhang none keeps the old growth. A comment at the early return notes that warichu never splits there: only Japanese renders warichu. AI-assisted-by: claude-opus-5-5
This commit is contained in:
parent
31115ab29a
commit
cc6f183a90
@ -81,7 +81,11 @@ merges their styles. Emphasis marks retain their per-base placement across
|
||||
wraps; text bounds include the marks so individual exports keep them whole.
|
||||
Without a text selection, presentation changes affect existing readings.
|
||||
Horizontal readings form one Latin group above or below the base; vertical
|
||||
readings form one sideways Latin group beside it. Pronunciation is entered
|
||||
readings form one sideways Latin group beside it. A reading longer than its
|
||||
base overhangs an adjacent comma, full stop or middle dot (or kana) without
|
||||
its own reading, by at most one ruby character and half the neighbour, in
|
||||
both orientations. Han neighbours take no overhang. What overhang cannot
|
||||
absorb, or all of it under ruby-overhang `none`, spreads the base. Pronunciation is entered
|
||||
by the author; the editor does not generate or distribute syllables.
|
||||
|
||||
Composed and combining tone marks retain their source text and shaping.
|
||||
|
||||
@ -1575,6 +1575,69 @@ mod tests {
|
||||
assert!(ruby.glyph_tops.last().unwrap() + ruby.font_size <= base.top + base.extent + 0.01);
|
||||
}
|
||||
|
||||
/// 文 中(zhuang) , 文 in vertical Chinese: the reading is longer than
|
||||
/// its base, with a Han character before and a comma after.
|
||||
fn chinese_pinyin_neighbour_layout(overhang: RubyOverhang) -> VerticalLayout {
|
||||
let mut content = spans_content(
|
||||
vec![
|
||||
make_span("文"),
|
||||
TextSpan {
|
||||
ruby: "zhuangzhuang".to_string(),
|
||||
ruby_overhang: overhang,
|
||||
..make_span("中")
|
||||
},
|
||||
make_span(",文"),
|
||||
],
|
||||
1000.0,
|
||||
);
|
||||
content.paragraphs_mut()[0].set_language("zh-Hant-TW".to_string());
|
||||
layout_with_height(&provider(PINYIN_TEST_FONT), &content, 1000.0)
|
||||
}
|
||||
|
||||
fn column_extent(layout: &VerticalLayout) -> f32 {
|
||||
layout
|
||||
.cells
|
||||
.iter()
|
||||
.map(|cell| cell.top + cell.extent)
|
||||
.fold(0.0, f32::max)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vertical_chinese_pinyin_overhangs_a_following_comma() {
|
||||
let overhung = chinese_pinyin_neighbour_layout(RubyOverhang::Auto);
|
||||
let grown = chinese_pinyin_neighbour_layout(RubyOverhang::None);
|
||||
assert!(
|
||||
column_extent(&overhung) < column_extent(&grown) - 0.5,
|
||||
"overhang shortens the column: {} vs {}",
|
||||
column_extent(&overhung),
|
||||
column_extent(&grown)
|
||||
);
|
||||
let ruby = ruby_of_span(&overhung, 1);
|
||||
let base = overhung.cells.iter().find(|cell| cell.span == 1).unwrap();
|
||||
let ruby_end = ruby.glyph_tops.last().unwrap() + ruby.glyphs.last().unwrap().extent;
|
||||
assert!(
|
||||
ruby_end > base.top + base.extent + 0.5,
|
||||
"the reading hangs over the comma"
|
||||
);
|
||||
assert!(
|
||||
ruby.glyph_tops[0] >= base.top - 0.01,
|
||||
"a Han neighbour takes no overhang, as in horizontal text"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vertical_chinese_pinyin_without_overhang_grows_its_base() {
|
||||
let layout = chinese_pinyin_neighbour_layout(RubyOverhang::None);
|
||||
let ruby = ruby_of_span(&layout, 1);
|
||||
let base = layout.cells.iter().find(|cell| cell.span == 1).unwrap();
|
||||
let ruby_length: f32 = ruby.glyphs.iter().map(|glyph| glyph.extent).sum();
|
||||
assert!(ruby_length > EM, "the reading is longer than its base");
|
||||
assert!((base.extent - ruby_length).abs() < 0.01);
|
||||
assert!(ruby.glyph_tops[0] >= base.top - 0.01);
|
||||
let ruby_end = ruby.glyph_tops.last().unwrap() + ruby.glyphs.last().unwrap().extent;
|
||||
assert!(ruby_end <= base.top + base.extent + 0.01);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ruby_base_moves_to_the_next_column_whole() {
|
||||
let content = |height: f32| {
|
||||
|
||||
@ -569,6 +569,11 @@ impl PlannedParagraph {
|
||||
|
||||
let policy = cells.paragraph.layout_policy();
|
||||
if !policy.uses_jlreq() {
|
||||
// Warichu pieces never split here: only Japanese renders
|
||||
// warichu, and the frontend drops it for other languages.
|
||||
// Readings overhang neighbours by the rule horizontal text
|
||||
// applies to every language (`ruby_overhang_room`).
|
||||
set_ruby_overhang_rooms(&flow, &mut ruby_units);
|
||||
grow_ruby_bases(&mut flow, &ruby_units);
|
||||
let text: String = cells
|
||||
.text_map
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user