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
@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:

html
<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:

html
<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:

javascript
annBar.open();
annBar.close();

A API pública da Faixa é:

MétodoPropó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):

csharp
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):

TipoObservações
StampAnnotationCarimbo de texto com tamanho de fonte, borda, cor; suporta Opacity, Rotate
NoteAnnotationNota adesiva com texto, cor de fundo, tamanho de fonte, TitleColor
RectangleAnnotationBorda + cores de preenchimento, Title/ShowTitle
CircleAnnotationBorda + preenchimento, ShowBorder
EllipseAnnotationBorda + preenchimento, ShowBorder
TriangleAnnotationCor da borda, BackColor, ShowBorder
LineAnnotationLinha reta com largura e cor
ArrowAnnotationLinha com ponta de seta; Direction configurável (tipo ArrowDirection, pontos cardeais, padrão E)
FreehandAnnotationTraço livre a partir de pontos codificados FreehandData
ImageAnnotationImagem 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

MembroPropó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

csharp
// 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

  1. Abra o documento e obtenha seu token.
  2. Carregue o XML ou dados codificados previamente armazenados nesse token.
  3. Permita que o widget leia e edite as anotações da sessão.
  4. Recupere o XML com GetAnnotationXML(token) quando sua aplicação decidir persistir.
  5. Exporte PDF/PNG quando for necessário um entregável achatado.
  6. 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

SintomaVerificação
Faixa de anotação está ausenteAnnotation capability e as quatro flags de CSS/script de anotação
Callback de salvamento relata um erroExpiração do token/sessão e middleware BasePath
Anotações C# não aparecemA 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çãoO servidor pode alcançar a URL da imagem durante a incorporação
Documento reaberto não tem anotaçõesPersista XML/dados fora da sessão do visualizador, então carregue-os no novo token

Esta página foi útil?