diff --git a/src/layout/table.rs b/src/layout/table.rs index b6cc23d..c743538 100644 --- a/src/layout/table.rs +++ b/src/layout/table.rs @@ -391,12 +391,23 @@ impl Engine<'_> { } // Absolute and minimum widths may claim more than the page; - // scale everything down proportionally so the table never - // overflows horizontally. + // scale columns down, preferably the ones with slack, so + // the table never overflows horizontally. let used: f32 = widths.iter().sum(); if used > total && used > 0.0 { - for width in &mut widths { - *width *= total / used; + let floor: Vec = (0..columns).map(|c| widths[c].min(min[c])).collect(); + let floor_total: f32 = floor.iter().sum(); + let slack_total = used - floor_total; + if floor_total < total && slack_total > 0.0 { + let excess = used - total; + for c in 0..columns { + widths[c] -= excess * (widths[c] - floor[c]) / slack_total; + } + } else { + // No slack anywhere, fall back to a uniform scale + for width in &mut widths { + *width *= total / used; + } } } diff --git a/src/layout/tests.rs b/src/layout/tests.rs index ded3524..776387d 100644 --- a/src/layout/tests.rs +++ b/src/layout/tests.rs @@ -391,6 +391,55 @@ fn auto_column_header_words_survive_content_width_scaling() { } } +#[test] +fn auto_columns_keep_their_minimum_width_when_a_cell_overflows() { + let fonts = test_fonts(); + let theme = Theme::default(); + let engine = Engine::new(&fonts, &theme); + let style = TableStyle { + columns: ColumnWidths::custom([ + ColumnWidth::Auto, + ColumnWidth::Fraction(1), + ColumnWidth::Auto, + ]), + ..TableStyle::data() + }; + + let overflowing_words = [ + "The-mantis-shrimp-Stomatopoda-Malacostraca-crustaceans-trinocular-depth-perception-circular-polarised-light", + "Odontodactylus-scyllarus-peacock-mantis-shrimp-smasher-appendage-acceleration-cavitation-bubble-collapse", + "Gonodactylus-smithii-spearer-burrow-defended-territory-aggressive-visual-signal-display-behaviour-noted-daily", + "Lysiosquillina-maculata-zebra-mantis-shrimp-monogamous-pair-bonding-burrow-sharing-lifetime-partnership", + "Hemisquilla-californiensis-giant-mantis-shrimp-bioluminescent-fluorescent-carapace-markings-detected-clearly", + ]; + + for word in overflowing_words { + let mut doc = Textris::new(); + doc.table_styled( + &style, + ["index", "name", "final score value"], + [["123", word, "12345"]], + ); + let d = doc.build(); + let crate::model::Block::Table(t) = &d.blocks[0] else { + panic!("expected a table"); + }; + let total = theme.page.content_width(); + let widths = engine.column_widths(t, t.columns(), total); + + // Auto columns must stay wide enough to hold their widest word + for col in [0, 2] { + let (min_content_width, _) = engine.column_metrics(t, col); + assert!( + widths[col] >= min_content_width + 2.0 * theme.table.inset_x - 0.01, + "{word:?}: column {col} ({}) should fit its own widest word (min {})", + widths[col], + min_content_width + 2.0 * theme.table.inset_x + ); + } + } +} + #[test] fn column_alignment_places_cell_text_left_center_and_right() { use crate::{