Quando decidi di far allegare file a un’applicazione locale, la scelta più ovvia è prendere una libreria “nota”, lanciare il caricamento e andare avanti. Ma il PDF non è Word. Non è Excel. E quando cominci a scavare nei dettagli di cosa significhi davvero estrarre testo da un PDF, scopri che la decisione architettonica che sembrava semplice ha radici molto più profonde di quanto pensassi.
Questa non è la storia di come implementare un caricamento di PDF. È la storia di cosa imparare quando quella scelta ti forza a confrontarti con il compromesso fondamentale tra completezza della feature e sostenibilità del sistema che la ospita.
Il PDF non ha struttura
Word dichiara i suoi paragrafi. Excel dichiara righe e celle. Sono formati che costruiscono la struttura dei dati, la scrivono nel file, e chi legge ha solo da estrarre. Un PDF, invece, non dichiara nulla di quello. Dichiara solo dove sta ogni carattere sulla pagina.
Questa differenza filosofica genera tre conseguenze concrete che diventeranno tre algoritmi che dovrai scrivere.
La prima è l’ordine di lettura. Su una pagina a due colonne, pdfminer (la libreria di analisi standard) restituisce le righe alternate: prima colonna, prima riga della seconda colonna, seconda riga della prima colonna, e così via. Il testo che ne esce è incomprensibile. Non è un bug di pdfminer; è la conseguenza di scegliere di ordinare per posizione verticale invece che per posizione orizzontale. E nessuna configurazione lo risolve (sono state provate, in ordine: boxes_flow=None, boxes_flow=-1.0, variazioni di char_margin, di line_margin). Devi ricostruire tu l’ordine cercando i corridoi verticali, conteggiando quale sia largo abbastanza da rappresentare una colonna vera invece che semplice spaziatura, e verificando che le righe in quelle fasce siano effettivamente testo denso e non celle di una tabella che verrebbe scardinata.
La seconda conseguenza è che intestazioni e piè di pagina si ripetono su ogni pagina. Su un documento di 50 pagine sono 100 righe che non dicono niente e rubano contesto al testo che importa. Devi identificarle guardando solo i bordi di ogni pagina, ridurre le cifre a placeholder (“Pagina 1 di 40” diventa “Pagina # di #”), conteggiare su quante pagine compare ogni pattern, e scartare ciò che supera una soglia (il 60% delle pagine). Ma qui c’è una trappola: su una pagina di una riga sola, quella riga è insieme il primo e l’ultimo elemento del bordo. Un documento di schede prodotto con la stessa frase in cima a ogni pagina verrebbe vuotato completamente, in silenzio, senza errore. Devi restringere il concetto di “bordo” sulle pagine corte per lasciar sempre almeno una riga nel mezzo.
La terza conseguenza è che un PDF va a capo a ogni riga tipografica, non a ogni paragrafo. Una frase arriva spezzata in cinque pezzi. Devi riattaccarli cercando il segnale di fine paragrafo, che non è il punto fermo (le frasi finiscono di continuo dentro un paragrafo) ma è geometrico: il punto fermo e insieme la riga che non arriva al margine destro. Poi devi gestire la sillabazione di fine riga (riattaccare “impor-” con “tante” ma tenere il trattino di “Emilia-Romagna”), sciolgliere le legature tipografiche (ffi diventa ffi), e mandare a capo le voci di elenco.
Ognuna di queste è un’euristica. Ogni euristica può sbagliare. E qui è dove la decisione architetturale comincia a pesare davvero.
La scelta della libreria
Quando hai scoperto che il PDF richiede queste tre ricostruzioni, naturalmente hai chiesto: c’è una libreria che le fa già? La risposta è pdfplumber. Fa tutto questo, e pure estrazione di tabelle. Perfetto. Installi e… no.
pip install pdfplumber risolve in cinque estensioni compilate. Non “dipendenze Python pure”. Estensioni C compilate contro la tua macchina: pillow, pypdfium2 (che è PDFium, il motore PDF di Google da 100.000 righe di C++, che pdfplumber usa solo per page.to_image() — una funzione che non chiamerai mai), charset-normalizer, cryptography, cffi.
pdfminer.six è il motore di layout che sta dentro pdfplumber, preso da solo. Due estensioni compilate: cryptography e cffi. Di queste, cryptography serve comunque, è quello che identifica quando un PDF è protetto da password e permette di lanciare un errore chiaro invece di fallire silenziosamente. pypdf aggiunge zero estensioni compilate, ma non sa fare analisi di layout: senza coordinate non riordini le colonne, e la ricucitura dei paragrafi parte da testo già rovinato.
Quindi: tre estensioni compilate con pdfminer.six, zero con pypdf, cinque con pdfplumber. Il costo di sceglierne uno invece dell’altro è invisibile finché il software gira sul laptop dello sviluppatore. Diventa visibile il giorno in cui devi pacchettizzare, o quando scopri che Smart App Control su Windows 11 blocca il caricamento dei binari non firmati, o quando devi supportare un ambiente dove il compilatore non c’è.
Se stai leggendo questo perché stai valutando di fare la stessa scelta, pdfminer.six non è la scelta più elegante ma è la scelta che non ti sorprende a deployment time. Il prezzo è che le tabelle dentro un PDF arrivano come testo corrente invece che come righe Markdown. Se allegherai davvero PDF con tabelle che contano, dovrai risolvere quello. Ma almeno lo sai adesso.
Contesto finito, file patologici
Con un contesto di 8192 token (il numero scelto per una conversazione su un modello locale) e un massimo di 1024 token per risposta, lo spazio per la storia di conversazione è finito. Se alleghi un PDF di 50 pagine, quanto spazio ha? 12.000 caratteri di limite per il testo estratto. Se il PDF supera quel limite, viene troncato e l’utente riceve un avviso.
Ma c’è qualcosa di più importante del troncamento per dimensione. Un PDF non è uno zip, quindi non ha un indice che ti dice quanto contenuto c’è dentro. Con un .docx o un .xlsx (che sono zip), prima di estrarre qualsiasi cosa puoi controllare la dimensione dichiarata all’interno dell’archivio e rifiutare anticipatamente i file che si dichiarano giganteschi una volta decompresiti (protezione contro le zip bomb). Con un PDF non puoi farlo. L’unica difesa è un limite sul numero di pagine lette: 50 pagine è il massimo. Non è una scelta elegante. È una barriera di contenimento perché non hai un modo migliore di proteggere il sistema da file patologici.
E poi c’è il caso che capita davvero: il PDF scansionato. Una fotografia di un documento cartaceo. Zero testo estratto. Se non lo riconosci e lo allega comunque, il modello riceve un contesto vuoto senza sapere perché. La difesa è una soglia: se sotto i 50 caratteri estratti, è una scansione, e dici all’utente che quella strada non funziona, ma hai anche la visione attiva sulle immagini, quindi può allegare la pagina come foto.
Questi vincoli non sono scelte di design. Sono confini oltre i quali il sistema si rompe, e tu non hai scelta su dove piazzarli.
Cosa imparare
Il PDF è una sfida non perché sia tecnicamente complesso (non lo è), ma perché forza il sistema a stare con i piedi dentro tre mondi diversi: l’analisi geometrica (il riordino delle colonne), la pulizia textuale (ricucitura e deduplica), e i vincoli finiti del contesto in cui il testo vivrà. Ognuno di questi è una decisione che ha conseguenze invisibili fino a quando non le scopri.
Scegliere una libreria “giusta” non risolve il problema, solo lo sposta. Quello che rimane è l’architettura di come il file entra, si trasforma, e dove il sistema dice “no, da qui in poi è responsabilità di chi l’ha allegato”. Questi confini non sono belli. Ma sono onesti. E sono il vero costo della feature.
Se stai valutando di aggiungere PDF al tuo sistema, non guardare la libreria. Guardai quanto contesto puoi permetterti di perdere, quanti stati di errore sei disposto a lasciare “fallire silenziosamente” prima di metterti a scrivere del codice defensivo, e se sei pronto ad avere quattro stringhe uguali in due posti del tuo codebase perché cambiarle tutte diventa un problema di sincronizzazione.
Quello è il vero prezzo della decisione. La libreria è solo il dettaglio.
Se l’articolo ti è piaciuto restiamo in contatto su linkedin a: https://www.linkedin.com/in/andreatonin/
| Editorial Manager at Pills for Nerds | CTO & Technology Consultant |
Technology professional, editor and lifelong nerd with over 30 years of experience in the digital and innovation sectors. I work as a CTO and technology consultant, designing complex and scalable software ecosystems, and I am also active in IT education and professional training. Alongside my technology career, I serve as Editorial Manager and contributor at Pills for Nerds, online publication covering video games, game development, emerging technologies, artificial intelligence, digital culture and industry events. In my editorial role, I coordinate content planning and contribute articles, interviews, event reports and in-depth features. You can learn more about my consulting work at lucedigitale.com and connect with me on LinkedIn

















