💡 Exemplo completo em funcionamento disponível no GitHub:
extract-annotations-from-pdf-using-groupdocs-parser-dotnet
Introdução
Um PDF que passou por revisão geralmente contém mais do que seu texto visível – notas adesivas, observações destacadas e comentários embutidos deixados pelos revisores. Percorrer página por página para encontrá‑los não escala quando um documento passa por várias rodadas de feedback. GroupDocs.Parser é uma biblioteca .NET que lê programaticamente as anotações incorporadas de um documento, transformando comentários dispersos dos revisores em dados estruturados que seu código pode manipular. Este tutorial mostra como extrair anotações de um PDF inteiro, detalhá‑las página por página, obtê‑las junto ao texto do documento e exportar os resultados para CSV ou JSON.
Encontrei esse problema ao criar um rastreador de revisões para uma equipe de documentação: uma nota de lançamento de 40 páginas havia passado por três revisores, e abrir o arquivo manualmente para encontrar cada comentário demorava mais do que realmente corrigir os problemas apontados. Extrair as anotações em poucas linhas de código transformou isso em uma tarefa de dois minutos.
Nas seções a seguir você aprenderá a:
- Extrair todas as anotações de um PDF em uma única passagem.
- Marcar cada anotação com a página à qual ela pertence.
- Obter texto do documento e texto da anotação juntos em uma única leitura.
- Serializar os resultados para CSV ou JSON para ferramentas downstream.
Por que extrair anotações de PDF é importante
Ler anotações de PDF programaticamente é útil para:
- Fluxos de revisão: Coletar cada comentário do revisor sem abrir o arquivo em um visualizador de PDF.
- Colaboração: Exibir seções destacadas ou anotadas diretamente dentro de suas próprias ferramentas.
- Auditoria: Manter um registro das marcações deixadas em um documento ao longo do tempo, mesmo depois de ele ser achatado ou finalizado.
GroupDocs.Parser adicionou extração nativa de anotações para documentos PDF na versão 26.7 através do método GetAnnotations, juntamente com a nova opção IncludeAnnotations em TextOptions para inserir o texto da anotação em uma leitura de texto regular.
Pré-requisitos
- .NET 6.0 ou superior
- GroupDocs.Parser for .NET 26.7+ (licença temporária)
- Um arquivo PDF com anotações existentes (por exemplo,
document-with-annotations.pdf)
Instale via NuGet:
dotnet add package GroupDocs.Parser
Como extrair anotações de um documento PDF?
Resposta: Carregue o arquivo com Parser, então chame GetAnnotations() para o documento inteiro ou GetAnnotations(pageIndex) para uma única página. Cada resultado é uma coleção de objetos AnnotationItem cujo atributo Value contém o texto do comentário. Se preferir ver os comentários embutidos no conteúdo regular do documento, defina IncludeAnnotations em TextOptions e chame GetText em vez disso.
Extração de Documento Inteiro
O trecho a seguir extrai todas as anotações do arquivo em uma única chamada, que é a forma mais rápida de verificar se um documento possui comentários abertos.
// 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;
Pontos‑chave:
GetAnnotations()retornanullquando a extração de anotações não é suportada para o documento, e uma coleção vazia quando o documento simplesmente não possui nenhuma.- Cada
AnnotationItemexpõe seu texto através da propriedadeValue– esse é o único dado que o SDK atualmente relata. - Nenhuma atribuição de página é incluída aqui; use a sobrecarga por página abaixo se precisar disso.
Extração por Página
Quando a localização de um comentário importa, percorra as páginas do documento e chame GetAnnotations(pageIndex) para cada uma.
// 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;
Pontos‑chave:
GetDocumentInfo().PageCountcontrola o laço; não há um “contagem de páginas de anotação” separada.GetAnnotations(pageIndex)usa um índice zero‑based, correspondendo a todos os outros métodos de nível de página da API.- A lista resultante de
AnnotationRecordtem exatamente o formato que uma exportação CSV ou JSON precisa.
Extraindo Texto Junto com Anotações
Em vez de duas passagens sobre o documento, você pode incorporar o texto da anotação diretamente na saída da extração 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;
}
}
Pontos‑chave:
IncludeAnnotationsé uma propriedade emTextOptions, portanto funciona com a mesma chamadaGetTextque você já usa para extração de texto simples.- Útil quando você deseja uma saída única no estilo transcrição, em vez de uma lista separada de comentários.
- Combine com
GetText(pageIndex, options)se precisar disso apenas para uma página.
Verificando o Suporte a Anotações Primeiro
Nem todo formato suporta anotações, então vale a pena verificar antes de construir lógica em torno de GetAnnotations.
// Returns true if the loaded document format supports annotation extraction
using (var parser = new Parser(path))
{
return parser.Features.Annotations;
}
Pontos‑chave:
Features.Annotationsé um simples flag booleano na instânciaParser.- Verificá‑lo antecipadamente torna a intenção explícita, embora
GetAnnotationsjá falhe graciosamente retornandonull.
Exportando as Anotações para CSV
Uma exportação CSV permite que revisores abram a lista de comentários diretamente no Excel. O método abaixo grava um arquivo de duas colunas (page,value) a partir dos registros marcados por página criados anteriormente.
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());
Pontos‑chave:
CsvEscapecoloca aspas de forma segura em campos que contêm vírgulas, aspas ou quebras de linha.- O arquivo resultante abre diretamente no Excel ou pode ser encaminhado para uma ferramenta de tickets.
Helper: CsvEscape
if (string.IsNullOrEmpty(s)) return string.Empty;
if (s.Contains(",") || s.Contains("\"") || s.Contains("\n"))
{
return "\"" + s.Replace("\"", "\"\"") + "\"";
}
return s;
Exportando as Anotações para JSON
Para pipelines que consomem comentários programaticamente, um array JSON costuma ser mais adequado que um 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());
Pontos‑chave:
- A saída é um array plano de objetos
{ page, value }– fácil de desserializar por qualquer serviço downstream. Escapemantém a carga válida JSON sem precisar de uma biblioteca de serialização.
Helper: Escape
return s?.Replace("\\", "\\\\").Replace("\"", "\\\"") ?? string.Empty;
Comparando Métodos: Quando Usar Cada Um
| Método | Melhor Para | Principais Vantagens | Limitações |
|---|---|---|---|
| Extração de Documento Inteiro | Verificação rápida de “há comentários?” | Chamada única, código mais simples | Sem atribuição de página |
| Extração por Página | Roteamento de feedback para a seção correta | Resultados marcados por página, prontos para exportar | Uma chamada extra por página |
| Texto Combinado + Anotações | Transcrição única legível | Nenhuma segunda passagem sobre o documento | Comentários não são separados do texto principal |
| Exportação CSV | Rastreamento de revisão baseado em planilhas | Fácil de abrir no Excel, legível por humanos | Limitado a estrutura plana |
| Exportação JSON | Pipelines automatizados, sistemas de tickets | Estruturado, legível por máquina | Carga útil um pouco maior |
Comece com a extração de documento inteiro para confirmar se um arquivo tem comentários que valem a pena ser tratados, depois passe para a extração por página quando precisar direcionar o feedback a uma seção específica.
Melhores Práticas e Dicas
- Dispose
Parserprontamente: envolva‑o em um blocousingpara liberar recursos nativos. - Distinga
nullde vazio:GetAnnotationsretornandonullsignifica que o formato não é suportado; uma coleção vazia indica que o documento não tem comentários. - Verifique
Features.Annotationsem jobs em lote: ignore arquivos não suportados logo no início, em vez de depender de uma verificaçãonullprofunda no seu laço. - Reutilize a lista marcada por página: construa‑a uma vez com
ExtractAnnotationsByPagee alimente tanto o exportador CSV quanto o JSON a partir dos mesmos dados, assim as duas saídas nunca divergem. - Segurança: o texto da anotação é entrada livre do revisor – trate‑o como qualquer outra string não confiável antes de renderizá‑lo em UI ou relatório.
Conclusão
GroupDocs.Parser oferece uma forma direta e programática de puxar comentários de revisores de um PDF em vez de caçá‑los manualmente. Ao extrair anotações para o documento inteiro, marcá‑las por página ou incorporá‑las ao fluxo de texto regular, você pode construir fluxos de revisão que exibem feedback no momento em que o documento entra na sua pipeline. Exporte os resultados para CSV ou JSON e conecte‑os diretamente às ferramentas que sua equipe já usa.
Próximos passos:
- Explore a referência da API GetAnnotations para a assinatura completa do método e suas sobrecargas.
- Aprenda como extrair texto de documentos PDF junto com anotações para um pipeline de conteúdo completo.
- Confira projetos de exemplo adicionais no GitHub para cenários de processamento em lote (Repositório de Exemplos).