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. L'intera pagina 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 visualizzatore — 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 l'intera composizione 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: '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>

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 visualizzatore.

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

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

L'API pubblica del Ribbon è:

MetodoScopo
attach(objViewer)Collega il Ribbon al visualizzatore 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 visualizzatore
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 di estensione opzionale 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 inoltre 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 Avvio rapido.

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 sopravvivere oltre la durata della sessione.

Creare 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) =>
{
    // Legato 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
StampAnnotationContrassegno di testo con dimensione del carattere, bordo, colore; supporta Opacity, Rotate
NoteAnnotationNota adesiva con testo, colore di sfondo, dimensione del carattere, TitleColor
RectangleAnnotationBordo + colori 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 es. un file in 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 sopra 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 utilizzano lo stesso motore di rendering della visualizzazione 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 file finale appiattito.
  6. Chiudi la sessione del documento.

Non utilizzare il token opaco del visualizzatore come identificatore permanente dell'annotazione. Associa i dati di annotazione persistiti ai tuoi identificatori di documento e di versione.

Note su sicurezza e rendering

  • Le richieste di annotazione utilizzano la stessa sicurezza sessione/token delle richieste di pagina.
  • Un URL relativo ImageAnnotation viene risolto dall'host della richiesta e deve rimanere raggiungibile dal server al momento del burn.
  • Convalida e controlla qualsiasi URL 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 sullo schermo.
  • Grandi payload di disegno libero e esportazioni ad alta risoluzione aumentano l'uso di memoria; testa documenti realistici e valori di zoom.

Risoluzione dei problemi

SintomoControllo
La barra degli strumenti di annotazione è mancanteAnnotation capacità 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 appaionoLa numerazione delle pagine è basata su 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 burn-in
Il documento riaperto non ha annotazioniPersisti XML/dati al di fuori della sessione del visualizzatore, poi caricali nel nuovo token

Questa pagina è stata utile?