Anmerkungen

Fügen Sie dem Viewer Anmerkungsunterstützung hinzu

Anmerkungen 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 Anmerkungsunterstützung ist durch die Lizenz‑Fähigkeit Annotation gesteuert (automatisch gewährt bei einer aktiven temporären Lizenz).

Aktivieren der Anmerkungs‑UI

Annotation ist ein Viewer‑Modul, keine eigenständige Symbolleiste. Die komplette Seite muss die Viewer‑Ressourcen, die Viewer‑Symbolleiste, den Viewer‑Mount und den initialisierten 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
@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
}))

Halten Sie die komplette Viewer‑Zusammensetzung im Markup sichtbar:

html
<nav id="toolbar" aria-label="Document viewer controls">
    <!-- Viewer-Steuerelemente, einschließlich der Schaltfläche, die 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 erst, wenn der Server bestätigt, dass Annotation lizenziert ist:

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>

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 Anmerkungen 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:

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

Die öffentliche Ribbon‑API lautet:

MethodPurpose
attach(objViewer)Verbinden Sie das Ribbon mit dem initialisierten Viewer; einmalig erforderlich
open() / close()Anmerkungsbearbeitung starten bzw. beenden
reset()Setzt das Ribbon in den geschlossenen, nicht‑bearbeitenden Zustand zurück
isOpen() / annotating()Liest den Ribbon‑Zustand / den Anmerkungs‑Bearbeitungszustand des Viewers
reopenEditable()Lädt die Anmerkungen der aktuellen Seite als bearbeitbare Objekte neu
updateActionState()Aktualisiert die Verfügbarkeit von Speicher‑/Lösch‑Steuerelementen nach Änderungen im Host
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 Objekt endpoints kann zusätzlich exportPdf, exportPng, imageUpload und imageList bereitstellen; Steuerelemente ohne konfigurierten Endpunkt bleiben verborgen. Für die kombinierte Viewer‑, Such‑ und Annotations‑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‑Umfang in Ihrer Anwendung, wenn Anmerkungen über die Lebensdauer der Sitzung hinaus erhalten bleiben müssen.

Annotations 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):

csharp
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));

    // In die Sitzung laden — das Widget holt sie über AnnLoad und das
    // renderer brennt sie in Bild-/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):

TypHinweise
StampAnnotationTextstempel mit Schriftgröße, Rand, Farbe; unterstützt Opacity, Rotate
NoteAnnotationHaftnotiz mit Text, Hintergrundfarbe, Schriftgröße, TitleColor
RectangleAnnotationRand- und Füllfarben, Title/ShowTitle
CircleAnnotationRand und Füllung, ShowBorder
EllipseAnnotationRand und Füllung, ShowBorder
TriangleAnnotationRandfarbe, BackColor, ShowBorder
LineAnnotationGerade Linie mit Breite und Farbe
ArrowAnnotationLinie mit Pfeilspitze; einstellbare Direction (Typ ArrowDirection, Himmelsrichtungen, Standard E)
FreehandAnnotationFreier Strich aus kodierten FreehandData-Punkten
ImageAnnotationBild von einer URL. Eine relative URL wird beim Hinzufügen der Annotation gegen den Anforderungs‑Host aufgelöst (nur das Bild wird zur Brennzeit abgerufen) — sie muss vom Server aus erreichbar sein (z. B. eine Datei unter wwwroot, die von UseStaticFiles bereitgestellt wird)

Die AnnotationManager-API

MemberZweck
Add(BaseAnnotation)Annotation in die Warteschlange stellen
GetAnnotations() / GetAnnotations(int page)Prüfen, 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

csharp
// 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 mit 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.

Persistenz‑Workflow

  1. Öffnen Sie das Dokument und erhalten Sie dessen Token.
  2. Laden Sie zuvor gespeichertes XML oder kodierte Daten in diesen Token.
  3. Lassen Sie das Widget die Sitzungs‑Annotationen lesen und bearbeiten.
  4. Rufen Sie XML mit GetAnnotationXML(token) ab, wenn Ihre Anwendung die Persistierung entscheidet.
  5. Exportieren Sie PDF/PNG, wenn ein flaches Ergebnis benötigt wird.
  6. Schließen Sie die Dokumentsitzung.

Verwenden Sie das undurchsichtige Viewer‑Token nicht als permanente Annotation‑Kennung. Verknüpfen Sie persistente Annotationsdaten mit Ihren eigenen Dokument‑ und Versionskennungen.

Sicherheits- und Rendering-Hinweise

  • Anmerkungsanfragen verwenden dieselbe Sitzungs-/Token‑Sicherheit wie Seitenanfragen.
  • Eine relative ImageAnnotation‑URL 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‑Forgery zu vermeiden.
  • Exporte wenden dieselbe Lizenz‑/Benutzerdefinierte‑Wasserzeichen‑Entscheidung wie die Bildschirmanzeige von Seiten an.
  • Große Freihand‑Payloads und hochauflösende Exporte erhöhen den Speicherverbrauch; testen Sie realistische Dokumente und Zoom‑Werte.

Fehlersuche

SymptomPrüfung
Anmerkungsleiste fehltAnnotation‑Fähigkeit und die vier Anmerkungs‑CSS/Script‑Flags
Speicher‑Callback meldet einen FehlerToken‑/Sitzungsablauf und Middleware BasePath
C#‑Anmerkungen werden nicht angezeigtSeitennummerierung ist einsbasiert und Daten wurden in das aktive Token geladen
Bildanmerkung erscheint auf dem Bildschirm, aber nicht im ExportDer Server kann die Bild‑URL während des Renderns erreichen
Wieder geöffnetes Dokument hat keine AnmerkungenXML/Daten außerhalb der Viewer‑Sitzung persistieren und dann in das neue Token laden

War diese Seite hilfreich?