fop_layout/layout/table/
column.rs1use fop_types::Length;
4
5use super::types::{BorderCollapse, ColumnInfo, ColumnWidth, GridCell, TableLayout};
6
7impl TableLayout {
8 pub fn compute_fixed_widths(&self, columns: &[ColumnWidth]) -> Vec<Length> {
10 let n_cols = columns.len();
11 if n_cols == 0 {
12 return Vec::new();
13 }
14
15 let total_spacing = match self.border_collapse {
17 BorderCollapse::Separate => self.border_spacing * (n_cols + 1) as i32,
18 BorderCollapse::Collapse => Length::ZERO,
19 };
20 let available_for_cols = self.available_width - total_spacing;
21
22 let total_proportional: f64 = columns
24 .iter()
25 .filter_map(|w| {
26 if let ColumnWidth::Proportional(p) = w {
27 Some(*p)
28 } else {
29 None
30 }
31 })
32 .sum();
33
34 let fixed_total: Length = columns
36 .iter()
37 .filter_map(|w| {
38 if let ColumnWidth::Fixed(len) = w {
39 Some(*len)
40 } else {
41 None
42 }
43 })
44 .fold(Length::ZERO, |acc, len| acc + len);
45
46 let remaining = available_for_cols - fixed_total;
48
49 columns
51 .iter()
52 .map(|spec| match spec {
53 ColumnWidth::Fixed(len) => *len,
54 ColumnWidth::Proportional(p) => {
55 if total_proportional > 0.0 {
56 Length::from_pt(remaining.to_pt() * p / total_proportional)
57 } else {
58 Length::ZERO
59 }
60 }
61 ColumnWidth::Auto => {
62 let auto_count = columns
64 .iter()
65 .filter(|w| matches!(w, ColumnWidth::Auto))
66 .count();
67 if auto_count > 0 {
68 remaining / auto_count as i32
69 } else {
70 Length::ZERO
71 }
72 }
73 })
74 .collect()
75 }
76
77 pub fn compute_auto_widths(&self, column_info: &[ColumnInfo]) -> Vec<Length> {
95 let n_cols = column_info.len();
96 if n_cols == 0 {
97 return Vec::new();
98 }
99
100 let total_spacing = match self.border_collapse {
102 BorderCollapse::Separate => self.border_spacing * (n_cols + 1) as i32,
103 BorderCollapse::Collapse => Length::ZERO,
104 };
105 let available_for_cols = self.available_width - total_spacing;
106
107 let mut widths = vec![Length::ZERO; n_cols];
108
109 let mut fixed_total = Length::ZERO;
111 let mut auto_count = 0;
112 let mut proportional_count = 0;
113
114 for (i, info) in column_info.iter().enumerate() {
115 match info.width_spec {
116 ColumnWidth::Fixed(len) => {
117 widths[i] = len;
118 fixed_total += len;
119 }
120 ColumnWidth::Auto => {
121 auto_count += 1;
122 }
123 ColumnWidth::Proportional(_) => {
124 proportional_count += 1;
125 }
126 }
127 }
128
129 let remaining = available_for_cols - fixed_total;
130
131 if proportional_count > 0 {
133 let total_proportional: f64 = column_info
134 .iter()
135 .filter_map(|info| {
136 if let ColumnWidth::Proportional(p) = info.width_spec {
137 Some(p)
138 } else {
139 None
140 }
141 })
142 .sum();
143
144 if total_proportional > 0.0 {
145 for (i, info) in column_info.iter().enumerate() {
146 if let ColumnWidth::Proportional(p) = info.width_spec {
147 widths[i] = Length::from_pt(remaining.to_pt() * p / total_proportional);
148 }
149 }
150 return widths;
151 }
152 }
153
154 if auto_count > 0 {
156 let mut total_min = Length::ZERO;
158 let mut total_max = Length::ZERO;
159
160 for info in column_info.iter() {
161 if matches!(info.width_spec, ColumnWidth::Auto) {
162 total_min += info.min_width;
163 total_max += info.max_width;
164 }
165 }
166
167 if remaining >= total_max {
169 for (i, info) in column_info.iter().enumerate() {
171 if matches!(info.width_spec, ColumnWidth::Auto) {
172 widths[i] = info.max_width;
173 }
174 }
175 } else if remaining >= total_min {
176 let range = total_max - total_min;
178 if range > Length::ZERO {
179 for (i, info) in column_info.iter().enumerate() {
180 if matches!(info.width_spec, ColumnWidth::Auto) {
181 let col_range = info.max_width - info.min_width;
182 let ratio = col_range.to_pt() / range.to_pt();
183 let extra = Length::from_pt((remaining - total_min).to_pt() * ratio);
184 widths[i] = info.min_width + extra;
185 }
186 }
187 } else {
188 let per_col = remaining / auto_count;
190 for (i, info) in column_info.iter().enumerate() {
191 if matches!(info.width_spec, ColumnWidth::Auto) {
192 widths[i] = per_col;
193 }
194 }
195 }
196 } else {
197 if total_min > Length::ZERO {
199 let scale = remaining.to_pt() / total_min.to_pt();
200 for (i, info) in column_info.iter().enumerate() {
201 if matches!(info.width_spec, ColumnWidth::Auto) {
202 widths[i] = Length::from_pt(info.min_width.to_pt() * scale);
203 }
204 }
205 } else {
206 let per_col = remaining / auto_count;
208 for (i, info) in column_info.iter().enumerate() {
209 if matches!(info.width_spec, ColumnWidth::Auto) {
210 widths[i] = per_col;
211 }
212 }
213 }
214 }
215 }
216
217 widths
218 }
219
220 pub fn measure_column_widths(
223 &self,
224 grid: &[Vec<Option<GridCell>>],
225 col_idx: usize,
226 ) -> (Length, Length) {
227 let mut min_width = Length::ZERO;
228 let mut max_width = Length::ZERO;
229
230 for row in grid {
231 if let Some(Some(cell)) = row.get(col_idx) {
232 if cell.rowspan > 0 && cell.colspan > 0 {
234 let cell_min = Length::from_pt(30.0); let cell_max = Length::from_pt(200.0); min_width = min_width.max(cell_min);
240 max_width = max_width.max(cell_max);
241 }
242 }
243 }
244
245 if max_width < min_width {
247 max_width = min_width;
248 }
249
250 (min_width, max_width)
251 }
252
253 pub fn update_column_info_from_grid(
255 &self,
256 column_info: &mut [ColumnInfo],
257 grid: &[Vec<Option<GridCell>>],
258 ) {
259 for (col_idx, info) in column_info.iter_mut().enumerate() {
260 if matches!(info.width_spec, ColumnWidth::Auto) {
261 let (min, max) = self.measure_column_widths(grid, col_idx);
262 info.min_width = min;
263 info.max_width = max;
264 }
265 }
266 }
267
268 pub fn distribute_colspan_widths(
271 &self,
272 column_info: &mut [ColumnInfo],
273 grid: &[Vec<Option<GridCell>>],
274 ) {
275 for row in grid {
276 for (col_idx, cell_opt) in row.iter().enumerate() {
277 if let Some(cell) = cell_opt {
278 if cell.rowspan > 0 && cell.colspan > 1 && cell.col == col_idx {
280 let end_col = (cell.col + cell.colspan).min(column_info.len());
281
282 let mut total_min = Length::ZERO;
284 let mut auto_cols = 0;
285
286 for i in cell.col..end_col {
287 if let Some(info) = column_info.get(i) {
288 if matches!(info.width_spec, ColumnWidth::Auto) {
289 total_min += info.min_width;
290 auto_cols += 1;
291 }
292 }
293 }
294
295 let cell_min = Length::from_pt(50.0 * cell.colspan as f64);
298
299 if cell_min > total_min && auto_cols > 0 {
300 let extra_per_col = (cell_min - total_min) / auto_cols;
302
303 for i in cell.col..end_col {
304 if let Some(info) = column_info.get_mut(i) {
305 if matches!(info.width_spec, ColumnWidth::Auto) {
306 info.min_width += extra_per_col;
307 info.max_width = info.max_width.max(info.min_width);
308 }
309 }
310 }
311 }
312 }
313 }
314 }
315 }
316 }
317}