💡 Volledig werkend voorbeeld beschikbaar op GitHub:
extract-annotations-from-pdf-using-groupdocs-parser-dotnet
Introduction
Een PDF die een review heeft ondergaan bevat meestal meer dan alleen de zichtbare tekst – plaknotities, gemarkeerde opmerkingen en inline‑commentaren van reviewers. Door elke pagina te scrollen om ze te vinden, schaalt niet meer zodra een document meerdere feedbackrondes heeft doorlopen. GroupDocs.Parser is een .NET‑bibliotheek die de ingebedde annotaties van een document programmatisch uitleest en verspreide reviewer‑commentaren omzet in gestructureerde data waar je code mee kan werken. Deze tutorial laat zien hoe je annotaties uit een volledige PDF haalt, ze per pagina opsplitst, ze samen met de documenttekst ophaalt en de resultaten exporteert naar CSV of JSON.
Ik kwam dit probleem tegen toen ik een review‑tracker bouwde voor een documentatieteam: een 40‑pagina’s tellende release‑note had drie reviewers doorlopen, en handmatig het bestand openen om elke opmerking te vinden duurde langer dan de feitelijke oplossing van de gemarkeerde issues. Het extraheren van de annotaties in een paar regels code maakte er een taak van twee minuten van.
In de volgende secties leer je hoe je:
- Elke annotatie uit een PDF in één keer kunt extraheren.
- Elke annotatie kunt labelen met de pagina waartoe hij behoort.
- Documenttekst en annotatietekst samen kunt ophalen in één leesactie.
- De resultaten kunt serialiseren naar CSV of JSON voor downstream‑tools.
Why Extracting PDF Annotations Matters
Het programmatisch lezen van PDF‑annotaties is nuttig voor:
- Review‑workflows: Verzamel elke reviewer‑opmerking zonder het bestand in een PDF‑viewer te openen.
- Samenwerking: Breng gemarkeerde of genoteerde secties direct binnen je eigen tools naar voren.
- Auditing: Houd een register bij van markup die op een document is achtergelaten, zelfs nadat het is geflatteerd of definitief gemaakt.
GroupDocs.Parser heeft native annotatie‑extractie voor PDF‑documenten toegevoegd in versie 26.7 via de GetAnnotations‑methode, naast een nieuwe IncludeAnnotations‑optie op TextOptions om annotatietekst in een reguliere tekst‑read op te nemen.
Prerequisites
- .NET 6.0 of later
- GroupDocs.Parser for .NET 26.7+ (temporary license)
- Een PDF‑bestand met bestaande annotaties (bijv.
document-with-annotations.pdf)
Installeren via NuGet:
dotnet add package GroupDocs.Parser
How do I extract annotations from a PDF document?
Answer: Laad het bestand met Parser, roep vervolgens GetAnnotations() aan voor het hele document of GetAnnotations(pageIndex) voor een enkele pagina. Elk resultaat is een collectie van AnnotationItem‑objecten waarvan de Value‑eigenschap de commentaartekst bevat. Als je de opmerkingen liever inline ziet met de reguliere inhoud van het document, stel dan IncludeAnnotations in op TextOptions en roep GetText aan.
Whole‑Document Extraction
De volgende code haalt elke annotatie uit het bestand in één enkele oproep, wat de snelste manier is om te controleren of een document überhaupt openstaande opmerkingen heeft.
// 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;
Key points:
GetAnnotations()returnsnullwhen annotation extraction isn’t supported for the document, and an empty collection when the document simply has none.- Each
AnnotationItemexposes its text through theValueproperty – that’s the only data point the SDK currently reports. - No page attribution is included here; use the per‑page overload below if you need it.
Per‑Page Extraction
Wanneer de locatie van een opmerking van belang is, loop je over de pagina’s van het document en roep je GetAnnotations(pageIndex) aan voor elke pagina.
// 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;
Key points:
GetDocumentInfo().PageCountdrives the loop; there’s no separate “annotation page count”.GetAnnotations(pageIndex)uses a zero‑based index, matching every other page‑level method in the API.- The resulting
AnnotationRecordlist is exactly the shape a CSV or JSON export needs.
Extracting Text Together with Annotations
In plaats van twee passes over het document te doen, kun je annotatietekst direct in de reguliere tekst‑extractie opnemen.
// 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;
}
}
Key points:
IncludeAnnotationsis a property onTextOptions, so this works with the sameGetTextcall you’d already use for plain text extraction.- Useful when you want a single transcript‑style output rather than a separate comment list.
- Combine it with
GetText(pageIndex, options)if you only need this for one page.
Checking Annotation Support First
Niet elk formaat ondersteunt annotaties, dus het is de moeite waard om dit eerst te controleren voordat je logica rond GetAnnotations bouwt.
// Returns true if the loaded document format supports annotation extraction
using (var parser = new Parser(path))
{
return parser.Features.Annotations;
}
Key points:
Features.Annotationsis a simple boolean flag on theParserinstance.- Checking it upfront makes intent explicit, even though
GetAnnotationsalready fails gracefully by returningnull.
Exporting the Annotations to CSV
Een CSV‑export laat reviewers de commentaarrij direct in Excel openen. De methode hieronder schrijft een twee‑koloms bestand (page,value) vanuit de eerder gebouwde pagina‑gelabelde records.
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());
Key points:
CsvEscapesafely quotes fields containing commas, quotes, or line breaks.- The resulting file opens directly in Excel or can be piped into a ticketing tool.
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
Voor pipelines die commentaren programmatisch consumeren, is een JSON‑array meestal beter geschikt dan een platte CSV.
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());
Key points:
- The output is a flat array of
{ page, value }objects – easy for any downstream service to deserialize. Escapekeeps the payload valid JSON without pulling in a serialization library.
Helper: Escape
return s?.Replace("\\", "\\\\").Replace("\"", "\\\"") ?? string.Empty;
Comparing Methods: When to Use Each
| Methode | Beste Voor | Belangrijkste Voordelen | Beperkingen |
|---|---|---|---|
| Whole‑Document Extraction | Snelle controle of er überhaupt opmerkingen zijn | Eén oproep, eenvoudigste code | Geen paginatoewijzing |
| Per‑Page Extraction | Feedback naar de juiste sectie routeren | Pagina‑gelabelde resultaten, klaar voor export | Eén extra oproep per pagina |
| Combined Text + Annotations | Eén leesbaar transcript | Geen tweede pass over het document | Opmerkingen zijn niet gescheiden van de hoofdtekst |
| CSV Export | Spreadsheet‑gebaseerde review‑tracking | Makkelijk te openen in Excel, mens‑leesbaar | Beperkt tot platte structuur |
| JSON Export | Geautomatiseerde pipelines, ticket‑systemen | Gestructureerd, machine‑leesbaar | Iets grotere payload |
Begin met whole‑document extraction om te bevestigen dat een bestand commentaren bevat die actie vereisen, en schakel vervolgens over naar per‑page extraction zodra je feedback naar een specifieke sectie moet routeren.
Best Practices and Tips
- Dispose
Parserpromptly: wrap it in ausingblock to free native resources. - Distinguish
nullfrom empty:GetAnnotationsreturningnullmeans the format isn’t supported; an empty collection means the document has no comments. - Check
Features.Annotationsin batch jobs: skip unsupported files early instead of relying on anullcheck deep inside your loop. - Reuse the page‑tagged list: build it once with
ExtractAnnotationsByPageand feed both the CSV and JSON exporters from the same data, so the two outputs never drift apart. - Security: annotation text is free‑form reviewer input – treat it the same as any other untrusted string before rendering it in a UI or report.
Conclusion
GroupDocs.Parser gives you a direct, programmatic way to pull reviewer comments out of a PDF instead of hunting for them by hand. By extracting annotations for the whole document, tagging them by page, or folding them into the regular text stream, you can build review workflows that surface feedback the moment a document lands in your pipeline. Export the results to CSV or JSON and wire them straight into the tools your team already uses.
Next steps:
- Explore the GetAnnotations API reference for the full method signature and overloads.
- Learn how to extract text from PDF documents alongside annotations for a complete content pipeline.
- Check out additional sample projects on GitHub for batch‑processing scenarios (Examples Repo).