💡 Esempio completo funzionante disponibile su GitHub:
extract-annotations-from-pdf-using-groupdocs-parser-dotnet

Introduction

Un PDF che è stato revisionato di solito contiene più del suo testo visibile – note adesive, osservazioni evidenziate e commenti in linea lasciati dai revisori. Scorrere ogni pagina per trovarli non è pratico una volta che un documento ha attraversato diversi cicli di feedback. GroupDocs.Parser è una libreria .NET che legge programmaticamente le annotazioni incorporate in un documento, trasformando i commenti sparsi dei revisori in dati strutturati su cui il tuo codice può operare. Questo tutorial mostra come estrarre le annotazioni da un intero PDF, suddividerle pagina per pagina, estrarle insieme al testo del documento e esportare i risultati in CSV o JSON.

Mi sono imbattuto in questo problema mentre costruivo un tracker di revisione per un team di documentazione: una nota di rilascio di 40 pagine era passata attraverso tre revisori, e aprire manualmente il file per trovare ogni commento richiedeva più tempo di quello necessario per correggere i problemi segnalati. Estrarre le annotazioni in poche righe di codice ha trasformato l’operazione in un compito di due minuti.

Nelle sezioni seguenti imparerai a:

  • Estrarre ogni annotazione da un PDF in un’unica passata.
  • Etichettare ogni annotazione con la pagina a cui appartiene.
  • Unire il testo del documento e il testo dell’annotazione in una singola lettura.
  • Serializzare i risultati in CSV o JSON per strumenti a valle.

Why Extracting PDF Annotations Matters

Leggere le annotazioni PDF programmaticamente è utile per:

  • Flussi di revisione: raccogliere ogni commento del revisore senza aprire il file in un visualizzatore PDF.
  • Collaborazione: esporre le sezioni evidenziate o annotate direttamente nei propri strumenti.
  • Audit: mantenere una traccia del markup lasciato su un documento nel tempo, anche dopo che è stato appiattito o finalizzato.

GroupDocs.Parser ha aggiunto l’estrazione nativa delle annotazioni per i documenti PDF nella versione 26.7 tramite il metodo GetAnnotations, insieme a una nuova opzione IncludeAnnotations su TextOptions per includere il testo delle annotazioni in una lettura di testo regolare.

Prerequisites

  • .NET 6.0 o successivo
  • GroupDocs.Parser per .NET 26.7+ (licenza temporanea)
  • Un file PDF con annotazioni esistenti (ad es., document-with-annotations.pdf)

Installa via NuGet:

dotnet add package GroupDocs.Parser

How do I extract annotations from a PDF document?

Risposta: Carica il file con Parser, quindi chiama GetAnnotations() per l’intero documento o GetAnnotations(pageIndex) per una singola pagina. Ogni risultato è una collezione di oggetti AnnotationItem il cui campo Value contiene il testo del commento. Se preferisci vedere i commenti in linea con il contenuto regolare del documento, imposta IncludeAnnotations su TextOptions e chiama GetText invece.

Whole‑Document Extraction

Il frammento seguente estrae ogni annotazione dal file in una singola chiamata, il modo più veloce per verificare se un documento contiene commenti aperti.

// Extract every annotation from the whole document
var result = new List<string>();
using (var parser = new Parser(path))
{
    IEnumerable<AnnotationItem> annotations = parser.GetAnnotations();
    if (annotations == null)
    {
        return result; // format doesn't support annotations
    }

    foreach (var item in annotations)
    {
        result.Add(item.Value); // annotation text
    }
}
return result;

Punti chiave:

  • GetAnnotations() restituisce null quando l’estrazione delle annotazioni non è supportata per il documento, e una collezione vuota quando il documento semplicemente non ne contiene.
  • Ogni AnnotationItem espone il suo testo tramite la proprietà Value – è l’unico dato che l’SDK attualmente fornisce.
  • Qui non è inclusa alcuna attribuzione di pagina; usa il sovraccarico per pagina mostrato di seguito se ti serve.

Per‑Page Extraction

Quando la posizione di un commento è importante, itera le pagine del documento e chiama GetAnnotations(pageIndex) per ciascuna.

// Tag each annotation with its zero-based page index
var result = new List<AnnotationRecord>();
using (var parser = new Parser(path))
{
    if (!parser.Features.Annotations)
    {
        return result;
    }

    var info = parser.GetDocumentInfo();
    if (info == null || info.PageCount == 0)
    {
        return result;
    }

    for (int pageIndex = 0; pageIndex < info.PageCount; pageIndex++)
    {
        IEnumerable<AnnotationItem> pageAnnotations = parser.GetAnnotations(pageIndex);
        if (pageAnnotations == null)
        {
            continue;
        }

        foreach (var item in pageAnnotations)
        {
            result.Add(new AnnotationRecord { PageIndex = pageIndex, Value = item.Value });
        }
    }
}
return result;

Punti chiave:

  • GetDocumentInfo().PageCount guida il ciclo; non esiste un “conteggio pagine delle annotazioni” separato.
  • GetAnnotations(pageIndex) utilizza un indice a base zero, coerente con tutti gli altri metodi a livello di pagina dell’API.
  • L’elenco risultante di AnnotationRecord ha esattamente la forma necessaria per un’esportazione CSV o JSON.

Extracting Text Together with Annotations

