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 gerendert und können in PDF/PNG-Exporte eingebrannt werden.
Die Annotationsunterstützung ist durch die Lizenzfähigkeit Annotation gesteuert (automatisch gewährt bei einer aktiven temporären Lizenz).
Aktivieren der Annotations‑Benutzeroberfläche
Annotation ist ein Viewer‑Modul, kein eigenständiges Werkzeug. Die komplette Seite muss die Viewer‑Ressourcen, die Viewer‑Symbolleiste, die Viewer‑Einbindung 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 // die eingebettete Annotationsleiste
}))Halten Sie die komplette Viewer‑Zusammensetzung im Markup sichtbar:
<nav id="toolbar" aria-label="Document viewer controls">
<!-- Viewer-Steuerungen, einschließlich des Buttons, der Annotation öffnet -->
</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: '/',
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 durch die Middleware (AnnSave), die sie in der Dokument‑Sitzung pro Seite speichert. Das Laden (AnnLoad) erfolgt automatisch, wenn eine Seite mit Annotationen gerendert wird. Die vier onAnn*‑Callbacks halten das Ribbon synchron mit dem Viewer‑Lebenszyklus.
Öffnen und schließen Sie es über jede host‑eigene 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() | Beginnen oder beenden Sie die Annotationsbearbeitung |
reset() | Setzen Sie das Ribbon in seinen geschlossenen, nicht‑bearbeitenden Zustand zurück |
isOpen() / annotating() | Lesen Sie den Ribbon‑Zustand / den Annotations‑Bearbeitungszustand des Viewers |
reopenEditable() | Laden Sie die Annotationen der aktuellen Seite als bearbeitbare Objekte neu |
updateActionState() | Aktualisieren Sie die Verfügbarkeit der Speicher‑/Lösch‑Steuerung nach Änderungen durch den Host |
headerSlot() | Erhalten Sie den optionalen Header‑Erweiterungs‑Slot für host‑eigene Steuerelemente |
onStatus, onToast, onLayout, onEditStart und onEditEnd sind optionale Host‑Callbacks. Das Objekt endpoints kann zusätzlich exportPdf, exportPng, imageUpload und imageList bereitstellen; Steuerelemente ohne konfigurierten Endpunkt bleiben verborgen.
Für die kombinierte Viewer, Search und Annotation-Startsequenz 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‑Envelope in Ihrer Anwendung, wenn Annotationen über die Lebensdauer der Sitzung hinaus erhalten bleiben müssen.
Annotationen in C# erstellen
Holen Sie 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) =>
{
// Gebunden an die Seitenabmessungen der offenen Sitzung
var manager = viewer.GetAnnotationManager(token);
var pageCount = viewer.GetPageCount(token);
// Ein Stempel pro Seite
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));
// Lade in die Sitzung — das Widget holt sie über AnnLoad und das
// Renderer brennt sie in image/PDF-Exporte ein.
viewer.LoadAnnotationData(token, manager);
return Results.Ok();
});Annotationstypen
Alle Typen befinden sich in Doconut.Annotations und erben von BaseAnnotation (Seitennummer + begrenzendes Rectangle):
| Typ | Hinweis |
|---|---|
StampAnnotation | Textstempel mit Schriftgröße, Rahmen, Farbe; unterstützt Opacity, Rotate |
NoteAnnotation | Haftnotiz mit Text, Hintergrundfarbe, Schriftgröße, TitleColor |
RectangleAnnotation | Rand- + Füllfarben, Title/ShowTitle |
CircleAnnotation | Rand + Füllung, ShowBorder |
EllipseAnnotation | Rand + Füllung, ShowBorder |
TriangleAnnotation | Randfarbe, BackColor, ShowBorder |
LineAnnotation | Gerade Linie mit Breite und Farbe |
ArrowAnnotation | Linie mit Pfeilspitze; einstellbares Direction (Typ ArrowDirection, Himmelsrichtungen, Standard E) |
FreehandAnnotation | Freier Strich 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 zum Brennzeitpunkt) — sie muss vom Server aus erreichbar sein (z. B. eine Datei unter wwwroot, bereitgestellt von UseStaticFiles) |
Die AnnotationManager‑API
| Mitglied | Zweck |
|---|---|
Add(BaseAnnotation) | Eine Annotation in die Warteschlange stellen |
GetAnnotations() / GetAnnotations(int page) | Untersuchen, was der Manager enthält |
ClearAnnotations() / ClearAnnotations(int page) | Alle entfernen / pro Seite |
GetAnnotationData() / GetAnnotationData(int page) | Kodierte Annotationsdaten‑Zeichenkette — ein Base64‑Datenpaket (was das Widget verbraucht) |
GetAnnotationXml() | XML‑Form |
Viewer spiegelt die Lade‑/Lese‑Operationen gegenüber 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 aller Seiten mit darauf gerenderten Anmerkungen
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");
});
// Oder ein ZIP von pro-Seite 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.
Persistenzablauf
- Öffnen Sie das Dokument und erhalten Sie dessen Token.
- Laden Sie zuvor gespeichtes XML oder kodierte Daten in diesen Token.
- Lassen Sie das Widget die Sitzungsannotation lesen und bearbeiten.
- Rufen Sie XML mit
GetAnnotationXML(token)ab, wenn Ihre Anwendung die Persistenz entscheidet. - Exportieren Sie PDF/PNG, wenn ein flaches Ergebnis erforderlich ist.
- Schließen Sie die Dokumentsitzung.
Verwenden Sie das undurchsichtige Viewer‑Token nicht als dauerhaften Annotations‑Identifier. Verknüpfen Sie persistente Annotationsdaten mit Ihren eigenen Dokument‑ und Versions‑Identifikatoren.
Sicherheit und Rendering-Hinweise
- Annotation-Anfragen verwenden dieselbe Sitzungs-/Token-Sicherheit wie Seitenanfragen.
- Eine relative
ImageAnnotationURL wird vom Anforderungs-Host aufgelöst und muss zum Zeitpunkt des Renderns für den Server erreichbar bleiben. - Validieren und kontrollieren Sie jede vom Benutzer bereitgestellte Bild-URL, um Server-seitige Request-Fälschungen zu vermeiden.
- Exporte wenden dieselbe Lizenz-/Benutzerdefiniert-Wasserzeichen-Entscheidung wie die Bildschirmseiten-Renderung an.
- Große Freihand-Payloads und hochauflösende Exporte erhöhen den Speicherverbrauch; testen Sie realistische Dokumente und Zoom-Werte.
Fehlersuche
| Symptom | Check |
|---|---|
| Annotierungsleiste fehlt | Annotation Fähigkeit und die vier Annotierungs‑CSS/Script‑Flags |
| Speicher-Callback meldet einen Fehler | Token-/Sitzungsablauf und Middleware BasePath |
| C#-Annotationen werden nicht angezeigt | Seitennummerierung ist einsbasiert und Daten wurden in das aktive Token geladen |
| Bildannotation erscheint auf dem Bildschirm, aber nicht im Export | Der Server kann die Bild-URL während des Renderns erreichen |
| Wieder geöffnetes Dokument hat keine Annotationen | XML/Daten außerhalb der Viewer‑Sitzung persistieren, dann in das neue Token laden |
War diese Seite hilfreich?