Pier-Jean's picture
Initial deploy: Docling Studio (local mode, port 7860)
5539271
"""Tests for bbox coordinate normalization.
These tests cover the core bbox pipeline — the most critical part of the
visual rendering. Every edge case matters because a broken bbox means
misaligned overlays in the UI.
"""
import pytest
from docling_core.types.doc.base import BoundingBox, CoordOrigin
from infra.bbox import EMPTY_BBOX, to_topleft_list
# ---------------------------------------------------------------------------
# Standard conversions
# ---------------------------------------------------------------------------
class TestToTopleftListStandard:
"""Normal bbox conversions (happy path)."""
def test_topleft_origin_unchanged(self):
"""TOPLEFT bbox should pass through unchanged."""
bbox = BoundingBox(l=10, t=20, r=100, b=80, coord_origin=CoordOrigin.TOPLEFT)
result = to_topleft_list(bbox, page_height=792.0)
assert result == [10, 20, 100, 80]
def test_bottomleft_origin_converted(self):
"""BOTTOMLEFT bbox should have y-coordinates flipped."""
bbox = BoundingBox(l=50, t=700, r=200, b=600, coord_origin=CoordOrigin.BOTTOMLEFT)
result = to_topleft_list(bbox, page_height=792.0)
# After conversion: new_t = 792 - 700 = 92, new_b = 792 - 600 = 192
assert result[0] == 50 # l unchanged
assert result[1] == pytest.approx(92.0) # t = page_height - old_t
assert result[2] == 200 # r unchanged
assert result[3] == pytest.approx(192.0) # b = page_height - old_b
def test_result_has_positive_dimensions(self):
"""Converted bbox should always have b > t (positive height)."""
bbox = BoundingBox(l=10, t=500, r=300, b=100, coord_origin=CoordOrigin.BOTTOMLEFT)
result = to_topleft_list(bbox, page_height=800.0)
left, t, r, b = result
assert r > left, "width should be positive"
assert b > t, "height should be positive"
def test_full_page_bbox_bottomleft(self):
"""A bbox covering the full page in BOTTOMLEFT origin."""
bbox = BoundingBox(l=0, t=792, r=612, b=0, coord_origin=CoordOrigin.BOTTOMLEFT)
result = to_topleft_list(bbox, page_height=792.0)
assert result == [0, 0, 612, 792]
def test_full_page_bbox_topleft(self):
"""A bbox covering the full page in TOPLEFT origin."""
bbox = BoundingBox(l=0, t=0, r=612, b=792, coord_origin=CoordOrigin.TOPLEFT)
result = to_topleft_list(bbox, page_height=792.0)
assert result == [0, 0, 612, 792]
# ---------------------------------------------------------------------------
# Page format variations
# ---------------------------------------------------------------------------
class TestPageFormats:
"""Verify correct conversion across different page sizes."""
def test_a4_page(self):
"""A4 page (595.28 x 841.89 pt) -- most common non-US format."""
page_height = 841.89
bbox = BoundingBox(l=72, t=769.89, r=523.28, b=72, coord_origin=CoordOrigin.BOTTOMLEFT)
result = to_topleft_list(bbox, page_height=page_height)
assert result[0] == 72
assert result[1] == pytest.approx(page_height - 769.89) # ~72
assert result[2] == 523.28
assert result[3] == pytest.approx(page_height - 72) # ~769.89
def test_a3_page(self):
"""A3 page (841.89 x 1190.55 pt)."""
page_height = 1190.55
bbox = BoundingBox(l=0, t=1190.55, r=841.89, b=0, coord_origin=CoordOrigin.BOTTOMLEFT)
result = to_topleft_list(bbox, page_height=page_height)
assert result == pytest.approx([0, 0, 841.89, 1190.55])
def test_legal_page(self):
"""US Legal page (612 x 1008 pt)."""
page_height = 1008.0
bbox = BoundingBox(l=50, t=50, r=562, b=958, coord_origin=CoordOrigin.TOPLEFT)
result = to_topleft_list(bbox, page_height=page_height)
assert result == [50, 50, 562, 958]
def test_landscape_page(self):
"""Landscape orientation (width > height)."""
page_height = 612.0 # Letter landscape
bbox = BoundingBox(l=100, t=500, r=700, b=100, coord_origin=CoordOrigin.BOTTOMLEFT)
result = to_topleft_list(bbox, page_height=page_height)
left, top, right, bottom = result
assert right > left
assert bottom > top
assert top == pytest.approx(612.0 - 500.0) # 112
assert bottom == pytest.approx(612.0 - 100.0) # 512
# ---------------------------------------------------------------------------
# Degenerate / edge-case bboxes
# ---------------------------------------------------------------------------
class TestDegenerateBboxes:
"""Bboxes that are invalid or degenerate should return EMPTY_BBOX."""
def test_zero_width_returns_empty(self):
"""A bbox with l == r (zero width) is degenerate."""
bbox = BoundingBox(l=100, t=20, r=100, b=80, coord_origin=CoordOrigin.TOPLEFT)
result = to_topleft_list(bbox, page_height=792.0)
assert result == EMPTY_BBOX
def test_zero_height_returns_empty(self):
"""A bbox with t == b (zero height) is degenerate."""
bbox = BoundingBox(l=10, t=50, r=100, b=50, coord_origin=CoordOrigin.TOPLEFT)
result = to_topleft_list(bbox, page_height=792.0)
assert result == EMPTY_BBOX
def test_inverted_lr_returns_empty(self):
"""A bbox where l > r (inverted x) is degenerate."""
bbox = BoundingBox(l=200, t=20, r=100, b=80, coord_origin=CoordOrigin.TOPLEFT)
result = to_topleft_list(bbox, page_height=792.0)
assert result == EMPTY_BBOX
def test_inverted_tb_topleft_returns_empty(self):
"""A TOPLEFT bbox where t > b (inverted y) is degenerate."""
bbox = BoundingBox(l=10, t=100, r=200, b=50, coord_origin=CoordOrigin.TOPLEFT)
result = to_topleft_list(bbox, page_height=792.0)
assert result == EMPTY_BBOX
def test_point_bbox_returns_empty(self):
"""A zero-area point bbox (l==r, t==b) is degenerate."""
bbox = BoundingBox(l=100, t=200, r=100, b=200, coord_origin=CoordOrigin.TOPLEFT)
result = to_topleft_list(bbox, page_height=792.0)
assert result == EMPTY_BBOX
def test_empty_bbox_is_not_mutated(self):
"""Each call returns a fresh list — no shared mutable state."""
bbox = BoundingBox(l=100, t=20, r=100, b=80, coord_origin=CoordOrigin.TOPLEFT)
result1 = to_topleft_list(bbox, page_height=792.0)
result2 = to_topleft_list(bbox, page_height=792.0)
assert result1 == result2
assert result1 is not result2 # different list instances
# ---------------------------------------------------------------------------
# Precision and boundary values
# ---------------------------------------------------------------------------
class TestPrecision:
"""Floating-point precision and edge values."""
def test_very_small_bbox(self):
"""A tiny but valid bbox (e.g. a period character)."""
bbox = BoundingBox(l=100.0, t=200.0, r=100.5, b=200.5, coord_origin=CoordOrigin.TOPLEFT)
result = to_topleft_list(bbox, page_height=792.0)
assert result == [100.0, 200.0, 100.5, 200.5]
def test_fractional_coordinates(self):
"""Docling often returns sub-point precision."""
bbox = BoundingBox(l=72.34, t=145.67, r=540.12, b=200.89, coord_origin=CoordOrigin.TOPLEFT)
result = to_topleft_list(bbox, page_height=842.0)
assert result == pytest.approx([72.34, 145.67, 540.12, 200.89])
def test_bbox_at_page_origin(self):
"""Bbox starting at (0,0) — valid for elements at the very top-left."""
bbox = BoundingBox(l=0, t=0, r=50, b=30, coord_origin=CoordOrigin.TOPLEFT)
result = to_topleft_list(bbox, page_height=792.0)
assert result == [0, 0, 50, 30]
def test_bbox_at_page_bottom_right(self):
"""Bbox at the very bottom-right corner of the page."""
bbox = BoundingBox(l=500, t=750, r=612, b=792, coord_origin=CoordOrigin.TOPLEFT)
result = to_topleft_list(bbox, page_height=792.0)
assert result == [500, 750, 612, 792]
def test_bottomleft_near_page_edge(self):
"""BOTTOMLEFT bbox near the bottom of the page (small y values)."""
bbox = BoundingBox(l=50, t=30, r=200, b=10, coord_origin=CoordOrigin.BOTTOMLEFT)
result = to_topleft_list(bbox, page_height=792.0)
# Converted: top = 792-30 = 762, bottom = 792-10 = 782
assert result[1] == pytest.approx(762.0)
assert result[3] == pytest.approx(782.0)