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 und lädt sie in eine offene Sitzung. In beiden Fällen werden sie auf den Seiten gerendert und können in PDF‑ oder PNG‑Exporte eingebettet werden.

Die Unterstützung für Annotationen ist durch die Lizenzfunktion Annotation geschützt (wird automatisch bei einer aktiven temporären Lizenz gewährt).

Aktivieren der Annotation‑Benutzeroberfläche

Annotation ist ein Viewer‑Modul, keine eigenständige Symbolleiste. Die Seite muss die Viewer‑Ressourcen, die Viewer‑Symbolleiste, den Viewer‑Mount und ein initialisiertes objViewer einbinden; das Annotation‑Ribbon wird dann montiert und an dieselbe Instanz angehängt.

Geben Sie die Annotationsdateien zusammen mit den Viewer‑Dateien aus. Sie sind lizenzgeschützt, sodass ihre Tags nur erscheinen, wenn die Funktion gewährt ist. In MVC 5 erstellen Sie die Tags im Controller (Views haben kein @inject):

csharp
var viewer = Doconut.DoconutHost.Services.GetRequiredService<Viewer>();
ViewBag.ViewerCss = viewer.ReferenceCss(new CssConfig
{
    IncludeViewerCss     = true,
    IncludeAnnotationCss = true    // jquery-ui.min.css + annotationBar.css
});
ViewBag.ViewerScripts = viewer.ReferenceScripts(new ScriptConfig
{
    IncludeJQuery            = true,
    IncludeViewerScripts     = true,
    IncludeAnnotationScripts = true,  // jquery-ui, raphael.js, annotation.js
    IncludeAnnotationBar     = true   // the embedded annotation ribbon
});
ViewBag.IsAnnotationEnabled = viewer.IsAnnotationEnabled;

Behalten Sie die vollständige Viewer‑Zusammensetzung im Markup bei:

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>

Die Annotationsdateien erzeugen das Ribbon innerhalb von annBarMount; Sie kopieren dessen Schaltflächen oder Dialoge nicht. Initialisieren Sie zuerst docViewer und erstellen Sie das Ribbon nur, wenn der Server bestätigt, dass Annotation lizenziert ist:

html
@Html.Raw(ViewBag.ViewerScripts)
<script>
    var annBar = null;
    var currentToken = '';

    var objViewer = $('#div_ctlDoc').docViewer({
        BasePath: '/',
        ResPath:  'doconut-res/images',
        onAnnLoaded:    function () { if (annBar) annBar.handleAnnLoaded(); },
        onAnnSaved:     function () { if (annBar) annBar.handleAnnSaved(); },
        onAnnSaveError: function () { if (annBar) annBar.handleAnnSaveError(); },
        onAnnClosed:    function () { if (annBar) annBar.handleAnnClosed(); },
        onError:        function (message) { console.error('Viewer error:', message); }
    });

    @if ((bool)ViewBag.IsAnnotationEnabled)
    {
        <text>
    annBar = $('#annBarMount').doconutAnnotationBar({
        docId: 'ctlDoc',
        getRequestParams: function () { return { token: currentToken }; },
        onLayout: function () { requestAnimationFrame(function () { objViewer.Refit(); }); }
    });
    annBar.attach(objViewer);
        </text>
    }
</script>

Ohne die Funktion lässt ReferenceScripts das Skript des Ribbons weg, sodass die serverseitige if‑Anweisung einen ungeschützten Aufruf von doconutAnnotationBar(...) davon abhält, die Seite zu beschädigen. In Web Forms schreiben Sie dieselbe Prüfung als <% if (IsAnnotationEnabled) { %> … <% } %>.

Das Speichern über das Ribbon sendet die Daten an DocImage.axd (AnnSave), das sie in der Dokument‑Sitzung pro Seite speichert. Das Laden (AnnLoad) erfolgt automatisch, wenn eine Seite mit Annotationen gerendert wird.

Öffnen und schließen Sie das Ribbon über Ihre eigene Symbolleiste mit annBar.open() und annBar.close(). Seine öffentliche API:

MethodeZweck
attach(objViewer)Verbinden Sie das Ribbon mit dem initialisierten Viewer; einmalig erforderlich
open() / close()In den Annotations‑Bearbeitungsmodus wechseln bzw. diesen verlassen
reset()Das Ribbon in den geschlossenen, nicht‑bearbeitenden Zustand zurücksetzen
isOpen() / annotating()Den Ribbon‑Zustand bzw. den Annotations‑Bearbeitungszustand des Viewers auslesen
reopenEditable()Die Annotationen der aktuellen Seite als editierbare Objekte neu laden
updateActionState()Die Speicher‑ und Lösch‑Steuerelemente nach Änderungen des Hosts aktualisieren
headerSlot()Der optionale Header‑Slot für host‑eigene Steuerelemente

