Annotazioni

Aggiungi il supporto alle annotazioni al visualizzatore

Le annotazioni in Doconut funzionano in due direzioni: gli utenti le disegnano nel widget del browser e il server le conserva per pagina, oppure il tuo codice le crea programmaticamente e le carica in una sessione aperta. In entrambi i casi vengono visualizzate nelle pagine e possono essere incorporate nelle esportazioni PDF/PNG.

Il supporto alle annotazioni è limitato dalla capacità di licenza Annotation (concessa automaticamente con una licenza Temporanea attiva).

Abilita l'interfaccia delle annotazioni

L'annotazione è un modulo Viewer, non una barra degli strumenti autonoma. La pagina completa deve includere le risorse Viewer, la barra degli strumenti Viewer, il mount Viewer e l'objViewer inizializzato; il Ribbon delle annotazioni viene quindi montato e collegato alla stessa istanza.

Emetti i bundle delle annotazioni insieme ai bundle del viewer — sono limitati dalla licenza, quindi i tag compaiono solo quando la capacità è disponibile:

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
}))

Mantieni la composizione completa del Viewer visibile nel markup:

html
<nav id="toolbar" aria-label="Document viewer controls">
    <!-- Controlli del visualizzatore, incluso il pulsante che apre Annotation -->
</nav>
<div id="annBarMount"></div>
<div id="divDocViewer"><div id="div_ctlDoc"></div></div>

Il bundle delle annotazioni genera il DOM del Ribbon all'interno di annBarMount; non è necessario copiare i suoi pulsanti o il markup della finestra di dialogo. Inizializza prima docViewer, quindi crea il Ribbon solo quando il server conferma che le Annotazioni sono licenziate:

html
<script>
    let annBar = null;
    let currentToken = '';

    const objViewer = $('#div_ctlDoc').docViewer({
        BasePath: '/',
        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>

Il salvataggio dal Ribbon invia i dati tramite il middleware (AnnSave), che li memorizza nella sessione del documento per pagina. Il caricamento (AnnLoad) avviene automaticamente quando una pagina con annotazioni viene renderizzata. I quattro callback onAnn* mantengono il Ribbon sincronizzato con il ciclo di vita del viewer.

Apri e chiudilo da qualsiasi barra degli strumenti Viewer di proprietà dell'host:

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

L'API pubblica del Ribbon è:

MethodPurpose
attach(objViewer)Collega il Ribbon al viewer inizializzato; richiesto una sola volta
open() / close()Entra o esci dalla modalità di modifica delle annotazioni
reset()Riporta il Ribbon al suo stato chiuso, non in modalità modifica
isOpen() / annotating()Leggi lo stato del Ribbon / lo stato di modifica delle annotazioni del viewer
reopenEditable()Ricarica le annotazioni della pagina corrente come oggetti modificabili
updateActionState()Aggiorna la disponibilità dei controlli di salvataggio/eliminazione dopo le modifiche dell'host
headerSlot()Ottieni lo slot opzionale di estensione dell'intestazione per i controlli di proprietà dell'host

onStatus, onToast, onLayout, onEditStart e onEditEnd sono callback opzionali dell'host. L'oggetto endpoints può fornire in più exportPdf, exportPng, imageUpload e imageList; i controlli senza un endpoint configurato rimangono nascosti. Per la sequenza di avvio combinata di Viewer, Search e Annotation, vedi Guida rapida.

Il bundle delle annotazioni aggiunge gli strumenti di authoring del browser, ma i dati appartengono ancora alla sessione del documento lato server identificata dal token. Riaprire la sorgente crea una nuova sessione; conserva l'XML o l'involucro di annotazione codificato nella tua applicazione se le annotazioni devono persistere oltre la durata della sessione.

Crea annotazioni in C#

Ottieni un manager legato alla sessione aperta, aggiungi annotazioni e caricale (con using Doconut.Annotations; per i tipi e using System.Drawing; per Rectangle/Color):

csharp
app.MapPost("/api/annotations/load-sample", (string token, Viewer viewer) =>
{
    // Vincolato alle dimensioni della pagina della sessione aperta
    var manager = viewer.GetAnnotationManager(token);
    var pageCount = viewer.GetPageCount(token);

    // Un timbro per pagina
    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));

    // Carica nella sessione — il widget li recupera tramite AnnLoad e il
    // renderer li incorpora nelle esportazioni immagine/PDF.
    viewer.LoadAnnotationData(token, manager);
    return Results.Ok();
});

