MCPcopy Create free account
hub / github.com/917Dhj/DeepPaperNote / _cluster_lines_into_rows

Function _cluster_lines_into_rows

scripts/extract_pdf_assets.py:693–742  ·  view source on GitHub ↗

Cluster sibling text lines that share roughly the same vertical band. PDFs created by LaTeX often emit one PyMuPDF "line" per cell, so a single visual row of a table is split into many independent line records. We merge lines whose ``y0`` falls within ``y_tolerance`` points of the row

(
    lines: list[dict], *, y_tolerance: float = 2.0
)

Source from the content-addressed store, hash-verified

691
692
693def _cluster_lines_into_rows(
694 lines: list[dict], *, y_tolerance: float = 2.0
695) -> list[dict]:
696 """Cluster sibling text lines that share roughly the same vertical band.
697
698 PDFs created by LaTeX often emit one PyMuPDF "line" per cell, so a single
699 visual row of a table is split into many independent line records. We
700 merge lines whose ``y0`` falls within ``y_tolerance`` points of the row
701 seed so that downstream heuristics can reason about a true logical row.
702
703 Each output record::
704
705 {
706 "bbox": (x0, y0, x1, y1), # union of all member bboxes
707 "tokens": [str, ...], # text content of each member, left-to-right
708 "text": str, # tokens joined by single spaces
709 "members": [dict, ...], # original line records inside the row
710 }
711 """
712 rows: list[dict] = []
713 sorted_lines = sorted(lines, key=lambda r: (r["bbox"][1], r["bbox"][0]))
714 for line in sorted_lines:
715 bx0, by0, bx1, by1 = line["bbox"]
716 placed = False
717 for row in rows:
718 rx0, ry0, rx1, ry1 = row["bbox"]
719 row_mid = (ry0 + ry1) / 2.0
720 line_mid = (by0 + by1) / 2.0
721 if abs(line_mid - row_mid) <= y_tolerance:
722 row["bbox"] = (
723 min(rx0, bx0),
724 min(ry0, by0),
725 max(rx1, bx1),
726 max(ry1, by1),
727 )
728 row["members"].append(line)
729 placed = True
730 break
731 if not placed:
732 rows.append({
733 "bbox": (bx0, by0, bx1, by1),
734 "members": [line],
735 })
736
737 for row in rows:
738 row["members"].sort(key=lambda m: m["bbox"][0])
739 row["tokens"] = [m["text"] for m in row["members"]]
740 row["text"] = " ".join(row["tokens"])
741 rows.sort(key=lambda r: r["bbox"][1])
742 return rows
743
744
745def _row_is_table_like(row: dict) -> bool:

Callers 1

Calls

no outgoing calls

Tested by

no test coverage detected