Σχόλια
Προσθήκη υποστήριξης Annotation στον προβολέα
Τα Annotation στο Doconut λειτουργούν σε δύο κατευθύνσεις: οι χρήστες τα σχεδιάζουν στο widget του προγράμματος περιήγησης και ο διακομιστής τα αποθηκεύει ανά σελίδα, ή ο κώδικάς σας τα δημιουργεί προγραμματιστικά και τα φορτώνει σε μια ανοιχτή συνεδρία. Σε κάθε περίπτωση εμφανίζονται στις σελίδες και μπορούν να ενσωματωθούν σε εξαγωγές PDF/PNG.
Η υποστήριξη Annotation ελέγχεται από τη δυνατότητα άδειας Annotation (χορηγείται αυτόματα με ενεργή προσωρινή άδεια).
Ενεργοποίηση του UI Annotation
Το Annotation είναι ένα module του Viewer, όχι μια αυτόνομη γραμμή εργαλείων. Η πλήρης σελίδα πρέπει να περιλαμβάνει τους πόρους του Viewer, τη γραμμή εργαλείων του Viewer, το mount του Viewer και το αρχικοποιημένο objViewer; η Ribbon του Annotation στη συνέχεια τοποθετείται και συνδέεται με το ίδιο παράδειγμα.
Εκδώστε τα πακέτα annotation μαζί με τα πακέτα του 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">
<!-- Έλεγχοι προβολέα, συμπεριλαμβανομένου του κουμπιού που ανοίγει το 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) συμβαίνει αυτόματα όταν μια σελίδα με Annotation αποδίδεται. Τα τέσσερα callbacks onAnn* διατηρούν τη Ribbon συγχρονισμένη με τον κύκλο ζωής του viewer.
Ανοίξτε και κλείστε το από οποιαδήποτε γραμμή εργαλείων Viewer που ανήκει στον κεντρικό (host):
annBar.open();
annBar.close();Το δημόσιο API της Ribbon είναι:
| Μέθοδος | Σκοπός |
|---|---|
attach(objViewer) | Συνδέει τη Ribbon με τον αρχικοποιημένο viewer· απαιτείται μία φορά |
open() / close() | Είσοδος ή έξοδος από την επεξεργασία Annotation |
reset() | Επιστρέφει τη Ribbon στην κλειστή, μη‑επεξεργαστική κατάσταση |
isOpen() / annotating() | Διαβάζει την κατάσταση της Ribbon / την κατάσταση επεξεργασίας Annotation του viewer |
reopenEditable() | Επαναφορτώνει τα Annotation της τρέχουσας σελίδας ως επεξεργάσιμα αντικείμενα |
updateActionState() | Ανανέωση της διαθεσιμότητας των ελέγχων αποθήκευσης/διαγραφής μετά από αλλαγές του host |
headerSlot() | Λαμβάνει το προαιρετικό slot επέκτασης κεφαλίδας για ελέγχους που ανήκουν στον host |
onStatus, onToast, onLayout, onEditStart και onEditEnd είναι προαιρετικά callbacks του host. Το αντικείμενο endpoints μπορεί επιπλέον να παρέχει exportPdf, exportPng, imageUpload και imageList; οι έλεγχοι χωρίς ρυθμισμένο endpoint παραμένουν κρυμμένοι. Για τη συνδυασμένη ακολουθία εκκίνησης Viewer, Search και Annotation, δείτε Γρήγορη Έναρξη.
Το πακέτο annotation προσθέτει τα εργαλεία δημιουργίας στο πρόγραμμα περιήγησης, αλλά τα δεδομένα ανήκουν ακόμη στη συνεδρία εγγράφου στο διακομιστή που προσδιορίζεται από το token. Η επαναφορά του πηγής δημιουργεί μια νέα συνεδρία· διατηρήστε το XML ή το κωδικοποιημένο περιτύλιγμα Annotation στην εφαρμογή σας εάν τα Annotation πρέπει να παραμείνουν μετά το τέλος της συνεδρίας.
Δημιουργία σημειώσεων σε C#
Αποκτήστε έναν διαχειριστή συνδεδεμένο με την ανοιχτή συνεδρία, προσθέστε σημειώσεις και φορτώστε τις (με using Doconut.Annotations; για τους τύπους και using System.Drawing; για Rectangle/Color):
app.MapPost("/api/annotations/load-sample", (string token, Viewer viewer) =>
{
// Συνδεδεμένο με τις διαστάσεις σελίδας της ανοιχτής συνεδρίας
var manager = viewer.GetAnnotationManager(token);
var pageCount = viewer.GetPageCount(token);
// Ένα σφραγίδα ανά σελίδα
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));
// Φορτώνει στη συνεδρία — το widget τα ανακτά μέσω AnnLoad και το
// renderer τα ενσωματώνει σε εξαγωγές εικόνας/PDF.
viewer.LoadAnnotationData(token, manager);
return Results.Ok();
});Τύποι σημειώσεων
Όλοι οι τύποι βρίσκονται στο Doconut.Annotations και κληρονομούν από το BaseAnnotation (αριθμός σελίδας + περιβάλλον Rectangle):
| Τύπος | Σημειώσεις |
|---|---|
StampAnnotation | Σφραγίδα κειμένου με μέγεθος γραμματοσειράς, περιθώριο, χρώμα· υποστηρίζει Opacity, Rotate |
NoteAnnotation | Αυτοκόλλητη σημείωση με κείμενο, χρώμα φόντου, μέγεθος γραμματοσειράς, TitleColor |
RectangleAnnotation | Χρώματα περιγράμματος + γεμίσματος, Title/ShowTitle |
CircleAnnotation | Περιθώριο + γέμισμα, ShowBorder |
EllipseAnnotation | Περιθώριο + γέμισμα, ShowBorder |
TriangleAnnotation | Χρώμα περιγράμματος, BackColor, ShowBorder |
LineAnnotation | Κατά ευθεία γραμμή με πάχος και χρώμα |
ArrowAnnotation | Γραμμή με κεφαλή βέλους· ρυθμιζόμενο Direction (τύπος ArrowDirection, σημειώσεις πυξίδας, προεπιλογή E) |
FreehandAnnotation | Ελεύθερη γραμμή από κωδικοποιημένα σημεία FreehandData |
ImageAnnotation | Εικόνα από URL. Ένα σχετικό URL επιλύεται σε σχέση με τον κεντρικό υπολογιστή του αιτήματος όταν προστίθεται η σημείωση (η λήψη της εικόνας γίνεται μόνο κατά τη δημιουργία) — πρέπει να είναι προσβάσιμο από τον διακομιστή (π.χ. ένα αρχείο κάτω από wwwroot που εξυπηρετείται από UseStaticFiles) |
Το API του AnnotationManager
| Μέλος | Σκοπός |
|---|---|
Add(BaseAnnotation) | Προσθήκη σημείωσης στην ουρά |
GetAnnotations() / GetAnnotations(int page) | Επιθεώρηση του τι κρατά ο διαχειριστής |
ClearAnnotations() / ClearAnnotations(int page) | Αφαίρεση όλων / ανά σελίδα |
GetAnnotationData() / GetAnnotationData(int page) | Κωδικοποιημένη συμβολοσειρά δεδομένων σημειώσεων — ένα Base64 περιτύλιγμα (αυτό που καταναλώνει το widget) |
GetAnnotationXml() | Μορφή XML |
Viewer αντικατοπτρίζει τις λειτουργίες φόρτωσης/ανάγνωσης έναντι μιας συνεδρίας: LoadAnnotationData(token, manager) ή LoadAnnotationData(token, encodedData) (το Base64 περιτύλιγμα από το GetAnnotationData()), LoadAnnotationXML(token, xml), GetAnnotationXML(token).
Εξαγωγή με ενσωματωμένες σημειώσεις
// PDF όλων των σελίδων με τις σημειώσεις να αποδίδονται πάνω τους
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");
});
// Ή ένα ZIP με PNG ανά σελίδα
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 όταν απαιτείται ένα επίπεδο παραδοτέο.
- Κλείστε τη συνεδρία του εγγράφου.
Μην χρησιμοποιείτε το αδιαφανές token του viewer ως μόνιμο αναγνωριστικό σημείωσης. Συνδέστε τα αποθηκευμένα δεδομένα σημειώσεων με τα δικά σας αναγνωριστικά εγγράφου και έκδοσης.
Σημειώσεις ασφαλείας και απόδοσης
- Τα αιτήματα σχολιασμού χρησιμοποιούν την ίδια ασφάλεια συνεδρίας/διακριτικού όπως τα αιτήματα σελίδας.
- Ένα σχετικό
ImageAnnotationURL επιλύεται από τον κεντρικό υπολογιστή του αιτήματος και πρέπει να παραμένει προσβάσιμο από τον διακομιστή κατά τη διαδικασία δημιουργίας. - Επικυρώστε και ελέγξτε οποιοδήποτε URL εικόνας που παρέχεται από τον χρήστη για να αποφύγετε την πλαστογράφηση αιτημάτων από τον διακομιστή.
- Οι εξαγωγές εφαρμόζουν την ίδια απόφαση άδειας/προσαρμοσμένου υδατογραφήματος όπως η απόδοση σελίδας στην οθόνη.
- Μεγάλα φορτία ελεύθερης σχεδίασης και εξαγωγές υψηλής ανάλυσης αυξάνουν τη χρήση μνήμης· δοκιμάστε ρεαλιστικά έγγραφα και τιμές ζουμ.
Επίλυση προβλημάτων
| Συμπτωμα | Έλεγχος |
|---|---|
| Η κορδέλα σχολιασμού λείπει | Η δυνατότητα Annotation και οι τέσσερις σημαίες CSS/σκριπτ σχολιασμού |
| Η κλήση επιστροφής αποθήκευσης αναφέρει σφάλμα | Λήξη διακριτικού/συνεδρίας και middleware BasePath |
| Τα σχόλια C# δεν εμφανίζονται | Η αρίθμηση σελίδων ξεκινά από το 1 και τα δεδομένα φορτώθηκαν στο ενεργό διακριτικό |
| Η εικόνα σχολιασμού εμφανίζεται στην οθόνη αλλά όχι στην εξαγωγή | Ο διακομιστής μπορεί να φτάσει στο URL της εικόνας κατά τη δημιουργία |
| Το ξανανοιγμένο έγγραφο δεν έχει σχόλια | Διατηρήστε το XML/δεδομένα εκτός της συνεδρίας του προβολέα, στη συνέχεια φορτώστε τα στο νέο διακριτικό |
Ήταν αυτή η σελίδα χρήσιμη;