Salta ai contenuti

CLI Python

Il comando nextpdf esegue l’estrazione di PDF dal terminale. Basta indirizzarlo a un endpoint NextPDF Connect, passargli un PDF e ricevere un output strutturato — testo con citazioni, tabelle, l’intero Abstract Syntax Tree (AST) semantico o un riepilogo dei metadati — sullo standard output (stdout) o in un file.

Il comando nextpdf è un gruppo di comandi Click. Le opzioni di connessione e di sessione — --base-url, --api-key, --log-level, --output/-o e --strict — sono definite a livello di gruppo, quindi vanno collocate prima del sottocomando. Il sottocomando e le relative opzioni (come --format o --page) vanno dopo:

nextpdf [GROUP OPTIONS] COMMAND [SUBCOMMAND] PDF_PATH [COMMAND OPTIONS]

Collocare un’opzione di gruppo dopo il sottocomando genera un errore. Ad esempio, nextpdf info document.pdf --base-url ... segnala Error: No such option: --base-url ed esce con stato 2, perché quando Click analizza --base-url si trova già all’interno del sottocomando info, che non definisce tale opzione.

Il modo più chiaro per evitare la trappola dell’ordine è fornire le credenziali tramite variabili d’ambiente (vedere Configurare una volta per shell). Gli esempi seguenti mostrano prima la forma con flag espliciti, così l’ordine corretto resta evidente.

Estrarre il testo come JSON:

Terminal window
nextpdf --base-url http://localhost:8080 --api-key "$NEXTPDF_API_KEY" extract text document.pdf

Estrarre le tabelle come valori separati da virgola (CSV):

Terminal window
nextpdf --base-url http://localhost:8080 --api-key "$NEXTPDF_API_KEY" extract tables invoice.pdf --format csv

Esaminare i metadati e la struttura del documento:

Terminal window
nextpdf --base-url http://localhost:8080 --api-key "$NEXTPDF_API_KEY" info document.pdf

Ottenere l’intero AST semantico:

Terminal window
nextpdf --base-url http://localhost:8080 --api-key "$NEXTPDF_API_KEY" ast document.pdf

Stampare la versione dell’SDK installata senza contattare alcun server:

Terminal window
nextpdf version

Il comando version è l’unico comando che non richiede né --base-url--api-key. Ogni altro comando contatta il server e richiede entrambi.

Ogni esempio legge la chiave API dalla variabile d’ambiente NEXTPDF_API_KEY anziché incorporarla nella riga di comando. Trattare la chiave come un segreto. Una chiave in chiaro nella riga di comando è visibile nella cronologia della shell e nell’elenco dei processi (ps) agli altri utenti dell’host.

Queste opzioni vanno collocate prima del sottocomando. Ogni opzione di connessione può leggere anche da una variabile d’ambiente, quindi è possibile omettere il flag quando la variabile è impostata.

OpzioneVariabile d’ambientePredefinitoScopo
--base-urlNEXTPDF_BASE_URL(obbligatorio)URL del server NextPDF Connect.
--api-keyNEXTPDF_API_KEY(obbligatorio)Chiave API per l’autenticazione bearer.
--log-levelwarningLivello di dettaglio del logging: debug, info, warning o error. I log vengono inviati allo standard error (stderr).
--output, -ostdoutScrive l’output del comando in un file invece che su stdout.
--strictoffRiservato per uso futuro. Attualmente il flag viene analizzato ma non modifica il comportamento.
--help, -hMostra la guida ed esce.

I valori --base-url e --api-key sono obbligatori per ogni comando tranne version. Se uno dei due manca — senza flag e senza variabile d’ambiente — il comando stampa un errore ed esce con stato 1.

Estrae blocchi di testo con citazioni. Ogni blocco contiene un’ancora di citazione (identificatore del nodo, indice di pagina, bounding box e punteggio di confidenza).

nextpdf [GROUP OPTIONS] extract text PDF_PATH [--format FORMAT] [--page N] [--headings-only]
OpzioneValoriPredefinitoScopo
--formatjson, markdown, plainjsonFormato di output.
--pageinterotutte le pagineEstrae solo questo indice di pagina in base 0.
--headings-onlyflagoffEstrae solo i nodi di intestazione.

PDF_PATH è un percorso di file oppure - per leggere i byte del PDF da stdin.

Estrae ogni tabella con ancore di citazione e struttura a livello di cella.

nextpdf [GROUP OPTIONS] extract tables PDF_PATH [--format FORMAT] [--page-start N] [--page-end N]
OpzioneValoriPredefinitoScopo
--formatjson, csvjsonFormato di output.
--page-startinteroprima paginaIndice della pagina iniziale (in base 0).
--page-endinteroultima paginaIndice della pagina finale (in base 0).

