Convertire PDF digitali in Markdown **perfetto per la vettorizzazione RAG**, senza revisione manuale. L'output deve essere testo pulito, strutturato in sezioni semanticamente coerenti, privo di artefatti, pronto per chunking e indicizzazione in un vector store.
**Non supportato:** PDF scansionati (immagini), PDF protetti da password.
CLI con due modalità: conversione (default, `--stem`, `--force`) e validazione (subcommand `validate`, con stems opzionali e `--detail`). Aggiunge `conversione/` a `sys.path` e delega a `_pipeline`. Uso: `python conversione/ [--stem X] [--force]` oppure `python conversione/ validate [X] [--detail]`.
Raggruppa in un unico modulo le tre responsabilità legate al PDF: `_check_deps()` verifica che `opendataloader-pdf` e Java 11+ siano disponibili; `validate_pdf(pdf_path) -> (bool, str)` controlla esistenza, dimensione e presenza di testo digitale; `convert_pdf(pdf_path, out_dir) -> Path` invoca opendataloader-pdf con i parametri RAG-ottimali e restituisce il percorso del `.md` prodotto. Auto-rileva i PDF taggati (Word/InDesign) per attivare `use_struct_tree`.
Contiene `apply_transforms(text) -> (text, stats)` che chiama ~35 trasformazioni atomiche (`_t_*`) in ordine semantico fisso — **non modificare l'ordine senza capire le dipendenze tra gruppi**. Ogni `_t_*` vive nel modulo del suo gruppo; le costanti regex compilate stanno in `_constants.py`.
Flag automatico: se il testo contiene "Esercizi/Problems/Homework", `_t_numbered_sections` non converte `- N. testo` in header (sono numerazioni di esercizi, non titoli).
### `_pipeline/structure.py` — analisi struttura
`analyze(md_path) -> dict` conta `#`/`##`/`###`, rileva lingua (it/en/fr/de/es), sceglie `strategia_chunking`:
| Strategia | Condizione |
|-----------|------------|
| `h3_aware` | ≥5 `###` |
| `h2_paragraph_split` | ≥3 `##`, pochi `###` |
| `paragraph` | struttura rada |
| `sliding_window` | testo piatto |
### `_pipeline/report.py` — metriche qualità
`build_report()` genera `report.json` con: statistiche trasformazioni, struttura, distribuzione lunghezze sezioni (`min`/`p25`/`mediana`/`p75`/`max`), anomalie (bare headers, sezioni corte/lunghe), residui con esempi (backtick, dot-leader, URL, `<br>`, simboli encoding, formule inline, footnote, PUA).
### `validate.py` — scoring
Assegna un voto 0–100 (A/B/C/D/F) leggendo `report.json`. Penalità principali:
| Problema | Penalità | Cap |
|----------|----------|-----|
| Struttura assente (livello 0) | −40 | — |
| Struttura piatta (livello 1) | −15 | — |
| Backtick residui | −2/cad | −20 |
| Caratteri PUA font Symbol | −2/cad | −20 |
| Dot-leader | −5/cad | −10 |
| URL/watermark | −5/cad | −15 |
| `<br>` inline | −2/cad | −15 |
| Bare headers | −3/cad | −15 |
---
## Cosa rende un Markdown perfetto per la vettorizzazione
- **Struttura semantica:** header Markdown = confini naturali dei chunk; ogni sezione è un'unità concettuale.
- Implementare la funzione nel modulo del gruppo corretto (`_encoding.py`, `_artifacts.py`, ecc.), importarla in `_apply.py` e inserire la coppia `("stat_key", _t_nuova)` nella lista `_transforms` nel punto logicamente corretto.