Whitespace-Projection Columns (Borderless Tables)

ocr/structure detects tables only when every row’s cell-left-x matches within a tolerance — it collapses on ragged or borderless tables, right-aligned numeric columns, or any row with a missing cell. edge_lines.find_grid needs ruling lines, so a table drawn purely with whitespace has no grid at all. column_layout finds columns the robust way the layout-analysis literature uses: by the gaps. It projects the OCR boxes onto the x-axis (an ink-density profile), reads off the persistent empty vertical bands as column gutters, assigns each box a column index, and buckets rows by vertical spacing to emit a borderless table.

Pure-stdlib over plain box dicts (a difference-array projection — no numpy), so it is fully unit-testable with no image and no OCR engine. Reuses table_grid_fill’s box-bounds reader. Imports no PySide6.

Headless API

from je_auto_control import (detect_borderless_table, column_gutters,
                             assign_columns, vertical_projection)

table = detect_borderless_table(ocr_boxes)
# {"n_rows": 3, "n_cols": 2, "rows": [["Name","Age"],["Ann","30"],["Bob","25"]],
#  "columns": [{"start": 70, "end": 120, "width": 50}]}

gutters = column_gutters(ocr_boxes, min_gap=8)   # empty vertical bands
tagged = assign_columns(ocr_boxes)               # each box + "column" index
profile = vertical_projection(ocr_boxes)         # ink density per x

vertical_projection returns the per-x ink-density profile; column_gutters returns the interior empty bands [{start, end, width}] at least min_gap wide; assign_columns tags every box with a 0-based column; detect_borderless_table combines columns (from gutters) with rows (from vertical spacing) into {n_rows, n_cols, rows, columns}, or None when fewer than min_cols columns / min_rows rows are found. Boxes accept {x, y, width, height} or {left, top, right, bottom} plus an optional text.

Executor commands

AC_detect_borderless_table (boxes / page_width / min_gap / min_cols / min_rows{found, table}) and AC_column_gutters (boxes / page_width / min_gap{count, gutters}). They are exposed as the MCP tools ac_detect_borderless_table / ac_column_gutters (read-only) and as the Script Builder commands Detect Borderless Table / Column Gutters (whitespace) under OCR.