💡 Ejemplo completo disponible en GitHub:
extract-annotations-from-pdf-using-groupdocs-parser-dotnet
Introduction
Un PDF que ha pasado por una revisión suele contener más que su texto visible: notas adhesivas, observaciones resaltadas y comentarios en línea dejados por los revisores. Desplazarse por cada página para encontrarlos no escala una vez que un documento ha pasado por varias rondas de retroalimentación. GroupDocs.Parser es una biblioteca .NET que lee las anotaciones incrustadas de un documento de forma programática, convirtiendo los comentarios dispersos de los revisores en datos estructurados que tu código puede procesar. Este tutorial muestra cómo extraer anotaciones de un PDF completo, desglosarlas página por página, obtenerlas junto con el texto del documento y exportar los resultados a CSV o JSON.
Me encontré con este problema mientras construía un rastreador de revisiones para un equipo de documentación: una nota de lanzamiento de 40 páginas había pasado por tres revisores, y abrir manualmente el archivo para encontrar cada comentario tomó más tiempo que corregir los problemas señalados. Extraer las anotaciones en unas pocas líneas de código convirtió eso en una tarea de dos minutos.
En las siguientes secciones aprenderás a:
- Extraer cada anotación de un PDF en una sola pasada.
- Etiquetar cada anotación con la página a la que pertenece.
- Obtener el texto del documento y el texto de la anotación juntos en una única lectura.
- Serializar los resultados a CSV o JSON para herramientas posteriores.
Why Extracting PDF Annotations Matters
Leer anotaciones de PDF de forma programática es útil para:
- Flujos de revisión: recopila cada comentario del revisor sin abrir el archivo en un visor de PDF.
- Colaboración: muestra secciones resaltadas o anotadas directamente dentro de tus propias herramientas.
- Auditoría: conserva un registro de las marcas dejadas en un documento a lo largo del tiempo, incluso después de que se haya aplanado o finalizado.
GroupDocs.Parser añadió extracción nativa de anotaciones para documentos PDF en la versión 26.7 mediante el método GetAnnotations, junto con una nueva opción IncludeAnnotations en TextOptions para incorporar el texto de la anotación en una lectura de texto regular.
Prerequisites
- .NET 6.0 o posterior
- GroupDocs.Parser para .NET 26.7+ (licencia temporal)
- Un archivo PDF con anotaciones existentes (p. ej.,
document-with-annotations.pdf)
Instala vía NuGet:
dotnet add package GroupDocs.Parser
How do I extract annotations from a PDF document?
Respuesta: Carga el archivo con Parser, luego llama a GetAnnotations() para todo el documento o a GetAnnotations(pageIndex) para una sola página. Cada resultado es una colección de objetos AnnotationItem cuyo atributo Value contiene el texto del comentario. Si prefieres ver los comentarios en línea con el contenido regular del documento, establece IncludeAnnotations en TextOptions y llama a GetText en su lugar.
Whole‑Document Extraction
El fragmento siguiente extrae todas las anotaciones del archivo en una única llamada, que es la forma más rápida de comprobar si un documento tiene comentarios abiertos.
// 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;
Puntos clave:
GetAnnotations()devuelvenullcuando la extracción de anotaciones no está soportada para el documento, y una colección vacía cuando el documento simplemente no tiene ninguna.- Cada
AnnotationItemexpone su texto a través de la propiedadValue; ese es el único dato que el SDK reporta actualmente. - No se incluye atribución de página aquí; usa la sobrecarga por página que sigue a continuación si la necesitas.
Per‑Page Extraction
Cuando la ubicación de un comentario importa, recorre las páginas del documento y llama a GetAnnotations(pageIndex) para cada una.
// 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;
Puntos clave:
GetDocumentInfo().PageCountimpulsa el bucle; no existe un “conteo de páginas de anotaciones” separado.GetAnnotations(pageIndex)usa un índice basado en cero, igual que cualquier otro método a nivel de página de la API.- La lista resultante de
AnnotationRecordtiene exactamente la forma que necesita una exportación a CSV o JSON.
Extracting Text Together with Annotations
En lugar de dos pasadas sobre el documento, puedes incorporar el texto de la anotación directamente en la salida de extracción de texto regular.
// 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;
}
}
Puntos clave:
IncludeAnnotationses una propiedad deTextOptions, por lo que funciona con la misma llamadaGetTextque ya usas para extracción de texto plano.- Útil cuando deseas una salida única tipo transcripción en lugar de una lista de comentarios separada.
- Combínalo con
GetText(pageIndex, options)si solo lo necesitas para una página.
Checking Annotation Support First
No todos los formatos admiten anotaciones, por lo que vale la pena comprobarlo antes de construir lógica alrededor de GetAnnotations.
// Returns true if the loaded document format supports annotation extraction
using (var parser = new Parser(path))
{
return parser.Features.Annotations;
}
Puntos clave:
Features.Annotationses una bandera booleana simple en la instancia deParser.- Verificarla al inicio hace que la intención sea explícita, aunque
GetAnnotationsya falla de forma elegante devolviendonull.
Exporting the Annotations to CSV
Una exportación a CSV permite a los revisores abrir la lista de comentarios directamente en Excel. El método a continuación escribe un archivo de dos columnas (page,value) a partir de los registros etiquetados por página creados antes.
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());
Puntos clave:
CsvEscapecita de forma segura los campos que contienen comas, comillas o saltos de línea.- El archivo resultante se abre directamente en Excel o puede canalizarse a una herramienta de tickets.
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
Para canalizaciones que consumen comentarios de forma programática, un arreglo JSON suele ser más adecuado que un CSV plano.
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());
Puntos clave:
- La salida es un arreglo plano de objetos
{ page, value }, fácil de deserializar por cualquier servicio posterior. Escapemantiene la carga útil como JSON válido sin necesidad de una biblioteca de serialización.
Helper: Escape
return s?.Replace("\\", "\\\\").Replace("\"", "\\\"") ?? string.Empty;
Comparing Methods: When to Use Each
| Método | Mejor para | Ventajas clave | Limitaciones |
|---|---|---|---|
| Extracción de documento completo | Ver rápidamente “¿existen comentarios?” | Llamada única, código más sencillo | No incluye atribución de página |
| Extracción por página | Enrutar la retroalimentación a la sección correcta | Resultados con etiqueta de página, listos para exportar | Una llamada extra por página |
| Texto combinado + anotaciones | Obtener una transcripción única | No se necesita una segunda pasada sobre el documento | Los comentarios no están separados del texto principal |
| Exportación a CSV | Seguimiento basado en hojas de cálculo | Fácil de abrir en Excel, legible por humanos | Estructura plana limitada |
| Exportación a JSON | Pipelines automatizados, sistemas de tickets | Estructurado, legible por máquinas | Carga ligeramente mayor |
Comienza con la extracción de documento completo para confirmar que el archivo tiene comentarios que valga la pena procesar, y luego pasa a la extracción por página cuando necesites dirigir la retroalimentación a una sección específica.
Best Practices and Tips
- Descarta
Parserrápidamente: envuélvelo en un bloqueusingpara liberar los recursos nativos. - Distingue
nullde vacío:GetAnnotationsque devuelvenullindica que el formato no es compatible; una colección vacía indica que el documento no tiene comentarios. - Comprueba
Features.Annotationsen trabajos por lotes: omite los archivos no compatibles temprano en lugar de depender de una verificaciónnullprofunda dentro del bucle. - Reutiliza la lista etiquetada por página: constrúyela una vez con
ExtractAnnotationsByPagey alimenta tanto al exportador CSV como al JSON desde los mismos datos, de modo que ambas salidas nunca diverjan. - Seguridad: el texto de la anotación es entrada libre del revisor; trátalo como cualquier otra cadena no confiable antes de renderizarlo en una UI o informe.
Conclusion
GroupDocs.Parser te brinda una forma directa y programática de extraer los comentarios de los revisores de un PDF, en lugar de buscarlos manualmente. Al extraer anotaciones para todo el documento, etiquetarlas por página o incorporarlas al flujo de texto regular, puedes crear flujos de revisión que expongan la retroalimentación en el momento en que el documento llega a tu canalización. Exporta los resultados a CSV o JSON y conéctalos directamente a las herramientas que tu equipo ya utiliza.
Próximos pasos:
- Explora la referencia de la API GetAnnotations para conocer la firma completa del método y sus sobrecargas.
- Aprende a extraer texto de documentos PDF junto con anotaciones para una canalización de contenido completa.
- Consulta proyectos de ejemplo adicionales en GitHub para escenarios de procesamiento por lotes (Repositorio de ejemplos).