Annotationen
Fügen Sie dem Viewer Annotationsunterstützung hinzu
Annotationen in Doconut funktionieren in zwei Richtungen: Benutzer zeichnen sie im Browser‑Widget und der Server speichert sie pro Seite, oder Ihr Code erstellt sie programmgesteuert und lädt sie in eine offene Sitzung. In beiden Fällen werden sie auf den Seiten dargestellt und können in PDF/PNG‑Exporten eingebrannt werden.
Die Annotationsunterstützung ist durch die Lizenz‑Fähigkeit Annotation geschützt (wird automatisch bei einer aktiven temporären Lizenz gewährt).
Aktivieren der Annotations‑UI
Annotation ist ein Viewer‑Modul, keine eigenständige Symbolleiste. Die komplette Seite muss die Viewer‑Ressourcen, die Viewer‑Symbolleiste, den Viewer‑Mount und das initialisierte objViewer enthalten; das Annotation‑Ribbon wird dann eingebunden und an dieselbe Instanz angehängt.
Geben Sie die Annotations‑Bundles zusammen mit den Viewer‑Bundles aus – sie sind lizenzgesteuert, sodass die Tags nur erscheinen, wenn die Fähigkeit verfügbar ist:
@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
}))Behalten Sie die komplette Viewer‑Zusammensetzung im Markup sichtbar:
<nav id="toolbar" aria-label="Document viewer controls">
<!-- Viewer controls, including the button that opens Annotation -->
</nav>
<div id="annBarMount"></div>
<div id="divDocViewer"><div id="div_ctlDoc"></div></div>Das Annotation‑Bundle erzeugt das Ribbon‑DOM innerhalb von annBarMount; Sie müssen dessen Schaltflächen oder Dialog‑Markup nicht kopieren. Initialisieren Sie zuerst docViewer und erstellen Sie das Ribbon nur, wenn der Server bestätigt, dass Annotation lizenziert ist:
<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>Das Speichern über das Ribbon sendet Daten über die Middleware (AnnSave), die sie pro Seite in der Dokument‑Sitzung speichert. Das Laden (AnnLoad) erfolgt automatisch, wenn eine Seite mit Annotationen gerendert wird. Die vier onAnn*‑Callbacks halten das Ribbon mit dem Viewer‑Lebenszyklus synchron.
Öffnen und schließen Sie es über jede vom Host besessene Viewer‑Symbolleiste:
annBar.open();
annBar.close();Die öffentliche Ribbon‑API lautet:
| Methode | Zweck |
|---|---|
attach(objViewer) | Verbinden Sie das Ribbon mit dem initialisierten Viewer; einmalig erforderlich |
open() / close() | Beginnt bzw. beendet die Annotationsbearbeitung |
reset() | Setzt das Ribbon in den geschlossenen, nicht‑bearbeitenden Zustand zurück |
isOpen() / annotating() | Liest den Ribbon‑Zustand / den Annotations‑Bearbeitungszustand des Viewers |
reopenEditable() | Lädt die Annotationen der aktuellen Seite erneut als bearbeitbare Objekte |
updateActionState() | Aktualisiert die Verfügbarkeit von Speicher‑/Lösch‑Steuerelementen nach Host‑Änderungen |
headerSlot() | Gibt den optionalen Header‑Erweiterungs‑Slot für host‑eigene Steuerelemente zurück |
onStatus, onToast, onLayout, onEditStart und onEditEnd sind optionale Host‑Callbacks. Das endpoints‑Objekt kann zusätzlich exportPdf, exportPng, imageUpload und imageList bereitstellen; Steuerelemente ohne konfigurierten Endpunkt bleiben verborgen. Für die Startsequenz des kombinierten Viewers, der Suche und der Annotation siehe Schnellstart.
Das Annotation‑Bundle fügt die Browser‑Authoring‑Werkzeuge hinzu, aber die Daten gehören weiterhin zur serverseitigen Dokument‑Sitzung, die durch das Token identifiziert wird. Das erneute Öffnen der Quelle erzeugt eine neue Sitzung; speichern Sie das XML oder den codierten Annotations‑Umschlag in Ihrer Anwendung, wenn Annotationen über die Lebensdauer der Sitzung hinaus erhalten bleiben sollen.
Annotationen in C# erstellen
Holen Sie sich einen Manager, der an die offene Sitzung gebunden ist, fügen Sie Annotationen hinzu und laden Sie sie (mit using Doconut.Annotations; für die Typen und using System.Drawing; für Rectangle/Color):
app.MapPost("/api/annotations/load-sample", (string token, Viewer viewer) =>
{
// Bound to the open session's page dimensions
var manager = viewer.GetAnnotationManager(token);
var pageCount = viewer.GetPageCount(token);
// One stamp per page
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));
// Load into the session — the widget fetches them via AnnLoad and the
// renderer burns them into image/PDF exports.
viewer.LoadAnnotationData(token, manager);
return Results.Ok();
});Annotations‑Typen
Alle Typen befinden sich in Doconut.Annotations und erben von BaseAnnotation (Seitennummer + begrenzendes Rectangle):
| Typ | Hinweise |
|---|---|
StampAnnotation | Textstempel mit Schriftgröße, Rand, Farbe; unterstützt Opacity, Rotate |
NoteAnnotation | Haftnotiz mit Text, Hintergrundfarbe, Schriftgröße, TitleColor |
RectangleAnnotation | Rand‑ und Füllfarben, Title/ShowTitle |
CircleAnnotation | Rand‑ und Füllung, ShowBorder |
EllipseAnnotation | Rand‑ und Füllung, ShowBorder |
TriangleAnnotation | Randfarbe, BackColor, ShowBorder |
LineAnnotation | Gerade Linie mit Breite und Farbe |
ArrowAnnotation | Linie mit Pfeilspitze; einstellbare Direction (Typ ArrowDirection, Himmelsrichtungen, Standard E) |
FreehandAnnotation | Freihandstrich aus codierten FreehandData‑Punkten |
ImageAnnotation | Bild von einer URL. Eine relative URL wird beim Hinzufügen der Annotation gegen den Anforderungs‑Host aufgelöst (nur das Abrufen des Bildes erfolgt beim Einbrennen) – sie muss vom Server aus erreichbar sein (z. B. eine Datei unter wwwroot, die von UseStaticFiles bereitgestellt wird). |
Die AnnotationManager‑API
| Mitglied | Zweck |
|---|---|
Add(BaseAnnotation) | Fügt eine Annotation zur Warteschlange hinzu |
GetAnnotations() / GetAnnotations(int page) | Zeigt an, welche Annotationen der Manager enthält |
ClearAnnotations() / ClearAnnotations(int page) | Entfernt alle / pro Seite |
GetAnnotationData() / GetAnnotationData(int page) | Kodierter Annotations‑Daten‑String — ein Base64‑Datenpaket (wie das Widget ihn verwendet) |
GetAnnotationXml() | XML‑Form |
Viewer spiegelt die Lade‑/Lese‑Operationen einer Sitzung wider: LoadAnnotationData(token, manager) oder LoadAnnotationData(token, encodedData) (das Base64‑Datenpaket von GetAnnotationData()), LoadAnnotationXML(token, xml), GetAnnotationXML(token).
Export mit eingebrannten Annotationen
// PDF of all pages with annotations rendered onto them
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");
});
// Or a ZIP of per-page PNGs
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");
});Exporte verwenden denselben Brenner wie die Bildschirmausgabe, sodass das, was die Benutzer sehen, im Dateiinhalt enthalten ist.
Persistenz‑Arbeitsablauf
- Öffnen Sie das Dokument und erhalten Sie sein Token.
- Laden Sie zuvor gespeichertes XML oder codierte Daten in dieses Token.
- Lassen Sie das Widget die Sitzungs‑Annotationen lesen und bearbeiten.
- Rufen Sie XML mit
GetAnnotationXML(token)ab, wenn Ihre Anwendung entscheidet, es zu speichern. - Exportieren Sie PDF/PNG, wenn ein flaches Ergebnis erforderlich ist.
- Schließen Sie die Dokument‑Sitzung.
Verwenden Sie das undurchsichtige Viewer‑Token nicht als dauerhaften Annotations‑Identifier. Verknüpfen Sie persistente Annotationsdaten mit Ihren eigenen Dokument‑ und Versions‑Identifikatoren.
Sicherheits‑ und Rendering‑Hinweise
- Annotations‑Anfragen verwenden dieselbe Sitzungs‑/Token‑Sicherheit wie Seiten‑Anfragen.
- Eine relative
ImageAnnotation‑URL wird vom Anforderungs‑Host aufgelöst und muss zum Brennzeitpunkt für den Server erreichbar bleiben. - Validieren und kontrollieren Sie jede vom Benutzer bereitgestellte Bild‑URL, um Server‑Side‑Request‑Forgery zu vermeiden.
- Exporte wenden dieselbe Lizenz‑/Benutzerdefiniert‑Wasserzeichen‑Entscheidung wie die Bildschirmausgabe von Seiten an.
- Große Freihand‑Payloads und hochauflösende Exporte erhöhen den Speicherverbrauch; testen Sie realistische Dokumente und Zoom‑Werte.
Fehlersuche
| Symptom | Prüfung |
|---|---|
| Annotation‑Ribbon fehlt | Annotation‑Fähigkeit und die vier Annotations‑CSS/Script‑Flags |
| Speicher‑Callback meldet einen Fehler | Token-/Sitzungs‑Ablauf und Middleware BasePath |
| C#‑Annotationen erscheinen nicht | Seitennummerierung beginnt bei 1 und Daten wurden in das aktive Token geladen |
| Bild‑Annotation erscheint auf dem Bildschirm, aber nicht im Export | Der Server kann die Bild‑URL während des Einbrennens erreichen |
| Wieder geöffnetes Dokument hat keine Annotationen | Persistieren Sie XML/Daten außerhalb der Viewer‑Sitzung und laden Sie sie dann in das neue Token |
War diese Seite hilfreich?