Invece di due passate sul documento, puoi incorporare direttamente il testo delle annotazioni nell’output dell’estrazione del testo normale.

// Read document text with annotation text included
using (var parser = new Parser(path))
{
    var options = new TextOptions
    {
        IncludeAnnotations = true
    };

    using (TextReader reader = parser.GetText(options))
    {
        return reader?.ReadToEnd() ?? string.Empty;
    }
}

Punti chiave:

  • IncludeAnnotations è una proprietà di TextOptions, quindi funziona con la stessa chiamata GetText che useresti per l’estrazione di testo semplice.
  • Utile quando vuoi un unico output in stile trascrizione anziché una lista separata di commenti.
  • Combinalo con GetText(pageIndex, options) se ti serve solo per una pagina.

Checking Annotation Support First

Non tutti i formati supportano le annotazioni, quindi è opportuno verificare prima di costruire logica attorno a GetAnnotations.

// Returns true if the loaded document format supports annotation extraction
using (var parser = new Parser(path))
{
    return parser.Features.Annotations;
}

Punti chiave:

  • Features.Annotations è un semplice flag booleano sull’istanza Parser.
  • Verificarlo in anticipo rende l’intento esplicito, anche se GetAnnotations restituisce già null in caso di mancato supporto.

Exporting the Annotations to CSV

Un’esportazione CSV consente ai revisori di aprire l’elenco dei commenti direttamente in Excel. Il metodo qui sotto scrive un file a due colonne (page,value) dai record etichettati per pagina creati in precedenza.

var sb = new StringBuilder();
sb.AppendLine("page,value");

foreach (var record in records)
{
    sb.AppendLine($"{record.PageIndex},{CsvEscape(record.Value)}");
}

File.WriteAllText(outputPath, sb.ToString());

Punti chiave:

  • CsvEscape aggiunge le virgolette ai campi contenenti virgole, virgolette o interruzioni di riga.
  • Il file risultante si apre direttamente in Excel o può essere inviato a uno strumento di ticketing.

Helper: CsvEscape

if (string.IsNullOrEmpty(s)) return string.Empty;
if (s.Contains(",") || s.Contains("\"") || s.Contains("\n"))
{
    return "\"" + s.Replace("\"", "\"\"") + "\"";
}
return s;

Exporting the Annotations to JSON

Per pipeline che consumano i commenti programmaticamente, un array JSON è solitamente più adatto di un CSV piatto.

var sb = new StringBuilder();
sb.AppendLine("[");

for (int i = 0; i < records.Count; i++)
{
    var comma = i < records.Count - 1 ? "," : string.Empty;
    sb.AppendLine($"  {{ \"page\": {records[i].PageIndex}, \"value\": \"{Escape(records[i].Value)}\" }}{comma}");
}

sb.AppendLine("]");
File.WriteAllText(outputPath, sb.ToString());

Punti chiave:

  • L’output è un array piatto di oggetti { page, value } – facile da deserializzare per qualsiasi servizio a valle.
  • Escape mantiene il payload JSON valido senza introdurre una libreria di serializzazione.

Helper: Escape

return s?.Replace("\\", "\\\\").Replace("\"", "\\\"") ?? string.Empty;

Comparing Methods: When to Use Each

Metodo Ideale per Vantaggi principali Limitazioni
Whole‑Document Extraction Verifica rapida “ci sono commenti?” Chiamata singola, codice più semplice Nessuna attribuzione di pagina
Per‑Page Extraction Instradare il feedback alla sezione corretta Risultati etichettati per pagina, pronti per l’esportazione Una chiamata extra per pagina
Combined Text + Annotations Trascrizione leggibile unica Nessuna seconda passata sul documento I commenti non sono separati dal testo principale
CSV Export Tracciamento basato su foglio di calcolo Facile da aprire in Excel, leggibile da umano Struttura piatta limitata
JSON Export Pipeline automatizzate, sistemi di ticketing Strutturato, leggibile da macchina Payload leggermente più grande

Inizia con l’estrazione dell’intero documento per confermare che il file contenga commenti da gestire, poi passa all’estrazione per pagina quando devi instradare il feedback a una sezione specifica.

Best Practices and Tips

  • Dispose Parser prontamente: avvolgilo in un blocco using per liberare le risorse native.
  • Distinguere null da vuoto: GetAnnotations che restituisce null indica che il formato non è supportato; una collezione vuota indica che il documento non ha commenti.
  • Controlla Features.Annotations nei job batch: salta i file non supportati subito, invece di affidarti a un controllo null più in fondo al ciclo.
  • Riutilizza la lista etichettata per pagina: costruiscila una volta con ExtractAnnotationsByPage e alimenta sia l’esportatore CSV che quello JSON dallo stesso set di dati, così i due output non divergono mai.
  • Sicurezza: il testo delle annotazioni è input libero del revisore – trattalo come qualsiasi altra stringa non attendibile prima di renderizzarlo in UI o report.

Conclusion

GroupDocs.Parser ti offre un modo diretto e programmatico per estrarre i commenti dei revisori da un PDF, evitando la ricerca manuale. Estrarre le annotazioni per l’intero documento, etichettarle per pagina o inserirle nel flusso di testo regolare ti permette di costruire flussi di revisione che mostrano il feedback non appena il documento entra nella tua pipeline. Esporta i risultati in CSV o JSON e collegali direttamente agli strumenti già in uso dal tuo team.

Passi successivi:

Additional Resources