Σχόλια
Προσθήκη υποστήριξης σχολίων στον προβολέα
Οι Σχόλια στο Doconut λειτουργούν σε δύο κατευθύνσεις: οι χρήστες τα σχεδιάζουν στο widget του προγράμματος περιήγησης και ο διακομιστής τα αποθηκεύει ανά σελίδα, ή ο κώδικάς σας τα δημιουργεί και τα φορτώνει σε μια ανοιχτή συνεδρία. Σε κάθε περίπτωση εμφανίζονται στις σελίδες και μπορούν να ενσωματωθούν σε εξαγωγές PDF ή PNG.
Η υποστήριξη Σχολίων ελέγχεται από τη δυνατότητα άδειας Annotation (χορηγείται αυτόματα με ενεργή προσωρινή άδεια).
Ενεργοποίηση του UI σχολίων
Το Annotation είναι ένα module του Viewer, όχι μια αυτόνομη γραμμή εργαλείων. Η σελίδα πρέπει να περιλαμβάνει τους πόρους του Viewer, τη γραμμή εργαλείων του Viewer, το mount του Viewer και ένα αρχικοποιημένο objViewer; το Annotation Ribbon στη συνέχεια τοποθετείται και συνδέεται με το ίδιο αντικείμενο.
Εξάγετε τα αρχεία σχολίων μαζί με τα αρχεία του viewer. Είναι περιορισμένα από άδεια, έτσι οι ετικέτες τους εμφανίζονται μόνο όταν η δυνατότητα χορηγείται. Στο MVC 5, δημιουργήστε τις ετικέτες στον ελεγκτή (οι προβολές δεν έχουν @inject):
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;Διατηρήστε τη πλήρη σύνθεση του 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 μέσα στο annBarMount; δεν χρειάζεται να αντιγράψετε τα κουμπιά ή τα διαλόγους του. Αρχικοποιήστε πρώτα το docViewer, έπειτα δημιουργήστε το Ribbon μόνο όταν ο διακομιστής επιβεβαιώσει ότι το Annotation είναι αδειοδοτημένο:
@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>Χωρίς τη δυνατότητα, το ReferenceScripts παραλείπει το script του Ribbon, έτσι η διακομιστική if αποτρέπει την κλήση doconutAnnotationBar(...) από το να σπάσει τη σελίδα. Στα Web Forms, γράψτε την ίδια προειδοποίηση ως <% if (IsAnnotationEnabled) { %> … <% } %>.
Η αποθήκευση από το Ribbon στέλνει τα δεδομένα στο DocImage.axd (AnnSave), το οποίο τα αποθηκεύει στη συνεδρία του εγγράφου ανά σελίδα. Η φόρτωση (AnnLoad) γίνεται αυτόματα όταν μια σελίδα με σχόλια αποδίδεται.
Ανοίξτε και κλείστε το Ribbon από τη δική σας γραμμή εργαλείων με annBar.open() και annBar.close(). Το δημόσιο API του:
| Μέθοδος | Σκοπός |
|---|---|
attach(objViewer) | Συνδέει το Ribbon με τον αρχικοποιημένο viewer· απαιτείται μία φορά |
open() / close() | Εισέρχεται ή εξέρχεται από την επεξεργασία σχολίων |
reset() | Επαναφέρει το Ribbon στην κλειστή, μη‑επεξεργαστική κατάσταση |
isOpen() / annotating() | Διαβάζει την κατάσταση του Ribbon / την κατάσταση επεξεργασίας σχολίων του viewer |
reopenEditable() | Φορτώνει ξανά τα σχόλια της τρέχουσας σελίδας ως επεξεργάσιμα αντικείμενα |
updateActionState() | Ανανεώνει τα κουμπιά αποθήκευσης και διαγραφής μετά από αλλαγές του κεντρικού συστήματος |
headerSlot() | Η προαιρετική θέση κεφαλίδας για ελεγκτικά που ανήκουν στο κεντρικό σύστημα |
onStatus, onToast, onLayout, onEditStart και onEditEnd είναι προαιρετικές κλήσεις του κεντρικού συστήματος. Το αντικείμενο endpoints μπορεί επίσης να παρέχει exportPdf, exportPng, imageUpload και imageList; τα ελεγκτικά χωρίς ρυθμισμένο endpoint παραμένουν κρυμμένα.
Δύο είδη callback, συνδεδεμένα με δύο τρόπους
Ο κώδικας σχολιασμού χρησιμοποιεί δύο στυλ callback, και το καθένα αποτυγχάνει σιωπηρά όταν συνδεθεί με τον αντίθετο τρόπο:
onAnnLoaded,onAnnSaved,onAnnSaveError,onAnnClosedείναι επιλογές τουdocViewer({...}), όπως κάθε callback προβολέα.Created,Changed,Deleted, καιPropertiesείναι παγκόσμιες συναρτήσεις που εντοπίζονται με το όνομα: το αναγνωριστικό του προβολέα προσαρτάται στο γεγονός. Για τον προβολέα στο#div_ctlDocτο αναγνωριστικό είναιctlDoc, έτσι οι συναρτήσεις είναιctlDoc_Created,ctlDoc_Changed,ctlDoc_DeletedκαιctlDoc_Properties. Μια λανθασμένη ή μετονομασμένη συνάρτηση απλώς δεν καλείται ποτέ.
Το SaveAnnotations() δεν επιστρέφει αν αποθηκεύτηκε: το αποτέλεσμα φτάνει μέσω onAnnSaved ή onAnnSaveError. Ο κώδικας που διαβάζει την τιμή επιστροφής του ποτέ δεν καθαρίζει τη σημαία «αποθηκευμένες αλλαγές».
Δημιουργία σχολίων σε C#
Αποκτήστε έναν διαχειριστή δεσμευμένο στη ανοιχτή συνεδρία, προσθέστε σχόλια και φορτώστε τα (με using Doconut.Annotations; για τους τύπους και using System.Drawing; για Rectangle και Color):
// 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);
}Τύποι σχολίων
Όλοι οι τύποι βρίσκονται στο Doconut.Annotations και κληρονομούν από το BaseAnnotation (αριθμός σελίδας συν το περιβάλλον Rectangle):
| Type | Σημειώσεις |
|---|---|
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 λύνει σε σχέση με τον τρέχοντα κεντρικό υπολογιστή του αιτήματος όταν προστίθεται το σχόλιο — δείτε την παρατήρηση εξαγωγής παρακάτω |
Το API του AnnotationManager
| Μέλος | Σκοπός |
|---|---|
Add(BaseAnnotation) | Προσθέτει ένα σχόλιο στην ουρά |
GetAnnotations() / GetAnnotations(int page) | Επιθεωρεί τι κρατά ο διαχειριστής |
ClearAnnotations() / ClearAnnotations(int page) | Αφαιρεί όλα / ανά σελίδα |
GetAnnotationData() / GetAnnotationData(int page) | Κωδικοποιημένα δεδομένα σχολίων — το περιτύλιγμα που καταναλώνει το widget |
GetAnnotationXml() | Μορφή XML |
Viewer αντικατοπτρίζει τις λειτουργίες φόρτωσης και ανάγνωσης έναντι μιας συνεδρίας: LoadAnnotationData(token, manager) ή LoadAnnotationData(token, encodedData), LoadAnnotationXML(token, xml), και GetAnnotationXML(token).
Εξαγωγή με ενσωματωμένα σχόλια
// 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");
}Οι εξαγωγές χρησιμοποιούν τον ίδιο μηχανισμό ενσωμάτωσης με την απόδοση στην οθόνη, καθώς και την ίδια άδεια και την ίδια απόφαση για προσαρμοσμένο υδατογράφημα.
Σχόλια εικόνας σε εσωτερικό διακομιστή εξάγονται ως "Εικόνα αποκλεισμένη"
Ο προβολέας και η εξαγωγή λαμβάνουν την εικόνα ενός ImageAnnotation από διαφορετικές πηγές:
| Επιδερμίδα | Ποιος κατεβάζει την εικόνα | Σε εσωτερικό ή διακομιστή ανάπτυξης |
|---|---|---|
| Ο προβολέας στο πρόγραμμα περιήγησης | Το πρόγραμμα περιήγησης του χρήστη | Η εικόνα εμφανίζεται |
ExportAnnotationsToPdfAsync / …PngZipAsync | Ο διακομιστής | Ένας κόκκινος "Εικόνα αποκλεισμένη" δείκτης |
Όταν ο διακομιστής ανακτά μια εικόνα για ενσωμάτωση, απορρίπτει διευθύνσεις που οδηγούν πίσω σε ιδιωτικά δίκτυα: loopback (localhost, 127.0.0.1), 0.0.0.0, link‑local (169.254.x.x), οι ιδιωτικές περιοχές 10.x, 172.16–31.x και 192.168.x, carrier‑grade NAT (100.64.0.0/10) και τις IPv6 link‑local και unique‑local διευθύνσεις. Αυτό προστατεύει τον διακομιστή από το να χρησιμοποιηθεί για πρόσβαση σε εσωτερικά συστήματα μέσω μιας προσαρμοσμένης URL εικόνας, και δεν υπάρχει επιλογή για απενεργοποίηση.
Μια σχετική URL εικόνας επιλύεται σε σχέση με τον κεντρικό υπολογιστή του τρέχοντος αιτήματος, έτσι ώστε σε localhost, σε εσωτερικό διακομιστή ή σε οποιονδήποτε ιστότοπο που προσπελάζεται μέσω ιδιωτικής διεύθυνσης, η εικόνα εμφανίζεται στον προβολέα και η εξαγωγή περιέχει το δείκτη αντί για αυτήν. Αυτό είναι η λειτουργία της προστασίας, όχι αποτυχία απόδοσης. Εάν οι εξαγωγές πρέπει να περιλαμβάνουν την εικόνα, σερβίρετέ την από έναν κεντρικό υπολογιστή με δημόσια διεύθυνση και χρησιμοποιήστε απόλυτη URL. Μια URL εικόνας που δεν μπορεί καθόλου να επιλυθεί κάνει αυτήν τη σημείωση να εξαφανιστεί χωρίς σφάλμα — ελέγξτε τα αποδομένα pixel ή το αρχείο καταγραφής δικτύου, όχι τον κωδικό κατάστασης.
Ροή διατήρησης
- Ανοίξτε το έγγραφο και αποκτήστε το διακριτικό του.
- Φορτώστε το προηγουμένως αποθηκευμένο XML ή κωδικοποιημένα δεδομένα στο διακριτικό αυτό.
- Επιτρέψτε στο widget να διαβάσει και να επεξεργαστεί τις σημειώσεις της συνεδρίας.
- Ανακτήστε το XML με
GetAnnotationXML(token)όταν η εφαρμογή σας αποφασίσει να αποθηκεύσει. - Εξάγετε PDF ή PNG όταν απαιτείται ένα επίπεδο παραδοτέο.
- Κλείστε τη συνεδρία του εγγράφου.
Μην χρησιμοποιείτε το διακριτικό του προβολέα ως μόνιμο αναγνωριστικό σημείωσης — λήγει με τη συνεδρία, και η επαναφορά της ομάδας εφαρμογών τερματίζει κάθε συνεδρία σε έναν μοναδικό διακομιστή. Αποθηκεύστε τα δεδομένα σημειώσεων σε σχέση με τα δικά σας αναγνωριστικά εγγράφου και έκδοσης.
Σημειώσεις ασφαλείας και απόδοσης
- Τα αιτήματα σημειώσεων χρησιμοποιούν την ίδια ασφάλεια συνεδρίας και διακριτικού όπως τα αιτήματα σελίδας.
- Επικυρώστε και ελέγξτε οποιαδήποτε URL εικόνας που παρέχεται από τον χρήστη.
- Μεγάλα ελεύθερα payloads και εξαγωγές υψηλής ανάλυσης αυξάνουν τη χρήση μνήμης· δοκιμάστε ρεαλιστικά έγγραφα και τιμές ζουμ.
Επίλυση προβλημάτων
| Σύμπτωμα | Έλεγχος |
|---|---|
| Η κορδέλα Σημειώσεων λείπει | Η δυνατότητα Annotation και οι τέσσερις σημαίες CSS/σκριπτ σημειώσεων |
| Η αποθήκευση αναφέρει σφάλμα | Λήξη διακριτικού ή συνεδρίας, και το BasePath του widget |
ctlDoc_Created (ή άλλη λειτουργία συμβάντος) δεν εκτελείται ποτέ | Το όνομα της λειτουργίας: id προβολέα + _ + συμβάν, ως παγκόσμια λειτουργία |
| Οι σημειώσεις C# δεν εμφανίζονται | Οι αριθμοί σελίδων αρχίζουν από το 1, και τα δεδομένα φορτώθηκαν στο ενεργό διακριτικό |
| Μια σημείωση εικόνας εμφανίζεται στην οθόνη αλλά όχι στην εξαγωγή | Ο κεντρικός υπολογιστής της εικόνας έχει ιδιωτική ή loopback διεύθυνση — δείτε παραπάνω |
| Ένα ξανά ανοιγμένο έγγραφο δεν έχει σημειώσεις | Αποθηκεύστε το XML ή τα δεδομένα εκτός της συνεδρίας προβολέα, έπειτα φορτώστε τα στο νέο διακριτικό |
Ήταν αυτή η σελίδα χρήσιμη;