"""Mappatura span→box: il box porta il testo originale che copre. Serve al mascheramento per-carattere del PDF (il box deve conoscere il testo da mascherare). Test puro, senza PDF reale (geometria sintetica).""" from __future__ import annotations from core.contracts import LayerPriority, Span from core.contracts.extraction import PageGeometry, WordBox from pdf.mapping import map_spans_to_boxes def _word(text, x0, y0, x1, y1, cs, ce): return WordBox(text=text, page=0, x0=x0, y0=y0, x1=x1, y1=y1, char_start=cs, char_end=ce) def _span(start, end, text, etype="PERSONA"): return Span(start, end, text, etype, 0.95, LayerPriority.RULES) def _boxes(page, spans): return map_spans_to_boxes( spans, (page,), severity_of=lambda t: "alta", label_of=lambda t: f"[{t}]", ) def test_box_carries_covered_text_single_row(): # "Mario Rossi" su una riga → un box col testo originale coperto. page = PageGeometry(page=0, width=600, height=800, words=( _word("Mario", 72, 100, 120, 112, 0, 5), _word("Rossi", 124, 100, 175, 112, 6, 11), )) boxes = _boxes(page, [_span(0, 11, "Mario Rossi")]) assert len(boxes) == 1 assert boxes[0].text == "Mario Rossi" def test_box_text_splits_per_row_for_multiline_entity(): # Entità a capo: due box, ciascuno col testo della propria riga. page = PageGeometry(page=0, width=600, height=800, words=( _word("Mario", 72, 100, 120, 112, 0, 5), _word("Rossi", 72, 130, 123, 142, 6, 11), # riga sotto )) boxes = _boxes(page, [_span(0, 11, "Mario Rossi")]) texts = sorted(b.text for b in boxes) assert texts == ["Mario", "Rossi"]