Ruled lines are not table structure
TLDR
Relying exclusively on vector path intersections (LatticeReconstructor) to detect table structures fails when designers encode column boundaries using whitespace and alignment rather than drawn ink lines (e.g. balance sheet label/value columns). Executing a secondary whitespace channel scanning pass over lattice-derived table cells detects interior un-stroked columns ($\ge 16\text{px}$ wide, occupied in $\ge 25\%$ of rows), inserting synthetic grid lines to split fused cells cleanly.
| Table Extraction Engine | Ruling Lines Source | Handling of Un-stroked Value Columns | Resulting Grid Accuracy |
|---|---|---|---|
| Pure Vector Lattice | Explicit $H/V$ path ops | Fuses label + value into 1 cell | Defective (Column dropped) |
| Lattice + Whitespace Scan | Vector paths + $16\text{px}$ gaps | Splits column via synthetic line | 100% Correct Data Grid |
Technical whitespace channel split engine
// Scan lattice table cells for interior whitespace channels and insert synthetic split lines
export function splitLatticeCellsByWhitespaceChannels(tableCellGrid, minChannelWidthPx = 16) {
const rowCount = tableCellGrid.length;
for (let colIdx = 0; colIdx < tableCellGrid[0].length; colIdx++) { const columnCells = tableCellGrid.map(row => row[colIdx]);
// Find overlapping empty X-channel across all rows in the cell column const emptyChannel = findInteriorEmptyXChannel(columnCells, minChannelWidthPx); if (!emptyChannel) continue;
// Guard: Verify both sides of the empty channel contain text in >= 25% of rows const occupiedRowsCount = columnCells.filter(cell => cell.hasLeftContent(emptyChannel.xMin) && cell.hasRightContent(emptyChannel.xMax) ).length;
if (occupiedRowsCount >= Math.ceil(rowCount * 0.25)) { // Split cell column into two distinct cells at channel center insertSyntheticGridLine(tableCellGrid, colIdx, emptyChannel.centerX); } }
return tableCellGrid; }
Rule of thumb: Scan vector lattice cells for interior whitespace channels ($\ge 16\text{px}$) to split alignment-based columns that were never drawn with explicit ink lines.