onStatus, onToast, onLayout, onEditStart und onEditEnd sind optionale Host‑Callbacks. Das Objekt endpoints kann außerdem exportPdf, exportPng, imageUpload und imageList bereitstellen; Steuerelemente ohne konfigurierten Endpunkt bleiben verborgen.

Zwei Arten von Callback, auf zwei Arten verdrahtet

Der Annotationscode verwendet zwei Callback‑Stile, und jeder schlägt stillschweigend fehl, wenn er auf die andere Weise verdrahtet wird:

  • onAnnLoaded, onAnnSaved, onAnnSaveError, onAnnClosed sind Optionen von docViewer({...}), wie jeder Viewer‑Callback.
  • Created, Changed, Deleted und Properties sind globale Funktionen, die per Namen gefunden werden: die Viewer‑ID wird dem Ereignis vorangestellt. Für den Viewer in #div_ctlDoc ist die ID ctlDoc, sodass die Funktionen ctlDoc_Created, ctlDoc_Changed, ctlDoc_Deleted und ctlDoc_Properties heißen. Eine falsch geschriebene oder umbenannte Funktion wird einfach nie aufgerufen.

SaveAnnotations() gibt nicht zurück, ob gespeichert wurde: das Ergebnis kommt über onAnnSaved oder onAnnSaveError. Code, der seinen Rückgabewert ausliest, löscht nie das Flag für „ungenügende Änderungen“.

Anmerkungen in C# erstellen

Erhalte einen Manager, der an die offene Sitzung gebunden ist, füge Anmerkungen hinzu und lade sie (mit using Doconut.Annotations; für die Typen und using System.Drawing; für Rectangle und Color):

csharp
// POST /Annotations/LoadSample?token=...
[HttpPost]
public ActionResult LoadSample(string token)
{
    var viewer = Doconut.DoconutHost.Services.GetRequiredService<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 viewer fetches them, and the renderer burns them into
    // image/PDF exports.
    viewer.LoadAnnotationData(token, manager);
    return new HttpStatusCodeResult(200);
}

Anmerkungstypen

Alle Typen befinden sich in Doconut.Annotations und erben von BaseAnnotation (Seitennummer plus begrenzendes Rectangle):

TypeNotes
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 (ArrowDirection, Himmelsrichtungen, Standard E)
FreehandAnnotationFreihandstrich aus kodierten FreehandData‑Punkten
ImageAnnotationBild von einer URL. Eine relative URL wird gegen den Host der aktuellen Anfrage aufgelöst, wenn die Anmerkung hinzugefügt wird – siehe den Hinweis zum Export unten

Die AnnotationManager-API

MemberPurpose
Add(BaseAnnotation)Annotation zur Warteschlange hinzufügen
GetAnnotations() / GetAnnotations(int page)Untersuchen, was der Manager enthält
ClearAnnotations() / ClearAnnotations(int page)Alle entfernen / pro Seite
GetAnnotationData() / GetAnnotationData(int page)Kodierte Annotationsdaten — das Datenpaket, das das Widget verarbeitet
GetAnnotationXml()XML‑Form

Viewer spiegelt die Lade- und Lesevorgänge gegen eine Sitzung wider: LoadAnnotationData(token, manager) oder LoadAnnotationData(token, encodedData), LoadAnnotationXML(token, xml) und GetAnnotationXML(token).

Export mit eingebrannten Anmerkungen

csharp
// PDF of all pages with annotations rendered onto them
public async Task<ActionResult> ExportPdf(string token)
{
    var viewer = Doconut.DoconutHost.Services.GetRequiredService<Viewer>();
    byte[] pdf = await viewer.ExportAnnotationsToPdfAsync(token, zoom: 100, ct: Response.ClientDisconnectedToken);
    return File(pdf, "application/pdf", "export.pdf");
}

// Or a ZIP of per-page PNGs
public async Task<ActionResult> ExportPngZip(string token)
{
    var viewer = Doconut.DoconutHost.Services.GetRequiredService<Viewer>();
    byte[] zip = await viewer.ExportAnnotationsToPngZipAsync(token, zoom: 100, ct: Response.ClientDisconnectedToken);
    return File(zip, "application/zip", "annotations-png.zip");
}

