1use rapiddoc_core::{Error, ErrorCode};
4use rapiddoc_native::grid::Table;
5pub mod quality;
6pub mod structure;
7pub mod wired;
8
9pub fn escape_html(text: &str) -> String {
11 let mut result = String::with_capacity(text.len());
12 for value in text.chars() {
13 match value {
14 '&' => result.push_str("&"),
15 '<' => result.push_str("<"),
16 '>' => result.push_str(">"),
17 '"' => result.push_str("""),
18 '\'' => result.push_str("'"),
19 _ => result.push(value),
20 }
21 }
22 result
23}
24
25pub fn table_html(table: &Table, max_cells: usize) -> Result<String, Error> {
27 let max_cells = rapiddoc_core::resources::limit(max_cells);
28 let count = table
29 .rows
30 .checked_mul(table.columns)
31 .ok_or_else(|| invalid("表格尺寸溢出"))?;
32 if count == 0 || count > max_cells {
33 return Err(invalid("表格为空或超过输出预算"));
34 }
35 let mut owner = vec![false; count];
36 let mut starts = vec![None; count];
37 for (index, cell) in table.cells.iter().enumerate() {
38 let last_row = cell
39 .row
40 .checked_add(cell.row_span)
41 .ok_or_else(|| invalid("单元格跨度溢出"))?;
42 let last_column = cell
43 .column
44 .checked_add(cell.column_span)
45 .ok_or_else(|| invalid("单元格跨度溢出"))?;
46 if cell.row_span == 0
47 || cell.column_span == 0
48 || last_row > table.rows
49 || last_column > table.columns
50 {
51 return Err(invalid("单元格跨度越界"));
52 }
53 for row in cell.row..last_row {
54 for column in cell.column..last_column {
55 let slot = &mut owner[row * table.columns + column];
56 if *slot {
57 return Err(invalid("单元格重复覆盖"));
58 }
59 *slot = true;
60 }
61 }
62 starts[cell.row * table.columns + cell.column] = Some(index);
63 }
64 if owner.iter().any(|value| !*value) {
65 return Err(invalid("表格存在未覆盖位置"));
66 }
67 let mut html = String::from("<table>\n");
68 for row in 0..table.rows {
69 html.push_str("<tr>");
70 for column in 0..table.columns {
71 if let Some(index) = starts[row * table.columns + column] {
72 let cell = &table.cells[index];
73 html.push_str("<td");
74 if cell.row_span > 1 {
75 html.push_str(&format!(" rowspan=\"{}\"", cell.row_span));
76 }
77 if cell.column_span > 1 {
78 html.push_str(&format!(" colspan=\"{}\"", cell.column_span));
79 }
80 html.push('>');
81 let value = escape_html(&cell.text)
82 .replace("\r\n", "\n")
83 .replace('\n', "<br/>");
84 html.push_str(&value);
85 html.push_str("</td>");
86 }
87 }
88 html.push_str("</tr>\n");
89 }
90 html.push_str("</table>");
91 Ok(html)
92}
93
94fn invalid(message: &str) -> Error {
96 Error {
97 code: ErrorCode::InvalidInput,
98 message: message.into(),
99 }
100}