Σχόλια

Προσθέστε υποστήριξη σχολίων στον προβολέα

Τα σχόλια στο Doconut λειτουργούν σε δύο κατευθύνσεις: οι χρήστες τα σχεδιάζουν στο widget του προγράμματος περιήγησης και ο διακομιστής τα αποθηκεύει ανά σελίδα, ή ο κώδικάς σας τα δημιουργεί προγραμματιστικά και τα φορτώνει σε μια ανοιχτή συνεδρία. Σε κάθε περίπτωση εμφανίζονται στις σελίδες και μπορούν να ενσωματωθούν σε εξαγωγές PDF/PNG.

Η υποστήριξη σχολίων ελέγχεται από τη δυνατότητα άδειας Annotation (χορηγείται αυτόματα με ενεργή προσωρινή άδεια).

Ενεργοποίηση του UI σχολίων

Το Annotation είναι ένα module του Viewer, όχι μια αυτόνομη γραμμή εργαλείων. Η πλήρης σελίδα πρέπει να περιλαμβάνει τους πόρους του Viewer, τη γραμμή εργαλείων του Viewer, το mount του Viewer και το αρχικοποιημένο objViewer; το Ribbon του Annotation στη συνέχεια τοποθετείται και συνδέεται με το ίδιο αντικείμενο.

Εκδώστε τα πακέτα annotation μαζί με τα πακέτα του viewer — είναι περιορισμένα από την άδεια, έτσι τα tags εμφανίζονται μόνο όταν η δυνατότητα είναι διαθέσιμη:

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
}))

Διατηρήστε τη σύνθεση του Viewer ορατή στο markup:

html
<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>

Το πακέτο Annotation δημιουργεί το Ribbon DOM μέσα στο annBarMount; δεν χρειάζεται να αντιγράψετε τα κουμπιά ή το markup του διαλόγου. Αρχικοποιήστε πρώτα το docViewer, έπειτα δημιουργήστε το Ribbon μόνο όταν ο διακομιστής επιβεβαιώσει ότι το Annotation είναι αδειοδοτημένο:

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>

Η αποθήκευση από το Ribbon στέλνει δεδομένα μέσω του middleware (AnnSave), το οποίο τα αποθηκεύει στη συνεδρία του εγγράφου ανά σελίδα. Η φόρτωση (AnnLoad) γίνεται αυτόματα όταν μια σελίδα με σχόλια αποδίδεται. Τα τέσσερα callbacks onAnn* διατηρούν το Ribbon συγχρονισμένο με τον κύκλο ζωής του viewer.

Ανοίξτε και κλείστε το από οποιαδήποτε γραμμή εργαλείων Viewer που ανήκει στον κεντρικό κώδικα:

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

Το δημόσιο API του Ribbon είναι:

ΜέθοδοςΣκοπός
attach(objViewer)Συνδέει το Ribbon με τον αρχικοποιημένο viewer· απαιτείται μία φορά
open() / close()Είσοδος ή έξοδος από την επεξεργασία σχολίων
reset()Επαναφέρει το Ribbon στην κλειστή, μη-επεξεργαστική κατάσταση
isOpen() / annotating()Διαβάζει την κατάσταση του Ribbon / την κατάσταση επεξεργασίας σχολίων του viewer
reopenEditable()Φορτώνει ξανά τα σχόλια της τρέχουσας σελίδας ως επεξεργάσιμα αντικείμενα
updateActionState()Ανανέωση της διαθεσιμότητας των ελέγχων αποθήκευσης/διαγραφής μετά από αλλαγές του κεντρικού κώδικα
headerSlot()Παίρνει το προαιρετικό slot επέκτασης κεφαλίδας για ελέγχους που ανήκουν στον κεντρικό κώδικα

onStatus, onToast, onLayout, onEditStart και onEditEnd είναι προαιρετικά callbacks του κεντρικού κώδικα. Το αντικείμενο endpoints μπορεί επιπλέον να παρέχει exportPdf, exportPng, imageUpload και imageList; έλεγχοι χωρίς ρυθμισμένο endpoint παραμένουν κρυμμένοι. Για τη συνδυασμένη ακολουθία εκκίνησης Viewer, Search και Annotation, δείτε την Γρήγορη Εκκίνηση(Quick Start).

Το πακέτο annotation προσθέτει τα εργαλεία δημιουργίας στο πρόγραμμα περιήγησης, αλλά τα δεδομένα ανήκουν ακόμη στη συνεδρία εγγράφου στην πλευρά του διακομιστή που προσδιορίζεται από το token. Η επανέναρξη του πηγής δημιουργεί νέα συνεδρία· διατηρήστε το XML ή το κωδικοποιημένο envelope annotation στην εφαρμογή σας εάν τα σχόλια πρέπει να παραμείνουν μετά το τέλος της συνεδρίας.

Δημιουργία σχολίων σε C#

Αποκτήστε έναν διαχειριστή δεσμευμένο στην ανοιχτή συνεδρία, προσθέστε σχόλια και φορτώστε τα (με using Doconut.Annotations; για τους τύπους και using System.Drawing; για Rectangle/Color):

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

Τύποι σχολίων

Όλοι οι τύποι βρίσκονται στο Doconut.Annotations και κληρονομούν από το BaseAnnotation (αριθμός σελίδας + περιοριστικό Rectangle):