PDF_PATH è un percorso di file oppure - per leggere da stdin.

Restituisce l’intero AST semantico come JSON: un albero gerarchico di nodi (intestazioni, paragrafi, tabelle, elenchi, figure) con bounding box e contenuto testuale.

nextpdf [GROUP OPTIONS] ast PDF_PATH [--page-start N] [--page-end N] [--token-budget N]
OpzioneValoriPredefinitoScopo
--page-startinteroprima paginaIndice della pagina iniziale (in base 0).
--page-endinteroultima paginaIndice della pagina finale (in base 0).
--token-budgetinteroillimitatoLimite approssimativo di token per l’AST restituito.

PDF_PATH è un percorso di file oppure - per leggere da stdin. Il comando ast produce un singolo albero del documento; non confronta due PDF. Per il confronto strutturale, vedere Ricetta: confrontare due revisioni di PDF.

Stampa un riepilogo JSON compatto di un documento: versione dello schema, hash dell’origine, numero di pagine, numero stimato di token, tipo del nodo radice e numero di figli della radice.

nextpdf [GROUP OPTIONS] info PDF_PATH

PDF_PATH è un percorso di file oppure - per leggere da stdin.

Stampa la versione dell’SDK installata (ad esempio, nextpdf 1.1.0) ed esce. Questo comando non contatta alcun server e non richiede credenziali.

nextpdf version

Impostare le opzioni di connessione una sola volta come variabili d’ambiente e omettere i flag ripetuti. Questa forma evita del tutto anche la trappola dell’ordine delle opzioni, perché le credenziali non compaiono mai nella riga di comando.

Terminal window
export NEXTPDF_BASE_URL=http://localhost:8080
export NEXTPDF_API_KEY=your-key
nextpdf extract text document.pdf

Su Windows PowerShell:

Terminal window
$env:NEXTPDF_BASE_URL = "http://localhost:8080"
$env:NEXTPDF_API_KEY = "your-key"
nextpdf extract text document.pdf

È preferibile caricare la chiave da un archivio di segreti o da un file .env tenuto fuori dal controllo di versione. Non incollare una chiave di produzione in una sessione di terminale condivisa o in uno script destinato al commit.

Il formato di output si seleziona per ciascun comando con --format. I comandi text e table supportano più formati; ast e info emettono sempre JSON.

ComandoFormatiPredefinito
extract textjson, markdown, plainjson
extract tablesjson, csvjson
astjsonjson
infojsonjson

Scegliere JSON quando un programma a valle richiede indici di pagina, punteggi di confidenza o identificatori di nodo. Scegliere CSV quando le tabelle vengono consumate da un foglio di calcolo o da una pipeline tabellare. Scegliere il testo plain o markdown quando il risultato viene letto da una persona o da uno strumento solo testo.

Il comando text emette un array JSON di blocchi con citazione. Ogni blocco contiene text, un oggetto citation (node_id, page_index, bbox, confidence) e un node_type opzionale. Inviare l’output a un file con --output (oppure reindirizzare stdout), quindi analizzarlo.

Questo esempio di shell utilizza jq per mantenere solo le intestazioni della pagina 0:

Terminal window
nextpdf --output blocks.json extract text report.pdf --format json
jq '[.[] | select(.citation.page_index == 0 and .node_type == "heading") | .text]' blocks.json

Gli stessi dati si analizzano agevolmente in Python. La CLI scrive un array JSON, che si può caricare con la libreria standard per leggere i campi tipizzati:

"""Parse cited text blocks emitted by `nextpdf extract text --format json`."""
import json
from pathlib import Path
def load_headings(blocks_path: Path) -> list[str]:
"""Return the text of every heading block on page 0.
Args:
blocks_path: Path to the JSON file written by `nextpdf extract text`.
Returns:
The text of each heading-type block whose citation is on page 0.
"""
raw = blocks_path.read_text(encoding="utf-8")
blocks: list[dict[str, object]] = json.loads(raw)
headings: list[str] = []
for block in blocks:
citation = block["citation"]
if block.get("node_type") == "heading" and citation["page_index"] == 0:
headings.append(str(block["text"]))
return headings
if __name__ == "__main__":
for heading in load_headings(Path("blocks.json")):
print(heading)

Quando servono modelli convalidati e tipizzati anziché dizionari grezzi, chiamare direttamente l’SDK invece di analizzare l’output della CLI. Vedere la panoramica di Python per il client NextPDF e il relativo tipo restituito CitedTextBlock.

Con --format csv, il comando table scrive un blocco CSV per ogni tabella. Una riga di commento, # Table N (pM), precede ogni tabella e indica il numero di tabella in base 1 e l’indice di pagina in base 0. Una riga vuota separa le tabelle consecutive. La CLI racchiude tra virgolette ed esegue l’escape dei valori delle celle con il modulo csv di Python, così i valori contenenti virgole, virgolette o ritorni a capo vengono preservati nel round-trip.