Exporte verwenden denselben Brenner wie die Bildschirmausgabe und dieselbe Lizenz‑ und benutzerdefinierte Wasserzeichen‑Entscheidung.

Bildannotationen auf einem Intranet‑Host exportieren als „Bild blockiert“

Der Viewer und der Export holen das Bild einer ImageAnnotation aus unterschiedlichen Quellen:

OberflächeWer lädt das Bild herunterAuf einem Intranet‑ oder Entwicklungs‑Host
Der Viewer im BrowserDer Browser des BenutzersDas Bild wird angezeigt
ExportAnnotationsToPdfAsync / …PngZipAsyncDer ServerEin roter „Bild blockiert“‑Platzhalter

Wenn der Server ein Bild abruft, um es einzubetten, verweigert er Adressen, die zurück in private Netze führen: Loopback (localhost, 127.0.0.1), 0.0.0.0, Link‑Local (169.254.x.x), die privaten Bereiche 10.x, 172.16–31.x und 192.168.x, Carrier‑Grade‑NAT (100.64.0.0/10) sowie IPv6‑Link‑Local‑ und Unique‑Local‑Adressen. Dies schützt den Server davor, über eine manipulierte Bild‑URL interne Systeme zu erreichen, und es gibt keinen Schalter, um das zu deaktivieren.

Eine relative Bild‑URL wird relativ zum Host der aktuellen Anfrage aufgelöst. Deshalb wird bei localhost, einem Intranet‑Server oder einer Site, die über eine private Adresse erreichbar ist, das Bild im Viewer angezeigt, während der Export stattdessen den Platzhalter enthält. Das ist die Schutzfunktion, kein Rendering‑Fehler. Wenn Exporte das Bild enthalten sollen, muss es von einem Host mit öffentlicher Adresse bereitgestellt und eine absolute URL verwendet werden. Eine Bild‑URL, die überhaupt nicht aufgelöst werden kann, lässt diese eine Annotation ohne Fehlermeldung verschwinden – prüfen Sie die gerenderten Pixel oder das Netzwerk‑Log, nicht den Statuscode.

Persistenz‑Workflow

  1. Öffnen Sie das Dokument und holen Sie sein Token.
  2. Laden Sie zuvor gespeicherte XML‑ oder kodierte Daten in dieses 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 ausführen soll.
  5. Exportieren Sie PDF oder PNG, wenn ein flaches Auslieferungsformat benötigt wird.
  6. Schließen Sie die Dokumentsitzung.

Verwenden Sie das Viewer‑Token nicht als dauerhaften Annotations‑Identifier – es verfällt mit der Sitzung, und ein Recycle des Anwendungspools beendet jede Sitzung auf einem einzelnen Server. Speichern Sie Annotationsdaten gegen Ihre eigenen Dokument‑ und Versions‑Identifier.

Sicherheits‑ und Rendering‑Hinweise

  • Annotations‑Anfragen nutzen dieselbe Sitzungs‑ und Token‑Sicherheit wie Seiten‑Anfragen.
  • Validieren und kontrollieren Sie jede vom Benutzer bereitgestellte Bild‑URL.
  • Große Freihand‑Payloads und hochauflösende Exporte erhöhen den Speicherverbrauch; testen Sie realistische Dokumente und Zoom‑Werte.

Fehlerbehebung

SymptomPrüfung
Das Annotation‑Ribbon fehltDie Annotation‑Fähigkeit und die vier Annotation‑CSS/Script‑Flags
Beim Speichern wird ein Fehler gemeldetToken‑ oder Sitzungs‑Ablauf, und das BasePath‑Attribut des Widgets
ctlDoc_Created (oder eine andere Ereignis‑Funktion) wird nie ausgeführtDer Funktionsname: Viewer‑ID + _ + Ereignis, als globale Funktion
C#‑Annotationen werden nicht angezeigtSeitenzahlen beginnen bei 1, und die Daten wurden in das aktive Token geladen
Eine Bildannotation wird im Viewer angezeigt, aber nicht im ExportDer Host des Bildes hat eine private oder Loopback‑Adresse – siehe oben
Ein erneut geöffnetes Dokument hat keine AnnotationenPersistieren Sie XML oder Daten außerhalb der Viewer‑Sitzung und laden Sie sie in das neue Token

War diese Seite hilfreich?