PDF Spatial Filtering#
PDF spatial filtering in Docling refers to post-conversion filtering and extraction of document elements by their bounding-box location. There are no native crop or region parameters in PdfPipelineOptions — the pipeline always processes full pages. Spatial constraints must be applied after conversion by inspecting each DocItem's ProvenanceItem.
Three concepts are central to this topic:
ProvenanceItem— the per-item spatial anchor (page number + bounding box + character span)BoundingBoxspatial predicates — utility methods for region overlap and containment tests- Multi-column reading order linearization — how the pipeline converts a 2D page layout into a linear sequence before spatial filtering is applied
Related but distinct topics: PDF Page Range Selection covers page-level page_range filtering; Layout Postprocessor covers overlap resolution during layout prediction; PDF Document Pipeline covers pipeline stage orchestration.
ProvenanceItem: The Spatial Anchor#
ProvenanceItem is defined in docling_core/types/doc/common/reference.py:
| Field | Type | Description |
|---|---|---|
page_no | int | 1-indexed page number |
bbox | BoundingBox | Coordinates l, t, r, b with a coord_origin flag |
charspan | CharSpan | (start, end) character offsets within the item's text |
Every DocItem.prov is a list — allowing a single logical element (e.g., a paragraph split across columns) to carry multiple spatial regions.
During PDF conversion, ProvenanceItem objects are created in ReadingOrderModel with coordinates converted to BOTTOMLEFT origin (the PDF coordinate system), as seen in the text element assembly path.
The underlying BoundingBox model (in docling_core/types/doc/base.py) stores l, t, r, b as floats plus a coord_origin enum (TOPLEFT or BOTTOMLEFT). Consumers that need pixel coordinates must call to_top_left_origin(page_height) before computing pixel positions.
Bounding-Box Filtering Pattern#
To restrict output to a spatial region of a page, iterate over items and test each prov.bbox against a target region using BoundingBox spatial predicates :
from docling_core.types.doc.base import BoundingBox, CoordOrigin
# Define a region-of-interest in BOTTOMLEFT coordinates (matching prov.bbox origin)
roi = BoundingBox(l=100, b=400, r=500, t=700, coord_origin=CoordOrigin.BOTTOMLEFT)
for item, _ in doc.iterate_items():
for prov in item.prov:
if prov.page_no != target_page:
continue
# Keep items whose bbox overlaps the ROI, or is contained within it
overlap = prov.bbox.intersection_area_with(roi)
if overlap > 0:
print(item.label, prov.bbox)
Key BoundingBox methods for spatial filtering :
| Method | Use |
|---|---|
intersection_area_with(other) | Raw overlap area in page units |
intersection_over_self(other) | Fraction of this bbox covered by the other — useful for "fully inside" tests |
intersection_over_union(other) | IoU score for overlap threshold checks |
overlaps(other) | Boolean: any overlap at all |
get_intersection_bbox(other) | Returns the intersection box or None if disjoint |
Important: All comparisons require matching coord_origin. Mixing TOPLEFT (typical for pixel/image coordinates) and BOTTOMLEFT (PDF coordinates from prov.bbox) raises a ValueError. Call to_bottom_left_origin(page_height) or to_top_left_origin(page_height) to normalize before comparing.
Image cropping: The Page.get_image(scale, cropbox) method accepts a cropbox: BoundingBox parameter to render only a sub-region of the page image. The enrichment pipeline uses this pattern to crop a picture's bbox (with an expansion margin) before sending it to a VLM .
Multi-Column Layout Linearization#
Before spatial filtering can be applied to output items, the pipeline linearizes the 2D page layout into a 1D reading order. This is done by ReadingOrderPredictor (from docling-ibm-models) , called inside ReadingOrderModel.__call__() during cross-page assembly.
Algorithm summary (rule-based, not ML):
- Spatial indexing: An R-tree indexes all page elements. For each element, candidates above and below it are queried.
- Horizontal dilation: Each element's bbox is dilated horizontally (up to a threshold,
_horizontal_dilation_threshold_norm = 0.15) to group vertically-aligned elements that belong to the same column. - Up/down dependency map: Dilated bboxes establish "is above" / "is below" relationships, building a directed graph across all elements on the page.
- Column-aware sorting: A custom
__lt__comparator resolves ordering within the graph — elements that overlap horizontally (same column) are sorted top-to-bottom by theirbcoordinate; elements that do not overlap horizontally (different columns) are sorted left-to-right by theirlcoordinate. - Depth-first traversal: A depth-first search from graph roots produces the final linear sequence.
Known limitations with complex layouts:
- Key-value regions: When
KEY_VALUE_REGIONclusters contain multiple key-column / value-column pairs, the reading-order model linearizes them left-to-right row by row, often interleaving keys and values. There are no pipeline configuration options to override this behavior . - Form-like pages: Documents with spatial field→value relationships rely on positional alignment that is lost after linearization; the VLM pipeline (
VlmPipeline) is the recommended alternative for heavy form content . - Silent corruption: Reading-order errors produce no degraded confidence scores — all items have valid
prov.bboxvalues, making corruption detectable only by inspecting the ordering relative to spatial coordinates .
Debugging: Sort items manually by (prov.page_no, -prov.bbox.t, prov.bbox.l) (top-left origin: descending t = top of page first) to compare the pipeline's reading order against a naïve spatial sort.
Absence of Native Crop/Region Parameters#
PdfPipelineOptions has no crop_bbox, region, or spatial filter field. The only spatial constraint available at pipeline configuration time is page_range — a page-index list that filters at the page level (no sub-page granularity). There is no sub-page spatial constraint mechanism: the pipeline always runs layout detection and reading-order prediction on full page images before assembling the DoclingDocument.
Recommended workaround — post-conversion filter:
result = converter.convert("document.pdf")
doc = result.document
region = BoundingBox(l=50, b=200, r=400, t=600, coord_origin=CoordOrigin.BOTTOMLEFT)
target_page = 2
items_in_region = [
item
for item, _ in doc.iterate_items()
if any(
p.page_no == target_page and p.bbox.intersection_area_with(region) > 0
for p in item.prov
)
]
Why no native support: Spatial filtering requires a fully assembled DoclingDocument with resolved reading order and provenance — it cannot be applied at the raw PDF parsing stage without re-running layout analysis on the cropped region. No feature request to expose this as a pipeline option has been accepted upstream as of 2026-07.
Related gap — orphaned table text: OCR-extracted TextItems inside TABLE bboxes can become stranded — present in doc.texts with valid prov.bbox but absent from the markdown export. PR #3753 (recover_orphaned_table_text) implements a spatial recovery opt-in that re-emits such items using bbox-based proximity . This is a targeted application of post-conversion spatial filtering as a correctness mechanism.