ΤύποςΣημειώσεις
StampAnnotationΣφραγίδα κειμένου με μέγεθος γραμματοσειράς, περίγραμμα, χρώμα· υποστηρίζει Opacity, Rotate
NoteAnnotationSticky note με κείμενο, χρώμα φόντου, μέγεθος γραμματοσειράς, TitleColor
RectangleAnnotationΧρώματα περιγράμματος + γεμίσματος, Title/ShowTitle
CircleAnnotationΠεριθώριο + γέμισμα, ShowBorder
EllipseAnnotationΠεριθώριο + γέμισμα, ShowBorder
TriangleAnnotationΧρώμα περιγράμματος, BackColor, ShowBorder
LineAnnotationΕυθεία γραμμή με πάχος και χρώμα
ArrowAnnotationΓραμμή με κεφαλή βέλους· ρυθμιζόμενο Direction (τύπου ArrowDirection, σημεία πυξίδας, προεπιλογή E)
FreehandAnnotationΕλεύθερη γραμμή από κωδικοποιημένα σημεία FreehandData
ImageAnnotationΕικόνα από URL. Ένα σχετικό URL επιλύεται σε σχέση με τον κεντρικό υπολογιστή της αίτησης όταν το σχόλιο προστίθεται (μόνο η λήψη της εικόνας γίνεται κατά το “burn” ) — πρέπει να είναι προσβάσιμο από τον διακομιστή (π.χ. αρχείο κάτω από wwwroot που εξυπηρετείται από UseStaticFiles)

Το API του AnnotationManager

ΜέλοςΣκοπός
Add(BaseAnnotation)Προσθέτει ένα σχόλιο στη σειρά
GetAnnotations() / GetAnnotations(int page)Εξετάζει τι κρατά ο διαχειριστής
ClearAnnotations() / ClearAnnotations(int page)Αφαιρεί όλα / ανά σελίδα
GetAnnotationData() / GetAnnotationData(int page)Κωδικοποιημένη συμβολοσειρά δεδομένων σχολίων — ένα envelope Base64 (αυτό που καταναλώνει το widget)
GetAnnotationXml()Μορφή XML
GetAnnotationData()(επαναλαμβανόμενο)
GetAnnotationXml()(επαναλαμβανόμενο)

Το Viewer αντικατοπτρίζει τις λειτουργίες φόρτωσης/ανάγνωσης έναντι μιας συνεδρίας: LoadAnnotationData(token, manager) ή LoadAnnotationData(token, encodedData) (το envelope Base64 από GetAnnotationData()), LoadAnnotationXML(token, xml), GetAnnotationXML(token).

Εξαγωγή με ενσωματωμένα σχόλια

csharp
// 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");
});

Οι εξαγωγές χρησιμοποιούν τον ίδιο “burner” με την απόδοση στην οθόνη, έτσι ό,τι βλέπουν οι χρήστες είναι αυτό που περιέχει το αρχείο.

Ροή εργασίας διατήρησης

  1. Ανοίξτε το έγγραφο και αποκτήστε το token του.
  2. Φορτώστε το αποθηκευμένο XML ή τα κωδικοποιημένα δεδομένα σε εκείνο το token.
  3. Επιτρέψτε στο widget να διαβάσει και να επεξεργαστεί τα σχόλια της συνεδρίας.
  4. Ανακτήστε το XML με GetAnnotationXML(token) όταν η εφαρμογή σας αποφασίσει να το διατηρήσει.
  5. Εξάγετε PDF/PNG όταν απαιτείται ένα επίπεδο (flattened) παραδοτέο.
  6. Κλείστε τη συνεδρία του εγγράφου.

Μην χρησιμοποιείτε το αδιαφανές token του viewer ως μόνιμο αναγνωριστικό σχολίου. Συσχετίστε τα διατηρημένα δεδομένα σχολίων με τα δικά σας αναγνωριστικά εγγράφου και έκδοσης.

Ασφάλεια και σημειώσεις απόδοσης

  • Τα αιτήματα σχολίων χρησιμοποιούν την ίδια ασφάλεια συνεδρίας/token με τα αιτήματα σελίδας.
  • Ένα σχετικό URL ImageAnnotation επιλύεται από τον κεντρικό υπολογιστή της αίτησης και πρέπει να παραμένει προσβάσιμο στο διακομιστή κατά το “burn”.
  • Επικυρώστε και ελέγξτε κάθε URL εικόνας που παρέχεται από χρήστη για αποφυγή server‑side request forgery.
  • Οι εξαγωγές εφαρμόζουν την ίδια απόφαση άδειας/προσαρμοσμένου υδατογραφήματος όπως η απόδοση σελίδας στην οθόνη.
  • Μεγάλα payload ελεύθερης γραφής και εξαγωγές υψηλής ανάλυσης αυξάνουν τη χρήση μνήμης· δοκιμάστε ρεαλιστικά έγγραφα και τιμές ζουμ.

Αντιμετώπιση προβλημάτων

ΣυμπτωμαΈλεγχος
Το ribbon σχολίων λείπειΔυνατότητα Annotation και οι τέσσερις σημαίες CSS/script annotation
Η κλήση αποθήκευσης αναφέρει σφάλμαΛήξη token/συνεδρίας και middleware BasePath
Τα σχόλια C# δεν εμφανίζονταιΗ αρίθμηση σελίδων είναι 1‑based και τα δεδομένα φορτώθηκαν στο ενεργό token
Η εικόνα σχολίου εμφανίζεται στην οθόνη αλλά όχι στην εξαγωγήΟ διακομιστής μπορεί να φτάσει το URL της εικόνας κατά το “burn‑in”
Το επανανοιγμένο έγγραφο δεν έχει σχόλιαΔιατηρήστε XML/δεδομένα εκτός της συνεδρίας του viewer, έπειτα φορτώστε τα στο νέο token

Ήταν αυτή η σελίδα χρήσιμη;