{"record":{"id":"7cd0524848f830a7","repo":"docling-project/docling","slug":"expected-coordinate-origin-to-be-coordorigin-topl","errorCode":null,"errorMessage":"expected coordinate origin to be {CoordOrigin.TOPLEFT.value}","messagePattern":"expected coordinate origin to be (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"docling/utils/ocr_utils.py","lineNumber":59,"sourceCode":"    im_size: Tuple[int, int],\n) -> BoundingRectangle:\n    # box is in the top, left, height, width format, top left coordinates\n    rect = rotate_bounding_box(bbox, angle=orientation, im_size=im_size)\n    rect = BoundingRectangle(\n        r_x0=rect.r_x0 / scale,\n        r_y0=rect.r_y0 / scale,\n        r_x1=rect.r_x1 / scale,\n        r_y1=rect.r_y1 / scale,\n        r_x2=rect.r_x2 / scale,\n        r_y2=rect.r_y2 / scale,\n        r_x3=rect.r_x3 / scale,\n        r_y3=rect.r_y3 / scale,\n        coord_origin=CoordOrigin.TOPLEFT,\n    )\n    if original_offset is not None:\n        if original_offset.coord_origin is not CoordOrigin.TOPLEFT:\n            msg = f\"expected coordinate origin to be {CoordOrigin.TOPLEFT.value}\"\n            raise ValueError(msg)\n        if original_offset is not None:\n            rect.r_x0 += original_offset.l\n            rect.r_x1 += original_offset.l\n            rect.r_x2 += original_offset.l\n            rect.r_x3 += original_offset.l\n            rect.r_y0 += original_offset.t\n            rect.r_y1 += original_offset.t\n            rect.r_y2 += original_offset.t\n            rect.r_y3 += original_offset.t\n    return rect\n","sourceCodeStart":41,"sourceCodeEnd":70,"githubUrl":"https://github.com/docling-project/docling/blob/61d76f1ff3f8428065465889f7b4577da7df704c/docling/utils/ocr_utils.py#L41-L70","documentation":"Raised by tesseract_box_to_bounding_rectangle() in docling/utils/ocr_utils.py when an original_offset BoundingBox is supplied whose coord_origin is not CoordOrigin.TOPLEFT. The function offsets Tesseract rectangle coordinates by simply adding offset.l and offset.t, which is only mathematically correct for top-left origin coordinates, so any other origin is rejected to avoid silently producing wrong geometry.","triggerScenarios":"Calling tesseract_box_to_bounding_rectangle(bbox, original_offset=offset, ...) where offset.coord_origin is CoordOrigin.BOTTOMLEFT (or anything other than TOPLEFT). This typically happens when the crop/offset box was created by code that assumes a bottom-left coordinate system, such as PDF-native or matplotlib-style coordinates.","commonSituations":"Mixing BoundingBox instances from different pipelines: a PDF backend (bottom-left origin) feeding an OCR crop step that expects image coordinates; converting page crops produced by another library that defaults to bottom-left; recent refactors that changed the coord_origin field of offset boxes.","solutions":["Convert the offset box to a top-left origin before the call using the BoundingBox's own conversion API (to_top_left_origin() / from relative image height), then pass it as original_offset.","Construct the offset box explicitly with coord_origin=CoordOrigin.TOPLEFT at creation time.","Pass original_offset=None and apply the offset yourself after checking/normalizing the origin."],"exampleFix":"# before\nrect = tesseract_box_to_bounding_rectangle(\n    bbox, original_offset=crop_box, scale=s, orientation=0, im_size=size\n)  # ValueError if crop_box is BOTTOMLEFT\n\n# after\nif crop_box.coord_origin is not CoordOrigin.TOPLEFT:\n    crop_box = crop_box.to_top_left_origin(page_height)\nrect = tesseract_box_to_bounding_rectangle(\n    bbox, original_offset=crop_box, scale=s, orientation=0, im_size=size\n)","handlingStrategy":"validation","validationCode":"from docling.datamodel.base_models import CoordOrigin\n\nif original_offset is not None and original_offset.coord_origin is not CoordOrigin.TOPLEFT:\n    original_offset = original_offset.to_top_left_origin(image_height)\nrect = tesseract_box_to_bounding_rectangle(bbox, original_offset=original_offset, scale=scale, orientation=orientation, im_size=im_size)","typeGuard":"from docling.datamodel.base_models import BoundingBox, CoordOrigin\n\ndef is_top_left(box: BoundingBox) -> bool:\n    return box.coord_origin is CoordOrigin.TOPLEFT","tryCatchPattern":"try:\n    rect = tesseract_box_to_bounding_rectangle(...)\nexcept ValueError as exc:\n    raise ValueError(f\"offset box origin invalid: {exc}\") from exc  # surface with context","preventionTips":["Always construct crop/offset boxes with coord_origin=CoordOrigin.TOPLEFT for image-space OCR work.","At the boundary between PDF (bottom-left) and image (top-left) coordinates, convert explicitly before handing boxes to OCR utilities.","Add an assert/inspection in tests that offset boxes passed to OCR utilities are TOPLEFT."],"tags":["ocr","coordinates","bounding-box","validation"],"backgroundTag":null,"analyzedSha":"61d76f1ff3f8428065465889f7b4577da7df704c","analyzedAt":"2026-08-14T23:53:18.727Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}