from ingestion.extract.glyph_order import find_reading_order_issues, is_reversed_order def test_normal_ltr_span_not_flagged(): # ordinary increasing x-origins, as any normal left-to-right span has assert not is_reversed_order([264.7, 269.4, 271.6, 276.3, 278.5]) def test_confirmed_page_1373_defect_shape_is_flagged(): # exact x-origins read via get_text("rawdict") from physical page 1373's # affected span (" tịx 4 =" reversed) — see docs/pdf-parsing-outlier-catalog.md item 9 x_origins = [66.32, 64.17, 61.53, 58.89, 54.14, 51.98, 47.23] assert is_reversed_order(x_origins) def test_single_char_span_not_flagged(): assert not is_reversed_order([100.0]) def test_empty_span_not_flagged(): assert not is_reversed_order([]) def test_tied_x_origins_not_flagged_as_reversed(): # equal x-origins (e.g. stacked/overlapping glyphs) are not "decreasing" assert not is_reversed_order([100.0, 100.0, 100.0]) def test_correctly_ordered_row_not_flagged(): row = {(20, 550.9): [(518.0, "n"), (525.2, "h"), (532.6, "i"), (536.8, "e")]} assert find_reading_order_issues(row) == [] def test_confirmed_page_714_row_misorder_is_flagged(): # reproduces the real page-714 finding: within one PyMuPDF block (20), # 4 line fragments are emitted out of x-order ("quản ", " ộ", "đ tệih", # "n " concatenated) that reconstruct correctly ("...nhiệt độ") when # re-sorted by x-origin — see outlier catalog item 9. row = { (20, 550.9): [ (518.06, "n"), (525.20, " "), (546.59, " "), (553.71, "ộ"), (541.84, "đ"), (539.45, " "), (536.81, "t"), (532.59, "ệ"), (529.95, "i"), (525.20, "h"), ] } issues = find_reading_order_issues(row) assert len(issues) == 1 assert issues[0].extracted_text != issues[0].corrected_text def test_different_blocks_at_same_y_not_merged(): # regression test for a real false positive: two DIFFERENT paragraphs in # different PyMuPDF blocks (a right-column paragraph starting at x=299.4 # and a left-column paragraph starting at x=35.4, page 1104) coincide at # the same y — grouping by block index (not a hand-picked x-coordinate # column boundary) is what keeps them from being merged into one "row". # This is the caller's responsibility (scan_reading_order groups by real # PyMuPDF block index); find_reading_order_issues just trusts its input # is already correctly grouped, which these two dict entries demonstrate. row_block_1 = {(1, 70.4): [(299.39, "m"), (306.78, "ô")]} row_block_4 = {(4, 70.4): [(35.43, "d"), (40.18, "e")]} assert find_reading_order_issues(row_block_1) == [] assert find_reading_order_issues(row_block_4) == [] def test_kerning_jitter_not_flagged_as_reading_order_defect(): # regression test for a real false positive found by running against the # actual PDF: "mefloquin" ('l' at x=491.566, 'o' at x=491.471 — a # 0.095pt kerning-driven dip) was previously "corrected" into the wrong # word "mefolquin". A row-level check with no decrease tolerance treats # ordinary kerning as a defect and corrupts already-correct text. row = { (5, 449.7): [ (474.865, "m"), (482.161, "e"), (486.284, "f"), (491.566, "l"), (491.471, "o"), (496.126, "q"), (500.781, "u"), (505.436, "i"), (507.982, "n"), ] } assert find_reading_order_issues(row) == []