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.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="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:
<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:
annBar.open();
annBar.close();L'API pubblica del Ribbon è:
| Metodo | Scopo |
|---|---|
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):
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):
| 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 | Bordo + colori 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 è 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
| 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 le annotazioni incorporate
// 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
- Apri il documento e ottieni il suo token.
- Carica l'XML o i 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 è necessario un deliverable appiattito.
- 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
ImageAnnotationrelativo è 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
| Sintomo | Verifica |
|---|---|
| Il ribbon delle annotazioni è mancante | Capacità Annotation e i quattro flag CSS/script delle annotazioni |
| 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 l'incorporamento |
| Il documento riaperto non ha annotazioni | Persisti XML/dati al di fuori della sessione del viewer, poi caricali nel nuovo token |
Questa pagina è stata utile?