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.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:
<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:
<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:
annBar.open();
annBar.close();L'API pubblica del Ribbon è:
| Method | Purpose |
|---|---|
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):
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):
| Tipo | Note |
|---|---|
StampAnnotation | Timbro di testo con dimensione del carattere, bordo, colore; supporta Opacity, Rotate |
NoteAnnotation | Nota adesiva con testo, colore di sfondo, dimensione del carattere, TitleColor |
RectangleAnnotation | Colori del bordo e di riempimento, Title/ShowTitle |
CircleAnnotation | Bordo + riempimento, ShowBorder |
EllipseAnnotation | Bordo + riempimento, ShowBorder |
TriangleAnnotation | Colore del bordo, BackColor, ShowBorder |
LineAnnotation | Linea retta con spessore e colore |
ArrowAnnotation | Linea con punta di freccia; Direction impostabile (tipo ArrowDirection, punti cardinali, predefinito E) |
FreehandAnnotation | Tratto libero da punti codificati FreehandData |
ImageAnnotation | Immagine 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
| Membro | Scopo |
|---|---|
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
// 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
- Apri il documento e ottieni il suo token.
- Carica XML o dati codificati precedentemente memorizzati in quel token.
- Consenti al widget di leggere e modificare le annotazioni della sessione.
- Recupera l'XML con
GetAnnotationXML(token)quando la tua applicazione decide di persistere. - Esporta PDF/PNG quando è richiesto un deliverable appiattito.
- 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
ImageAnnotationviene 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
| Sintomo | Verifica |
|---|---|
| La barra degli strumenti di annotazione è mancante | capacità Annotation e i quattro flag CSS/script di annotazione |
| Il callback di salvataggio segnala un errore | Scadenza del token/sessione e middleware BasePath |
| Le annotazioni C# non compaiono | La numerazione delle pagine parte da 1 e i dati sono stati caricati nel token attivo |
| L'annotazione immagine appare sullo schermo ma non nell'esportazione | Il server può raggiungere l'URL dell'immagine durante il rendering |
| Il documento riaperto non ha annotazioni | Persisti XML/dati al di fuori della sessione del visualizzatore, quindi caricali nel nuovo token |
Questa pagina è stata utile?