Tipi di annotazione

Tutti i tipi si trovano in Doconut.Annotations e derivano da BaseAnnotation (numero di pagina + Rectangle di delimitazione):

TipoNote
StampAnnotationTimbro di testo con dimensione del carattere, bordo, colore; supporta Opacity, Rotate
NoteAnnotationNota adesiva con testo, colore di sfondo, dimensione del carattere, TitleColor
RectangleAnnotationColori del bordo e di riempimento, Title/ShowTitle
CircleAnnotationBordo + riempimento, ShowBorder
EllipseAnnotationBordo + riempimento, ShowBorder
TriangleAnnotationColore del bordo, BackColor, ShowBorder
LineAnnotationLinea retta con spessore e colore
ArrowAnnotationLinea con punta di freccia; Direction impostabile (tipo ArrowDirection, punti cardinali, predefinito E)
FreehandAnnotationTratto libero da punti codificati FreehandData
ImageAnnotationImmagine da un URL. Un URL relativo viene risolto rispetto all'host della richiesta quando l'annotazione viene aggiunta (solo il recupero dell'immagine avviene al momento del rendering) — deve essere raggiungibile dal server (ad esempio un file sotto wwwroot servito da UseStaticFiles)

API di AnnotationManager

MembroScopo
Add(BaseAnnotation)Accoda un'annotazione
GetAnnotations() / GetAnnotations(int page)Ispeziona ciò che il manager contiene
ClearAnnotations() / ClearAnnotations(int page)Rimuove tutti / per pagina
GetAnnotationData() / GetAnnotationData(int page)Stringa di dati di annotazione codificata — un involucro Base64 (ciò che il widget consuma)
GetAnnotationXml()Forma XML

Viewer rispecchia le operazioni di caricamento/lettura su una sessione: LoadAnnotationData(token, manager) o LoadAnnotationData(token, encodedData) (l'involucro Base64 da GetAnnotationData()), LoadAnnotationXML(token, xml), GetAnnotationXML(token).

Esporta con annotazioni incorporate

csharp
// PDF di tutte le pagine con le annotazioni renderizzate su di esse
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");
});

// Oppure un ZIP di PNG per pagina
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");
});

Le esportazioni usano lo stesso motore di rendering delle annotazioni a schermo, quindi ciò che gli utenti vedono è ciò che il file contiene.

Flusso di lavoro di persistenza

  1. Apri il documento e ottieni il suo token.
  2. Carica XML o dati codificati precedentemente memorizzati in quel token.
  3. Consenti al widget di leggere e modificare le annotazioni della sessione.
  4. Recupera l'XML con GetAnnotationXML(token) quando la tua applicazione decide di persistere.
  5. Esporta PDF/PNG quando è richiesto un deliverable appiattito.
  6. Chiudi la sessione del documento.

Non utilizzare il token opaco del visualizzatore come identificatore permanente delle annotazioni. Associa i dati delle annotazioni persistenti ai tuoi identificatori di documento e di versione.

Note sulla sicurezza e sul rendering

  • Le richieste di annotazione utilizzano la stessa sicurezza di sessione/token delle richieste di pagina.
  • Un URL relativo di ImageAnnotation viene risolto dall'host della richiesta e deve rimanere raggiungibile dal server al momento del rendering.
  • Convalidare e controllare qualsiasi URL di immagine fornito dall'utente per evitare falsificazioni di richieste lato server.
  • Le esportazioni applicano la stessa decisione di licenza/marchio personalizzato della resa della pagina a schermo.
  • Payload di disegno a mano libera di grandi dimensioni ed esportazioni ad alta risoluzione aumentano l'uso di memoria; testare documenti realistici e valori di zoom.

Risoluzione dei problemi

SintomoVerifica
La barra degli strumenti di annotazione è mancantecapacità Annotation e i quattro flag CSS/script di annotazione
Il callback di salvataggio segnala un erroreScadenza del token/sessione e middleware BasePath
Le annotazioni C# non compaionoLa numerazione delle pagine parte da 1 e i dati sono stati caricati nel token attivo
L'annotazione immagine appare sullo schermo ma non nell'esportazioneIl server può raggiungere l'URL dell'immagine durante il rendering
Il documento riaperto non ha annotazioniPersisti XML/dati al di fuori della sessione del visualizzatore, quindi caricali nel nuovo token

Questa pagina è stata utile?