docling-project / docling-project/docling

Add configurable page padding to improve table detection for edge-to-edge tables

Open
#2,965 1 comment 1 reaction 0 assignees View on GitHub
enhancement triage/close-stale
Dominant language
Python
Stars
66.4k
Forks
4.8k
Avg merge
2d 21h
Merged PRs (30d)
84

Description

The LayoutModel (Heron/Egret) fails to detect tables that extend to or near the page edges. When there is insufficient margin between a table boundary and the page boundary, the model cannot distinguish them. TableFormer never receives the table region, and structured table content is extracted as unstructured text -- or lost entirely.

This is not a rare edge case. Many real-world documents use the full page width (or smal margin) for tables.

## The fix is simple: padding works

We tested multiple PDFs with and without external L+R padding (40pt / ~14mm white borders added left and right via pypdf before sending to Docling). Results:

| Document type | Tables (no padding) | Tables (with padding) | Effect |
|---------------|--------------------|-----------------------|--------|
| Full-page table | 1 | **2** | Missed table now detected |
| Dense form with edge tables | partial | **complete** | Edge table fully captured (715 tok vs 19 tok fragment) |
| Table-heavy, good margins | identical | identical | No effect |
| Table-heavy, centered | identical | identical | No effect |
| Text-only | 0 | 0 | No effect (identical) |

Adding padding to page images **recovers tables that LayoutModel completely misses**. A 715-token table was reduced to a 19-token fragment without padding -- 97% data loss for that section. No amount of downstream processing can fix a table that LayoutModel never detected.

However, our external workaround re-encodes the entire PDF via pypdf, which changes the internal document structure. This caused regressions in some documents (complete page data loss, table detection downgrades) and adds 44-100% processing time.

**A native Docling solution at the image level would get the benefits without the regressions**, because it only adds white pixels to the rendered page image -- no PDF structure changes, no re-encoding overhead. Bounding box coordinates would need to be adjusted for the padding offset in downstream models, but this is straightforward with known padding values.

## The infrastructure already exists (but was never completed)

**TFPredictor reads padding config but never applies it:**

`docling-ibm-models/tableformer/data_management/tf_predictor.py`:
```python
# Lines 117-118: Config is READ but never USED
self._padding = config["predict"].get("padding", False) # Always False
self._padding_size = config["predict"].get("padding_size", 10) # Never used
```

The `tm_config.json` already contains `"padding": false, "padding_size": 50`. There is also:
- `_depad_bboxes()` method (lines 324-376) for removing padding from bounding boxes -- **exists but is never called**
- `_prepare_image()` (lines 995-1022) squashes images to 448x448 **ignoring `self._padding` entirely**
- Dataset config has `"padding_mode": "null"` and `"padding_color": [0, 0, 0]`

The infrastructure was designed (config reading, bbox depadding, dataset padding config) but the actual image padding step was never implemented.

The experimental `TableCropsLayoutModel` (PR #2669) treats the entire page as a single table -- useful for pre-cropped table images, but not applicable to multi-element pages.

## Proposed solution: configurable per-side page padding

Add a `page_padding` option to `PdfPipelineOptions` that adds white padding to rendered page images before layout detection:

```python
page_padding: Annotated[
Union[int, tuple[int, int], tuple[int, int, int, int]],
Field(
description="Padding in pixels added around page images before layout detection. "
"Helps detect tables that extend to page edges. "
"int: uniform all sides. "
"tuple(h, w): vertical/horizontal. "
"tuple(top, right, bottom, left): per-side. "
"0 = disabled.",
),
] = 0
```

Examples:
- `page_padding=40` -- uniform 40px all sides
- `page_padding=(0, 40)` -- L+R only (our tested configuration)
- `page_padding=(10, 40, 10, 40)` -- individual per-side control

Best insertion point: `LayoutModel.predict_layout()` before line 170 -- targeted to layout detection only, no side effects on OCR or other models. Padding offsets need to be subtracted from predicted bounding boxes before passing to downstream models.

## What padding does NOT help with

- Form-style layouts where fields are not grouped into tables -- `pipeline: vlm` is the better approach
- Documents where tables already have sufficient margins (no change, as expected)
- Text-only documents (identical output, as expected)

## External workaround (current)

```python
from pypdf import PdfReader, PdfWriter
import io

def add_padding_to_pdf(pdf_bytes: bytes, padding: float = 40.0) -> bytes:
reader = PdfReader(io.BytesIO(pdf_bytes))
writer = PdfWriter()
for page in reader.pages:
new_w = float(page.mediabox.width) + 2 * padding
new_h = float(page.mediabox.height) + 2 * padding
blank = writer.add_blank_page(width=new_w, height=new_h)
blank.merge_translated_page(page, tx=padding, ty=padding)
output = io.BytesIO()
writer.write(output)
return output.getvalue()
```

This proves the concept but is not production-viable due to PDF re-encoding side effects.

## Environment

Docling v1.11.0 (docling-serve), default settings (pypdfium2 backend, Heron LayoutModel, table_mode=accurate)

## Related

- #56 -- TableFormer gives different output for slight bbox padding changes (open since Dec 2024, no response)
- #278 -- Long tables, fields being truncated
- #1306 -- Wrong detection for wide table rows
- #2073 -- Failing table structure identification
- #2203 -- Empty tables with Heron Layout Model

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.