Σχόλια
Προσθέστε υποστήριξη σχολίων στον προβολέα
Τα σχόλια στο Doconut λειτουργούν σε δύο κατευθύνσεις: οι χρήστες τα σχεδιάζουν στο widget του προγράμματος περιήγησης και ο διακομιστής τα αποθηκεύει ανά σελίδα, ή ο κώδικάς σας τα δημιουργεί προγραμματιστικά και τα φορτώνει σε μια ανοιχτή συνεδρία. Σε κάθε περίπτωση εμφανίζονται στις σελίδες και μπορούν να ενσωματωθούν σε εξαγωγές PDF/PNG.
Η υποστήριξη σχολίων ελέγχεται από τη δυνατότητα άδειας Annotation (χορηγείται αυτόματα με ενεργή προσωρινή άδεια).
Ενεργοποίηση του UI σχολίων
Το Annotation είναι ένα module του Viewer, όχι μια αυτόνομη γραμμή εργαλείων. Η πλήρης σελίδα πρέπει να περιλαμβάνει τους πόρους του Viewer, τη γραμμή εργαλείων του Viewer, το mount του Viewer και το αρχικοποιημένο objViewer; το Ribbon του Annotation στη συνέχεια τοποθετείται και συνδέεται με το ίδιο αντικείμενο.
Εκδώστε τα πακέτα annotation μαζί με τα πακέτα του viewer — είναι περιορισμένα από την άδεια, έτσι τα tags εμφανίζονται μόνο όταν η δυνατότητα είναι διαθέσιμη:
@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:
<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 είναι αδειοδοτημένο:
<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 που ανήκει στον κεντρικό κώδικα:
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):
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 |
NoteAnnotation | Sticky 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).
Εξαγωγή με ενσωματωμένα σχόλια
// 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” με την απόδοση στην οθόνη, έτσι ό,τι βλέπουν οι χρήστες είναι αυτό που περιέχει το αρχείο.
Ροή εργασίας διατήρησης
- Ανοίξτε το έγγραφο και αποκτήστε το token του.
- Φορτώστε το αποθηκευμένο XML ή τα κωδικοποιημένα δεδομένα σε εκείνο το token.
- Επιτρέψτε στο widget να διαβάσει και να επεξεργαστεί τα σχόλια της συνεδρίας.
- Ανακτήστε το XML με
GetAnnotationXML(token)όταν η εφαρμογή σας αποφασίσει να το διατηρήσει. - Εξάγετε PDF/PNG όταν απαιτείται ένα επίπεδο (flattened) παραδοτέο.
- Κλείστε τη συνεδρία του εγγράφου.
Μην χρησιμοποιείτε το αδιαφανές 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 |
Ήταν αυτή η σελίδα χρήσιμη;