Layout: keep narrow cell line widths (task 245) (#2436)

Co-authored-by: Brandon Thomas <echelon@gmail.com>
This commit is contained in:
Fin WarmanandBrandon Thomas authored and GitHub committed 2026-10-18 10:24:58 -04:00
1 parent d7bd5bcd3c
commit 4deb654862
3 files changed
+45 -3

No files matched your search

+5 -3
View File
@@ -1845,9 +1845,11 @@ fn ptab_stop(align: PTabAlign, base: PTabBase, leader: TabLeader, width: f32, in
}
fn break_lines(pl: &mut ParaLayout, env: &ParaEnv, mark_style: u32, level: Option<&Level>) {
// Narrow cells and float spans must retain their usable width.
const MIN_LINE_WIDTH: f32 = 1.0;
let suffix = level.map(|l| l.suffix);
let rp = pl.rp.clone();
let width = env.width.max(12.0);
let width = env.width.max(MIN_LINE_WIDTH);
let base_right = (width - rp.indent_right).max(1.0);
let default_tab = env.doc.settings.default_tab;
let first_left = rp.indent_left + rp.indent_first;
@@ -1929,7 +1931,7 @@ fn break_lines(pl: &mut ParaLayout, env: &ParaEnv, mark_style: u32, level: Optio
narrowed = !rest.is_empty() || a > lo || b < base_right;
row_rest = rest;
row_first = lines.len();
break (a, b.max(a + 12.0));
break (a, b.max(a + MIN_LINE_WIDTH));
}
Err(below) if below > top && guard < 50 => {
top = below;
@@ -1937,7 +1939,7 @@ fn break_lines(pl: &mut ParaLayout, env: &ParaEnv, mark_style: u32, level: Optio
}
Ok((None, _)) | Err(_) => {
row_first = lines.len();
break (lo, base_right.max(lo + 12.0));
break (lo, base_right.max(lo + MIN_LINE_WIDTH));
}
}
}
+38
View File
@@ -952,6 +952,44 @@ fn positional_tab_after_only_a_bookmark_stays_on_its_line() {
assert!((x - 108.0).abs() < 0.5, "X at the indent: {x}");
}
#[test]
fn narrow_cells_keep_their_width() {
let mut p = wordcraft_doc::Paragraph::with_text("WW", wordcraft_doc::CharProps { size: Some(6.0), ..Default::default() });
p.props.space_before = Some(0.0);
p.props.space_after = Some(0.0);
let mut d = Document::new();
d.body = vec![wordcraft_doc::para_block(p.clone())];
let wide = lay(&d);
let advance = wide.pages[0]
.items
.iter()
.find_map(|item| match item {
Placed::Lines { para, .. } => Some(para.clusters.iter().map(|c| c.adv).sum::<f32>()),
_ => None,
})
.unwrap();
let narrow = advance - 0.25;
assert!(narrow > 6.0 && advance < 12.0, "probe width {advance}");
for (width, expected_lines) in [(narrow, 2), (1.0, 2), (12.0, 1)] {
let mut table = Table::new(1, 1, width);
table.props.style = None;
table.props.fixed = true;
table.props.cell_margins = Some([0.0; 4]);
table.rows[0].cells[0].blocks = vec![wordcraft_doc::para_block(p.clone())];
d.body = vec![std::sync::Arc::new(Block::Table(table))];
let l = lay(&d);
let lines = l.pages[0]
.items
.iter()
.find_map(|item| match item {
Placed::Lines { para, .. } => Some(para.lines.len()),
_ => None,
})
.unwrap();
assert_eq!(lines, expected_lines, "cell width {width}, text width {advance}");
}
}
#[test]
fn lists_get_labels_and_indent() {
let mut d = Document::from_text("one\ntwo\nthree");
+2
View File
@@ -23,6 +23,7 @@ Additive paragraph spacing retains the full before-space after a section page br
| 2026-10-16 | trivial | Split-cell content retains its resolved top margin and border band on continuation pages, matching original Word Mac16.113.2 controls. Synthetic regressions cover margins, borders, line spacing and image frames. |
| Hint-reliant CJK glyph outlines (DFKai-SB / 標楷體) | 🟡 | Required TrueType interpreter runs at units-per-em size; local Windows glyph-bounds regression and synthetic DOCX page/window comparison pass. Other platforms and hint-reliant families remain unmeasured; no native Word comparison. | — |
| Line breaking (UAX #14 with Word's exceptions: no break after `/`) | ✅ | `layout::tests::no_line_break_right_after_a_slash` | — |
| Narrow table cells | ✅ | `layout::tests::narrow_cells_keep_their_width`: usable widths below 12 pt wrap without expansion; oversized glyphs still make progress | 0 |
| Justification, Word 2013+ space shrinking (compat mode 15) | ✅ | #109 | — |
| Spaces at the end of a line hang past the right margin instead of wrapping; the caret after them stays at the margin, in the body and in headers and footers | ✅ | `layout::tests::caret_after_hanging_spaces_stays_at_the_margin` (#2146): a letter typed after them wraps to the next line | — |
| Complex scripts (Myanmar, Khmer, Indic): shaped per script run with OpenType fallback fonts, one cluster per syllable, Myanmar wraps between syllables | 🟡 | `layout::tests_complex` (#604). Khmer, Thai and Lao still wrap only at spaces (need a dictionary) | 4–8 |
@@ -211,6 +212,7 @@ Behind-text pictures in table cells participate in centre and bottom vertical al
| 2026-10-16 | trivial | Behind-text pictures in table cells participate in centre and bottom vertical alignment without increasing row flow height. |
| 2026-10-15 | trivial | Absent or zero DrawingML effect extents remain zero on rotated pictures; contracted axes no longer reserve invented effects room. Synthetic quarter-turn import regression (`zero_effects_stay_zero_rotated`). |
| 2026-10-15 | trivial | Additive paragraph spacing retains the full before-space after a section page break; ordinary overlapping spacing is unchanged. |
| 2026-10-15 | trivial | Removed the 12 pt line-width floor in narrow cells and float spans; synthetic wrapping and oversized-glyph controls |
| 2026-10-14 | trivial | Row table border exceptions draw without leaking into other rows |
| 2026-10-14 | trivial | Consistent cell widths override a stale table grid |
| 2026-10-14 | trivial | Paragraphs inside complex-field instructions take no flow height; empty result paragraphs keep their line. |