Σχόλια
Προσθέστε υποστήριξη σχολίων στον προβολέα
Τα σχόλια στο Doconut λειτουργούν σε δύο κατευθύνσεις: οι χρήστες τα σχεδιάζουν στο widget του προγράμματος περιήγησης και ο διακομιστής τα αποθηκεύει ανά σελίδα, ή ο κώδικάς σας τα δημιουργεί προγραμματιστικά και τα φορτώνει σε μια ανοιχτή συνεδρία. Σε κάθε περίπτωση εμφανίζονται στις σελίδες και μπορούν να ενσωματωθούν σε εξαγωγές PDF/PNG.
Η υποστήριξη σχολίων ελέγχεται από τη δυνατότητα άδειας Annotation (χορηγείται αυτόματα με ενεργή προσωρινή άδεια).
Ενεργοποίηση του UI σχολίων
Το Annotation είναι ένα module του Viewer, όχι μια αυτόνομη γραμμή εργαλείων. Η πλήρης σελίδα πρέπει να περιλαμβάνει τους πόρους του Viewer, τη γραμμή εργαλείων του Viewer, το mount του Viewer και το αρχικοποιημένο objViewer; το Annotation Ribbon στη συνέχεια τοποθετείται και συνδέεται με το ίδιο παράδειγμα.
Εκδώστε τα πακέτα σχολίων μαζί με τα πακέτα του viewer — είναι ελεγχόμενα από άδεια, έτσι οι ετικέτες εμφανίζονται μόνο όταν η δυνατότητα είναι διαθέσιμη:
@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 δημιουργεί το DOM του Ribbon μέσα στο 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 που ανήκει στον κεντρικό (host) εφαρμογή:
annBar.open();
annBar.close();Το δημόσιο API του Ribbon είναι:
| Μέθοδος | Σκοπός |
|---|---|
attach(objViewer) | Συνδέει το Ribbon με τον αρχικοποιημένο viewer· απαιτείται μία φορά |
open() / close() | Εισέρχεται ή εξέρχεται από την επεξεργασία σχολίων |
reset() | Επαναφέρει το Ribbon στην κλειστή, μη-επεξεργαστική κατάσταση |
isOpen() / annotating() | Διαβάζει την κατάσταση του Ribbon / την κατάσταση επεξεργασίας σχολίων του viewer |
reopenEditable() | Φορτώνει ξανά τα σχόλια της τρέχουσας σελίδας ως επεξεργάσιμα αντικείμενα |
updateActionState() | Ανανεώνει τη διαθεσιμότητα των ελέγχων αποθήκευσης/διαγραφής μετά από αλλαγές του host |
headerSlot() | Λαμβάνει το προαιρετικό slot επέκτασης κεφαλίδας για ελέγχους που ανήκουν στον host |
Τα onStatus, onToast, onLayout, onEditStart και onEditEnd είναι προαιρετικά callbacks του host. Το αντικείμενο endpoints μπορεί επιπλέον να παρέχει exportPdf, exportPng, imageUpload και imageList; τα στοιχεία χωρίς διαμορφωμένο endpoint παραμένουν κρυμμένα. Για τη σειρά εκκίνησης του συνδυασμένου Viewer, Search και Annotation, δείτε Γρήγορη Έναρξη.
Το πακέτο σχολίων προσθέτει τα εργαλεία δημιουργίας του προγράμματος περιήγησης, αλλά τα δεδομένα ανήκουν ακόμη στη συνεδρία εγγράφου στην πλευρά του διακομιστή που προσδιορίζεται από το token. Η επαναφορά του προέλευσης δημιουργεί μια νέα συνεδρία· αποθηκεύστε το XML ή το κωδικοποιημένο φάκελο σχολίων στην εφαρμογή σας εάν τα σχόλια πρέπει να παραμείνουν μετά τη λήξη της συνεδρίας.
Δημιουργία σχολίων σε 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 σημείωση με κείμενο, χρώμα φόντου, μέγεθος γραμματοσειράς, TitleColor |
RectangleAnnotation | Περίγραμμα + χρώματα γεμίσματος, Title/ShowTitle |
CircleAnnotation | Περίγραμμα + γέμισμα, ShowBorder |
EllipseAnnotation | Περίγραμμα + γέμισμα, ShowBorder |
TriangleAnnotation | Χρώμα περιγράμματος, BackColor, ShowBorder |
LineAnnotation | Κατά ευθεία γραμμή με πλάτος και χρώμα |
ArrowAnnotation | Γραμμή με κεφαλή βέλους· ρυθμιζόμενη Direction (τύπος ArrowDirection, σημεία πυξίδας, προεπιλογή E) |
FreehandAnnotation | Ελεύθερη γραμμή από κωδικοποιημένα σημεία FreehandData |
ImageAnnotation | Εικόνα από URL. Ένα σχετικό URL επιλύεται σε σχέση με τον κεντρικό (request) host όταν προστίθεται το σχόλιο (η λήψη της εικόνας γίνεται μόνο κατά τη διαδικασία ενσωμάτωσης) — πρέπει να είναι προσβάσιμο από τον διακομιστή (π.χ. ένα αρχείο κάτω από wwwroot που εξυπηρετείται από UseStaticFiles). |
Το API του AnnotationManager
| Μέλος | Σκοπός |
|---|---|
Add(BaseAnnotation) | Προσθέτει ένα σχόλιο στην ουρά |
GetAnnotations() / GetAnnotations(int page) | Εξετάζει τι κρατά ο διαχειριστής |
ClearAnnotations() / ClearAnnotations(int page) | Καθαρίζει όλα / ανά σελίδα |
GetAnnotationData() / GetAnnotationData(int page) | Κωδικοποιημένη συμβολοσειρά δεδομένων σχολίων — ένα envelope Base64 (αυτό που καταναλώνει το widget) |
GetAnnotationXml() | Μορφή XML |
Το 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");
});Οι εξαγωγές χρησιμοποιούν τον ίδιο μηχανισμό ενσωμάτωσης όπως η απόδοση στην οθόνη, έτσι ό,τι βλέπουν οι χρήστες είναι αυτό που περιέχει το αρχείο.
Ροή εργασίας διατήρησης
- Ανοίξτε το έγγραφο και λάβετε το token του.
- Φορτώστε το προηγούμενα αποθηκευμένο XML ή κωδικοποιημένα δεδομένα σε αυτό το token.
- Επιτρέψτε στο widget να διαβάσει και να επεξεργαστεί τα σχόλια της συνεδρίας.
- Ανακτήστε το XML με
GetAnnotationXML(token)όταν η εφαρμογή σας αποφασίσει να το διατηρήσει. - Εξάγετε PDF/PNG όταν απαιτείται ένα επίπεδο (flattened) παραδοτέο.
- Κλείστε τη συνεδρία του εγγράφου.
Μην χρησιμοποιείτε το αδιαφανές token του viewer ως μόνιμο αναγνωριστικό σχολίου. Συσχετίστε τα διατηρημένα δεδομένα σχολίων με τα δικά σας αναγνωριστικά εγγράφου και έκδοσης.
Σημειώσεις ασφαλείας και απόδοσης
- Τα αιτήματα σχολίων χρησιμοποιούν την ίδια ασφάλεια συνεδρίας/token όπως τα αιτήματα σελίδας.
- Ένα σχετικό URL
ImageAnnotationεπιλύεται από τον host του αιτήματος και πρέπει να παραμένει προσβάσιμο στον διακομιστή κατά τη διαδικασία ενσωμάτωσης. - Επικυρώστε και ελέγξτε οποιοδήποτε URL εικόνας που παρέχεται από τον χρήστη για να αποφύγετε παραποίηση αιτημάτων από τον διακομιστή.
- Οι εξαγωγές εφαρμόζουν την ίδια απόφαση άδειας/προσαρμοσμένου υδατογραφήματος όπως η απόδοση σελίδας στην οθόνη.
- Μεγάλα payload ελεύθερων γραμμών και εξαγωγές υψηλής ανάλυσης αυξάνουν τη χρήση μνήμης· δοκιμάστε ρεαλιστικά έγγραφα και τιμές ζουμ.
Επίλυση προβλημάτων
| Σύμπτωμα | Έλεγχος |
|---|---|
| Λείπει η γραμμή σχολίων | Annotation δυνατότητα και οι τέσσερις σημαίες CSS/script σχολίων |
| Το callback αποθήκευσης αναφέρει σφάλμα | Λήξη token/συνεδρίας και middleware BasePath |
| Τα σχόλια C# δεν εμφανίζονται | Η αρίθμηση σελίδων ξεκινά από το 1 και τα δεδομένα φορτώθηκαν στο ενεργό token |
| Η εικόνα σχολίου εμφανίζεται στην οθόνη αλλά όχι στην εξαγωγή | Ο διακομιστής μπορεί να φτάσει στο URL της εικόνας κατά τη διαδικασία ενσωμάτωσης |
| Το επανανοιγμένο έγγραφο δεν έχει σχόλια | Διατηρήστε το XML/δεδομένα εκτός της συνεδρίας viewer, έπειτα φορτώστε τα στο νέο token |
Ήταν αυτή η σελίδα χρήσιμη;