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 memorizza 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 è controllato dalla capacità di licenza Annotation (concessa automaticamente con una licenza Temporanea attiva).

Abilita l'interfaccia delle annotazioni

L'annotazione è un modulo del Viewer, non una barra degli strumenti autonoma. La pagina completa deve includere le risorse del Viewer, la barra degli strumenti del Viewer, il mount del 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 controllati 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="Controlli del visualizzatore di documenti">
    <!-- 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 del 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 viewer.

Apri e chiudi il Ribbon da qualsiasi barra degli strumenti del Viewer gestita dall'host:

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

L'API pubblica del Ribbon è:

MetodoScopo
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 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/cancellazione dopo le modifiche dell'host
headerSlot()Ottieni lo slot opzionale di estensione dell'intestazione per i controlli gestiti dall'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 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; persisti l'XML o l'involucro di annotazione codificato nella tua applicazione se le annotazioni devono sopravvivere 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) =>
{
    // 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),
            $"PAGINA {page}", 28, 4, Color.Maroon)
        {
            Opacity = 60,
            Rotate  = -8
        });
    }

    manager.Add(new NoteAnnotation(1, new Rectangle(420, 150, 220, 120),
        "Caricato dal codice C#.", Color.FromArgb(255, 255, 255, 170), 14));

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

Tipi di annotazione

Tutti i tipi si trovano in Doconut.Annotations ed ereditano 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
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 è risolto rispetto all'host della richiesta quando l'annotazione viene aggiunta (solo il recupero dell'immagine avviene al momento dell'incorporamento) — 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 le annotazioni incorporate

csharp
// PDF di tutte le pagine con annotazioni renderizzate sopra
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", "esportazione.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", "annotazioni-png.zip");
});

Le esportazioni utilizzano lo stesso processo di incorporamento della resa su 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 l'XML o i 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 è necessario un deliverable appiattito.
  6. Chiudi la sessione del documento.

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

Note su sicurezza e rendering

  • Le richieste di annotazione utilizzano la stessa sicurezza sessione/token delle richieste di pagina.
  • Un URL ImageAnnotation relativo è risolto dall'host della richiesta e deve rimanere raggiungibile dal server al momento dell'incorporamento.
  • Convalida e controlla qualsiasi URL di immagine fornito dall'utente per evitare falsificazioni di richieste lato server.
  • Le esportazioni applicano la stessa decisione di licenza/acqua personalizzata della resa delle pagine su schermo.
  • Carichi di dati freehand grandi e esportazioni ad alta risoluzione aumentano l'uso di memoria; testa documenti realistici e valori di zoom.

Risoluzione dei problemi

SintomoVerifica
Il ribbon delle annotazioni è mancanteCapacità Annotation e i quattro flag CSS/script delle annotazioni
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 l'incorporamento
Il documento riaperto non ha annotazioniPersisti XML/dati al di fuori della sessione del viewer, poi caricali nel nuovo token

Questa pagina è stata utile?