Terminal window
nextpdf --output tables.csv extract tables statement.pdf --format csv

Poiché il file contiene più blocchi CSV, suddividerlo in corrispondenza delle righe di commento prima di analizzare ciascun blocco come tabella autonoma:

"""Split multi-table CSV output from `nextpdf extract tables --format csv`."""
import csv
import io
from pathlib import Path
def read_tables(csv_path: Path) -> list[list[list[str]]]:
"""Parse the multi-block CSV file into a list of tables.
Each table is a list of rows; each row is a list of cell strings.
The leading `# Table N (pM)` comment row is dropped from every table.
Args:
csv_path: Path to the file written by `nextpdf extract tables`.
Returns:
One parsed table per `# Table` block in the file.
"""
text = csv_path.read_text(encoding="utf-8")
tables: list[list[list[str]]] = []
current: list[str] = []
for line in text.splitlines(keepends=True):
if line.startswith("# Table ") and current:
tables.append(_parse_block(current))
current = []
current.append(line)
if current:
tables.append(_parse_block(current))
return tables
def _parse_block(lines: list[str]) -> list[list[str]]:
"""Parse one CSV block, discarding its leading comment row."""
reader = csv.reader(io.StringIO("".join(lines)))
rows = [row for row in reader if row]
return rows[1:] if rows and rows[0] and rows[0][0].startswith("# Table ") else rows
if __name__ == "__main__":
for index, table in enumerate(read_tables(Path("tables.csv")), start=1):
print(f"table {index}: {len(table)} rows")

La CLI utilizza tre codici di uscita. Controllare $? negli script di shell (oppure $LASTEXITCODE in PowerShell) per diramare in base all’esito, positivo o negativo, e leggere i messaggi diagnostici da stderr, che resta separato dai dati su stdout.

Codice di uscitaSignificatoEsempi
0Esito positivo.Un comando è stato completato; version ha stampato l’output.
1Errore di runtime. La CLI stampa Error: <message> su stderr.File di input non trovato o non regolare, stdin vuoto, --base-url/--api-key mancante o non valido, qualsiasi errore lato server (licenza richiesta, quota superata, PDF non taggato, timeout di build o altro errore dell’API).
2Errore d’uso, segnalato da Click.Comando od opzione sconosciuti (incluso un’opzione di gruppo collocata dopo il sottocomando), un argomento obbligatorio mancante come PDF_PATH.

Ogni errore lato server si manifesta con codice di uscita 1 e un messaggio leggibile su stderr. L’SDK solleva un’eccezione tipizzata — NextPDFLicenseError (HTTP 402), QuotaExceededError (HTTP 429), AstNoStructTreeError (HTTP 422, PDF non taggato), AstBuildTimeoutError (HTTP 504) o la classe base NextPDFAPIError. La CLI le intercetta tutte sotto la classe base condivisa NextPDFError, stampa il messaggio ed esce con 1. La CLI non espone codici di uscita distinti per tipo di errore. Per distinguere, ad esempio, un errore di quota da un errore di licenza in uno script, esaminare il testo del messaggio su stderr o chiamare direttamente l’SDK (vedere la panoramica di Python per le classi delle eccezioni).

Uno schema di scripting che separa i dati dalla diagnostica:

#!/usr/bin/env bash
set -euo pipefail
# Credentials come from the environment, not the command line.
: "${NEXTPDF_BASE_URL:?set NEXTPDF_BASE_URL}"
: "${NEXTPDF_API_KEY:?set NEXTPDF_API_KEY}"
if nextpdf --output contract.ast.json ast contract.pdf; then
echo "AST written to contract.ast.json"
else
status=$?
echo "nextpdf failed with exit code ${status}" >&2
exit "${status}"
fi

Si noti che --output scrive i dati nel file indicato e stampa solo la riga di conferma Written to <path> su stderr, quindi stdout resta vuoto. Senza --output, i dati vengono inviati a stdout e possono essere reindirizzati.

Ogni ricetta seguente utilizza solo comandi e flag verificati. In tutti i casi, le credenziali provengono dall’ambiente.

Trasformare una cartella di fatture in un file CSV per documento, da usare in un foglio di calcolo o in una pipeline contabile:

