Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e6bb75ee3f |
@@ -110,15 +110,21 @@ pub fn detect_tables_from_lines(items: &[TextItem], lines: &[PdfLine], page: u32
|
|||||||
return Vec::new();
|
return Vec::new();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reject page-spanning frames: if the grid covers >90% of a standard page
|
// Reject page-spanning frames: a decorative outer border has just 4
|
||||||
// dimension in both axes, it's a border frame, not a table.
|
// edges (top/bottom/left/right). Real full-page tables — common in
|
||||||
|
// governmental ledgers, financial reports, etc. — span the same A4 /
|
||||||
|
// Letter dimensions but have many internal row/column rules. Only
|
||||||
|
// reject when the line set looks like a bare frame, not a grid.
|
||||||
// Standard pages are ~595×842 (A4) or ~612×792 (Letter).
|
// Standard pages are ~595×842 (A4) or ~612×792 (Letter).
|
||||||
if table_width > 500.0 && table_height > 700.0 {
|
if table_width > 500.0 && table_height > 700.0 && horizontals.len() <= 4 && verticals.len() <= 4
|
||||||
|
{
|
||||||
log::debug!(
|
log::debug!(
|
||||||
"detect_lines p{}: rejected — page-spanning frame ({:.0}×{:.0})",
|
"detect_lines p{}: rejected — page-spanning frame ({:.0}×{:.0}, {} h + {} v)",
|
||||||
page,
|
page,
|
||||||
table_width,
|
table_width,
|
||||||
table_height
|
table_height,
|
||||||
|
horizontals.len(),
|
||||||
|
verticals.len()
|
||||||
);
|
);
|
||||||
return Vec::new();
|
return Vec::new();
|
||||||
}
|
}
|
||||||
@@ -410,6 +416,81 @@ mod tests {
|
|||||||
assert!(tables.is_empty());
|
assert!(tables.is_empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_page_spanning_bare_frame_rejected() {
|
||||||
|
// Just an outer A4-sized rectangle: 2 horizontals + 2 verticals.
|
||||||
|
// No internal structure → decorative border, not a table.
|
||||||
|
let lines = vec![
|
||||||
|
make_hline(20.0, 20.0, 575.0, 1), // top
|
||||||
|
make_hline(820.0, 20.0, 575.0, 1), // bottom
|
||||||
|
make_vline(20.0, 20.0, 820.0, 1), // left
|
||||||
|
make_vline(575.0, 20.0, 820.0, 1), // right
|
||||||
|
];
|
||||||
|
let items = vec![
|
||||||
|
make_item("title", 100.0, 100.0, 1),
|
||||||
|
make_item("body", 100.0, 200.0, 1),
|
||||||
|
];
|
||||||
|
let tables = detect_tables_from_lines(&items, &lines, 1);
|
||||||
|
assert!(
|
||||||
|
tables.is_empty(),
|
||||||
|
"Page-sized 4-edge frame should be rejected as decoration"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_page_spanning_grid_with_internal_lines_accepted() {
|
||||||
|
// Full-page table on an A4-sized layout (width > 500, height > 700)
|
||||||
|
// that previously hit the "page-spanning frame" early reject before
|
||||||
|
// downstream validation could even look at it. The line set has
|
||||||
|
// many internal horizontal + vertical rules, so the bare-frame
|
||||||
|
// guard should let it through.
|
||||||
|
let mut lines = Vec::new();
|
||||||
|
// 13 horizontal rules across a 540pt-wide span. Row heights vary
|
||||||
|
// a touch so the chart-gridline rejector (CV < 0.02) doesn't fire.
|
||||||
|
let x_left = 20.0_f32;
|
||||||
|
let x_right = 560.0_f32;
|
||||||
|
let h_ys: Vec<f32> = [
|
||||||
|
30.0, 95.0, 155.0, 220.0, 280.0, 345.0, 410.0, 470.0, 535.0, 600.0, 660.0, 720.0, 780.0,
|
||||||
|
]
|
||||||
|
.to_vec();
|
||||||
|
for &y in &h_ys {
|
||||||
|
lines.push(make_hline(y, x_left, x_right, 1));
|
||||||
|
}
|
||||||
|
// 7 column dividers spanning full table height (>700pt span).
|
||||||
|
let v_xs = [20.0, 95.0, 175.0, 250.0, 340.0, 450.0, 560.0];
|
||||||
|
let y_top = *h_ys.first().unwrap();
|
||||||
|
let y_bot = *h_ys.last().unwrap();
|
||||||
|
for &x in &v_xs {
|
||||||
|
lines.push(make_vline(x, y_top, y_bot, 1));
|
||||||
|
}
|
||||||
|
// Populate every cell so the capture-ratio + density checks pass.
|
||||||
|
let mut items = Vec::new();
|
||||||
|
for r in 0..(h_ys.len() - 1) {
|
||||||
|
let row_y = (h_ys[r] + h_ys[r + 1]) / 2.0;
|
||||||
|
for c in 0..(v_xs.len() - 1) {
|
||||||
|
let col_x = (v_xs[c] + v_xs[c + 1]) / 2.0;
|
||||||
|
items.push(make_item("x", col_x, row_y, 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let tables = detect_tables_from_lines(&items, &lines, 1);
|
||||||
|
assert_eq!(
|
||||||
|
tables.len(),
|
||||||
|
1,
|
||||||
|
"Full-page table with internal grid should be accepted"
|
||||||
|
);
|
||||||
|
let t = &tables[0];
|
||||||
|
assert!(
|
||||||
|
t.cells.len() >= 6,
|
||||||
|
"expected ≥6 rows, got {}",
|
||||||
|
t.cells.len()
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
t.cells[0].len() >= 3,
|
||||||
|
"expected ≥3 columns, got {}",
|
||||||
|
t.cells[0].len()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_single_column_rejected() {
|
fn test_single_column_rejected() {
|
||||||
// Only 2 col edges (1 column) — not a table even with verticals
|
// Only 2 col edges (1 column) — not a table even with verticals
|
||||||
|
|||||||
Reference in New Issue
Block a user