Anotações
Adicionar suporte a anotações ao visualizador
As anotações no Doconut funcionam em duas direções: os usuários as desenham no widget do navegador e o servidor as persiste por página, ou seu código as cria programaticamente e as carrega em uma sessão aberta. De qualquer forma, elas são renderizadas nas páginas e podem ser incorporadas em exportações PDF/PNG.
O suporte a anotações é controlado pela capacidade de licença Annotation (concedida automaticamente sob uma licença Temporária ativa).
Habilitar a interface de anotações
Anotação é um módulo do Visualizador, não uma barra de ferramentas independente. A página completa deve incluir os recursos do Visualizador, a barra de ferramentas do Visualizador, o ponto de montagem do Visualizador e o objViewer inicializado; então a Faixa de Anotação é montada e anexada a essa mesma instância.
Emita os pacotes de anotação junto com os pacotes do visualizador — eles são controlados por licença, portanto as tags só aparecem quando a capacidade está disponível:
@Html.Raw(Viewer.ReferenceCss(new CssConfig
{
IncludeViewerCss = true,
IncludeAnnotationCss = true // jquery-ui.min.css + annotationBar.css
}))
@Html.Raw(Viewer.ReferenceScripts(new ScriptConfig
{
IncludeJQuery = true,
IncludeViewerScripts = true,
IncludeAnnotationScripts = true, // jquery-ui, raphael.js, annotation.js
IncludeAnnotationBar = true // the embedded annotation ribbon
}))Mantenha a composição completa do Visualizador visível no markup:
<nav id="toolbar" aria-label="Document viewer controls">
<!-- Viewer controls, including the button that opens Annotation -->
</nav>
<div id="annBarMount"></div>
<div id="divDocViewer"><div id="div_ctlDoc"></div></div>O pacote de Anotação gera o DOM da Faixa dentro de annBarMount; você não precisa copiar seus botões ou marcação de diálogo. Inicialize docViewer primeiro, então crie a Faixa somente quando o servidor confirmar que a Anotação está licenciada:
<script>
let annBar = null;
let currentToken = '';
const objViewer = $('#div_ctlDoc').docViewer({
BasePath: '/doconut',
ResPath: '/doconut-res/images',
onAnnLoaded: () => annBar?.handleAnnLoaded(),
onAnnSaved: () => annBar?.handleAnnSaved(),
onAnnSaveError: () => annBar?.handleAnnSaveError(),
onAnnClosed: () => annBar?.handleAnnClosed(),
onError: (message) => console.error('Viewer error:', message)
});
@if (Viewer.IsAnnotationEnabled)
{
<text>
annBar = $('#annBarMount').doconutAnnotationBar({
docId: 'ctlDoc',
getRequestParams: () => ({ token: currentToken }),
onStatus: (message) => console.log(message),
onToast: (message, type) => console.log(type, message),
onLayout: () => requestAnimationFrame(() => objViewer.Refit())
});
annBar.attach(objViewer);
</text>
}
</script>Salvar a partir da Faixa envia os dados através do middleware (AnnSave), que os armazena na sessão do documento por página. Carregar (AnnLoad) ocorre automaticamente quando uma página com anotações é renderizada. Os quatro callbacks onAnn* mantêm a Faixa sincronizada com o ciclo de vida do visualizador.
Abra e feche-a a partir de qualquer barra de ferramentas do Visualizador controlada pelo host:
annBar.open();
annBar.close();A API pública da Faixa é:
| Método | Propósito |
|---|---|
attach(objViewer) | Conecta a Faixa ao visualizador inicializado; necessário uma única vez |
open() / close() | Inicia ou sai da edição de anotações |
reset() | Retorna a Faixa ao estado fechado, sem edição |
isOpen() / annotating() | Lê o estado da Faixa / o estado de edição de anotações do visualizador |
reopenEditable() | Recarrega as anotações da página atual como objetos editáveis |
updateActionState() | Atualiza a disponibilidade dos controles de salvar/excluir após alterações no host |
headerSlot() | Obtém o slot opcional de extensão de cabeçalho para controles controlados pelo host |
onStatus, onToast, onLayout, onEditStart e onEditEnd são callbacks opcionais do host. O objeto endpoints pode fornecer adicionalmente exportPdf, exportPng, imageUpload e imageList; controles sem um endpoint configurado permanecem ocultos. Para a sequência de inicialização combinada do Visualizador, Busca e Anotação, veja Início Rápido.
O pacote de anotação adiciona as ferramentas de autoria do navegador, mas os dados ainda pertencem à sessão de documento do lado do servidor identificada pelo token. Reabrir a fonte cria uma nova sessão; persista o XML ou o envelope de anotação codificado em sua aplicação se as anotações precisarem sobreviver além da vida útil da sessão.
Criar anotações em C#
Obtenha um gerenciador vinculado à sessão aberta, adicione anotações e carregue-as (com using Doconut.Annotations; para os tipos e using System.Drawing; para Rectangle/Color):
app.MapPost("/api/annotations/load-sample", (string token, Viewer viewer) =>
{
// Bound to the open session's page dimensions
var manager = viewer.GetAnnotationManager(token);
var pageCount = viewer.GetPageCount(token);
// One stamp per page
for (int page = 1; page <= pageCount; page++)
{
manager.Add(new StampAnnotation(page, new Rectangle(30, 20, 240, 90),
$"PAGE {page}", 28, 4, Color.Maroon)
{
Opacity = 60,
Rotate = -8
});
}
manager.Add(new NoteAnnotation(1, new Rectangle(420, 150, 220, 120),
"Loaded from C# code.", Color.FromArgb(255, 255, 255, 170), 14));
// Load into the session — the widget fetches them via AnnLoad and the
// renderer burns them into image/PDF exports.
viewer.LoadAnnotationData(token, manager);
return Results.Ok();
});Tipos de anotação
Todos os tipos estão em Doconut.Annotations e herdam de BaseAnnotation (número da página + Rectangle delimitador):
| Tipo | Observações |
|---|---|
StampAnnotation | Carimbo de texto com tamanho de fonte, borda, cor; suporta Opacity, Rotate |
NoteAnnotation | Nota adesiva com texto, cor de fundo, tamanho de fonte, TitleColor |
RectangleAnnotation | Borda + cores de preenchimento, Title/ShowTitle |
CircleAnnotation | Borda + preenchimento, ShowBorder |
EllipseAnnotation | Borda + preenchimento, ShowBorder |
TriangleAnnotation | Cor da borda, BackColor, ShowBorder |
LineAnnotation | Linha reta com largura e cor |
ArrowAnnotation | Linha com ponta de seta; Direction configurável (tipo ArrowDirection, pontos cardeais, padrão E) |
FreehandAnnotation | Traço livre a partir de pontos codificados FreehandData |
ImageAnnotation | Imagem a partir de uma URL. Uma URL relativa é resolvida em relação ao host da requisição quando a anotação é adicionada (apenas a obtenção da imagem ocorre no momento da incorporação) — ela deve ser acessível ao servidor (por exemplo, um arquivo em wwwroot servido por UseStaticFiles). |
A API AnnotationManager
| Membro | Propósito |
|---|---|
Add(BaseAnnotation) | Enfileira uma anotação |
GetAnnotations() / GetAnnotations(int page) | Inspeciona o que o gerenciador contém |
ClearAnnotations() / ClearAnnotations(int page) | Remove todas / por página |
GetAnnotationData() / GetAnnotationData(int page) | String codificada de dados de anotação — um envelope Base64 (o que o widget consome) |
GetAnnotationXml() | Forma XML |
Viewer espelha as operações de carregamento/leitura contra uma sessão: LoadAnnotationData(token, manager) ou LoadAnnotationData(token, encodedData) (o envelope Base64 de GetAnnotationData()), LoadAnnotationXML(token, xml), GetAnnotationXML(token).
Exportar com anotações incorporadas
// PDF of all pages with annotations rendered onto them
app.MapGet("/api/annotations/export-pdf", async (string token, Viewer viewer) =>
{
byte[] pdf = await viewer.ExportAnnotationsToPdfAsync(token, zoom: 100);
return Results.File(pdf, "application/pdf", "export.pdf");
});
// Or a ZIP of per-page PNGs
app.MapGet("/api/annotations/export-png-zip", async (string token, Viewer viewer) =>
{
byte[] zip = await viewer.ExportAnnotationsToPngZipAsync(token, zoom: 100);
return Results.File(zip, "application/zip", "annotations-png.zip");
});Exportações usam o mesmo processo de incorporação da renderização na tela, portanto o que os usuários veem é o que o arquivo contém.
Fluxo de persistência
- Abra o documento e obtenha seu token.
- Carregue o XML ou dados codificados previamente armazenados nesse token.
- Permita que o widget leia e edite as anotações da sessão.
- Recupere o XML com
GetAnnotationXML(token)quando sua aplicação decidir persistir. - Exporte PDF/PNG quando for necessário um entregável achatado.
- Feche a sessão do documento.
Não use o token opaco do visualizador como identificador permanente de anotação. Associe os dados de anotação persistidos com seus próprios identificadores de documento e versão.
Notas de segurança e renderização
- Solicitações de anotação usam a mesma segurança de sessão/token das solicitações de página.
- Uma URL relativa de
ImageAnnotationé resolvida a partir do host da requisição e deve permanecer acessível ao servidor no momento da incorporação. - Valide e controle qualquer URL de imagem fornecida pelo usuário para evitar falsificação de solicitações no lado do servidor.
- Exportações aplicam a mesma decisão de licença/marca d'água personalizada da renderização de página na tela.
- Cargas úteis grandes de desenhos livres e exportações de alta resolução aumentam o uso de memória; teste documentos realistas e valores de zoom.
Solução de Problemas
| Sintoma | Verificação |
|---|---|
| Faixa de anotação está ausente | Annotation capability e as quatro flags de CSS/script de anotação |
| Callback de salvamento relata um erro | Expiração do token/sessão e middleware BasePath |
| Anotações C# não aparecem | A numeração de páginas começa em 1 e os dados foram carregados no token ativo |
| Anotação de imagem aparece na tela mas não na exportação | O servidor pode alcançar a URL da imagem durante a incorporação |
| Documento reaberto não tem anotações | Persista XML/dados fora da sessão do visualizador, então carregue-os no novo token |
Esta página foi útil?