#!/usr/bin/env bash
set -euo pipefail
: "${NEXTPDF_BASE_URL:?set NEXTPDF_BASE_URL}"
: "${NEXTPDF_API_KEY:?set NEXTPDF_API_KEY}"
mkdir -p out
for pdf in invoices/*.pdf; do
name="$(basename "${pdf}" .pdf)"
nextpdf --output "out/${name}.csv" extract tables "${pdf}" --format csv
done

Ogni out/<name>.csv contiene un blocco CSV per ogni tabella rilevata, con un’intestazione # Table N (pM) prima di ciascuna tabella. Analizzare i blocchi con il lettore CSV mostrato sopra.

Combinare --headings-only con il formato markdown per produrre rapidamente una struttura leggibile o da incollare negli appunti:

Terminal window
nextpdf --output outline.md extract text whitepaper.pdf --headings-only --format markdown

Il comando ast della CLI restituisce l’albero di un singolo documento; non dispone di un sottocomando di confronto. Il confronto strutturale è disponibile nell’SDK come client.ast.get_ast_diff(...) e nel server Model Context Protocol (MCP) come strumento nextpdf_diff. Eseguire il confronto tramite l’SDK:

"""Compare two PDF revisions structurally with the NextPDF SDK.
The API key is read from the environment, never hard-coded.
"""
import os
from pathlib import Path
from nextpdf import NextPDF
def diff_revisions(original: Path, modified: Path) -> None:
"""Print a structural diff summary between two PDF revisions.
Args:
original: Path to the earlier PDF revision.
modified: Path to the later PDF revision.
"""
base_url = os.environ["NEXTPDF_BASE_URL"]
api_key = os.environ["NEXTPDF_API_KEY"]
client = NextPDF(base_url=base_url, api_key=api_key)
result = client.ast.get_ast_diff(
original.read_bytes(),
modified.read_bytes(),
)
summary = result.summary
print(f"added: {summary.added_node_count}")
print(f"removed: {summary.removed_node_count}")
print(f"changed: {summary.changed_node_count}")
for entry in result.diff:
preview = entry.text_preview or ""
print(f" {entry.type:<8} {entry.node_type:<12} p{entry.page_index} {preview}")
if __name__ == "__main__":
diff_revisions(Path("contract-v1.pdf"), Path("contract-v2.pdf"))

Per eseguire lo stesso confronto da un agente IA anziché da uno script, registrare il server MCP e chiamare lo strumento nextpdf_diff. Vedere la pagina del server MCP di Python.

Ricetta: trasmettere un PDF in ingresso da un altro strumento

Sezione intitolata “Ricetta: trasmettere un PDF in ingresso da un altro strumento”

Leggere i byte del PDF da stdin con - per concatenare nextpdf dopo uno strumento che emette un PDF sul proprio stdout:

Terminal window
curl --silent https://example.com/report.pdf | nextpdf info -

L’argomento - indica al comando di leggere il documento da stdin. Se non arriva alcun byte, il comando segnala un errore ed esce con 1.

La CLI costruisce ogni risposta in memoria e la scrive in un’unica operazione: reindirizzare o convogliare l’output è quindi semplice, ma l’output non viene prodotto in modo incrementale. Un AST o un insieme di tabelle di grandi dimensioni viene interamente bufferizzato prima che il primo byte raggiunga stdout o il file --output. Pianificare memoria e latenza per risposte sull’intero documento, non per un flusso.

Ogni invocazione di nextpdf crea un nuovo client e una nuova connessione HTTP, quindi un ciclo su molti file apre e chiude una connessione per file. Il costo della connessione è di solito ridotto rispetto al tempo di estrazione lato server, ma su larga scala rappresenta un overhead reale.

  • Riutilizzare un solo endpoint. Indirizzare ogni invocazione allo stesso deployment NextPDF Connect, in modo che il server possa riutilizzare cache già pronte e pool di connessioni. Evitare di distribuire un batch su più endpoint, a meno che il bilanciamento del carico non sia intenzionale.
  • Limitare il lavoro per chiamata. Usare --page, --page-start/--page-end o --token-budget per elaborare solo le pagine necessarie. Intervalli di pagine più piccoli riducono sia il tempo del server sia la dimensione della risposta; --token-budget limita l’AST che l’agente deve leggere.
  • Eseguire i batch in un solo processo per i lavori di grandi dimensioni. Per i batch ad alto volume, è preferibile usare l’SDK Python invece di chiamare ripetutamente la CLI. Un singolo client NextPDF (o AsyncNextPDF) a lunga durata riutilizza una connessione HTTP in pool per ogni documento, eliminando l’avvio del processo e la configurazione della connessione che un ciclo della CLI paga ogni volta. La panoramica di Python mostra il ciclo di vita del client e AsyncNextPDF supporta l’estrazione concorrente su molti PDF.
  • Tenere i log fuori dal percorso dei dati. Lasciare --log-level al valore predefinito per le esecuzioni batch. I log diagnostici vanno su stderr e non corrompono i dati su stdout, ma alzare il livello a debug aggiunge rumore e un lieve overhead.