1use {
2 crate::{
3 composite::*,
4 fit::{
5 wrap,
6 TblFit,
7 },
8 line::FmtLine,
9 skin::MadSkin,
10 spacing::Spacing,
11 },
12 minimad::{
13 Alignment,
14 TableRow,
15 },
16};
17
18#[derive(Debug)]
20pub struct FmtTableRow<'s> {
21 pub cells: Vec<FmtComposite<'s>>,
22}
23
24#[derive(Debug)]
26pub enum RelativePosition {
27 Top,
28 Other, Bottom,
30}
31
32#[derive(Debug)]
37pub struct FmtTableRule {
38 pub position: RelativePosition, pub widths: Vec<usize>,
40 pub aligns: Vec<Alignment>,
41}
42
43impl FmtTableRule {
44 pub fn set_nbcols(&mut self, nbcols: usize) {
45 self.widths.truncate(nbcols);
46 self.aligns.truncate(nbcols);
47 for ic in 0..nbcols {
48 if ic >= self.widths.len() {
49 self.widths.push(0);
50 }
51 if ic >= self.aligns.len() {
52 self.aligns.push(Alignment::Unspecified);
53 }
54 }
55 }
56}
57
58impl<'s> FmtTableRow<'s> {
59 pub fn from(table_row: TableRow<'s>, skin: &MadSkin) -> FmtTableRow<'s> {
60 let mut table_row = table_row;
61 FmtTableRow {
62 cells: table_row
63 .cells
64 .drain(..)
65 .map(|composite| FmtComposite::from(composite, skin))
66 .collect(),
67 }
68 }
69}
70
71struct Table {
78 start: usize,
79 height: usize, nbcols: usize, }
82
83#[allow(clippy::needless_range_loop)]
84impl Table {
85 pub fn fix_columns(&mut self, lines: &mut Vec<FmtLine<'_>>, width: usize, skin: &MadSkin) {
86 let mut nbcols = self.nbcols;
87 if nbcols == 0 || width == 0 {
88 return;
89 }
90 let mut cols_removed = false;
91
92 let widths = match TblFit::new(nbcols, width) {
95 Ok(mut tbl_fit) => {
96 for line in lines.iter_mut().skip(self.start).take(self.height) {
97 if let FmtLine::TableRow(FmtTableRow { cells }) = line {
98 for ic in 0..nbcols {
99 if cells.len() <= ic {
100 cells.push(FmtComposite::new());
101 } else {
102 tbl_fit.see_cell(ic, cells[ic].visible_length);
103 }
104 }
105 } else if let FmtLine::TableRule(rule) = line {
106 rule.set_nbcols(nbcols);
107 } else {
108 panic!("not a table row, should not happen");
109 }
110 }
111 tbl_fit.fit().col_widths
112 }
113 Err(_) => {
114 nbcols = (width - 1) / 4;
116 cols_removed = true;
117 vec![3; nbcols]
118 }
119 };
120
121 for ir in (self.start..self.start + self.height).rev() {
127 let line = &mut lines[ir];
128 if let FmtLine::TableRow(FmtTableRow { cells }) = line {
129 let mut cells_to_add: Vec<Vec<FmtComposite<'_>>> = Vec::new();
130 cells.truncate(nbcols);
131 for ic in 0..nbcols {
132 if cells.len() <= ic {
133 cells.push(FmtComposite::new());
135 continue;
136 }
137 cells_to_add.push(Vec::new());
138 if cells[ic].visible_length > widths[ic] {
139 let mut composites =
141 wrap::hard_wrap_composite(&cells[ic], widths[ic], skin)
142 .expect("tbl fitter guaranteed all columns to be wide enough");
143 let mut drain = composites.drain(..);
146 cells[ic] = drain.next().unwrap();
147 for c in drain {
148 cells_to_add[ic].push(c);
149 }
150 }
151 }
152 let nb_new_lines = cells_to_add.iter().fold(0, |m, cells| m.max(cells.len()));
153 for inl in (0..nb_new_lines).rev() {
154 let mut new_cells: Vec<FmtComposite<'_>> = Vec::new();
155 for cell in cells_to_add.iter_mut().take(nbcols) {
156 new_cells.push(if cell.len() > inl {
157 cell.remove(inl)
158 } else {
159 FmtComposite::new()
160 });
161 }
162 let new_line = FmtLine::TableRow(FmtTableRow { cells: new_cells });
163 lines.insert(ir + 1, new_line);
164 self.height += 1;
165 }
166 }
167 }
168 let mut current_aligns: Vec<Alignment> = vec![Alignment::Center; nbcols];
171 for ir in self.start..self.start + self.height {
172 let line = &mut lines[ir];
173 match line {
174 FmtLine::TableRow(FmtTableRow { cells }) => {
175 for ic in 0..nbcols {
176 cells[ic].spacing = Some(Spacing {
177 width: widths[ic],
178 align: current_aligns[ic],
179 });
180 }
181 }
182 FmtLine::TableRule(rule) => {
183 if cols_removed {
184 rule.set_nbcols(nbcols);
185 }
186 if ir == self.start {
187 rule.position = RelativePosition::Top;
188 } else if ir == self.start + self.height - 1 {
189 rule.position = RelativePosition::Bottom;
190 }
191 rule.widths[..nbcols].clone_from_slice(&widths[..nbcols]);
192 current_aligns[..nbcols].clone_from_slice(&rule.aligns[..nbcols]);
193 }
194 _ => {
195 panic!("It should be a table part");
196 }
197 }
198 }
199 }
200}
201
202fn find_tables(lines: &[FmtLine<'_>]) -> Vec<Table> {
204 let mut tables: Vec<Table> = Vec::new();
205 let mut current: Option<Table> = None;
206 for (idx, line) in lines.iter().enumerate() {
207 match line {
208 FmtLine::TableRule(FmtTableRule { aligns, .. }) => match current.as_mut() {
209 Some(b) => {
210 b.height += 1;
211 b.nbcols = b.nbcols.max(aligns.len());
212 }
213 None => {
214 current = Some(Table {
215 start: idx,
216 height: 1,
217 nbcols: aligns.len(),
218 });
219 }
220 },
221 FmtLine::TableRow(FmtTableRow { cells }) => match current.as_mut() {
222 Some(b) => {
223 b.height += 1;
224 b.nbcols = b.nbcols.max(cells.len());
225 }
226 None => {
227 current = Some(Table {
228 start: idx,
229 height: 1,
230 nbcols: cells.len(),
231 });
232 }
233 },
234 _ => {
235 if let Some(c) = current.take() {
236 tables.push(c);
237 }
238 }
239 }
240 }
241 if let Some(c) = current.take() {
242 tables.push(c);
243 }
244 tables
245}
246
247pub fn fix_all_tables(lines: &mut Vec<FmtLine<'_>>, width: usize, skin: &MadSkin) {
253 for tbl in find_tables(lines).iter_mut().rev() {
254 tbl.fix_columns(lines, width, skin);
255 }
256}