Chú thích
Thêm hỗ trợ chú thích cho trình xem
Chú thích trong Doconut hoạt động theo hai hướng: người dùng vẽ chúng trong widget trình duyệt và máy chủ lưu chúng theo từng trang, hoặc mã của bạn tạo chúng và tải chúng vào một phiên mở. Dù theo cách nào, chúng sẽ được hiển thị trên các trang và có thể được nhúng vào các tệp xuất PDF hoặc PNG.
Hỗ trợ chú thích được kiểm soát bởi khả năng cấp phép Annotation (được cấp tự động khi có giấy phép Temporary đang hoạt động).
Bật giao diện người dùng chú thích
Chú thích là một mô-đun Viewer, không phải là một thanh công cụ độc lập. Trang phải bao gồm các tài nguyên Viewer, thanh công cụ Viewer, phần gắn Viewer, và một objViewer đã được khởi tạo; sau đó Ribbon Chú thích sẽ được gắn và đính kèm vào cùng một thể hiện.
Phát các tệp chú thích cùng với các tệp viewer. Chúng được kiểm soát bằng giấy phép, vì vậy các thẻ của chúng chỉ xuất hiện khi khả năng được cấp. Trong MVC 5, xây dựng các thẻ trong controller (các view không có @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;Giữ toàn bộ cấu trúc Viewer trong 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>Các tệp Chú thích tạo Ribbon bên trong annBarMount; bạn không cần sao chép các nút hoặc hộp thoại của nó. Khởi tạo docViewer trước, sau đó tạo Ribbon chỉ khi máy chủ xác nhận rằng Chú thích đã được cấp phép:
@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>Nếu không có khả năng này, ReferenceScripts sẽ bỏ qua script của Ribbon, vì vậy câu lệnh if phía máy chủ giữ cho lời gọi doconutAnnotationBar(...) không được bảo vệ không làm hỏng trang. Trong Web Forms, viết cùng một bảo vệ như <% if (IsAnnotationEnabled) { %> … <% } %>.
Lưu từ Ribbon sẽ gửi dữ liệu tới DocImage.axd (AnnSave), nơi lưu trữ nó trong phiên tài liệu theo từng trang. Tải (AnnLoad) diễn ra tự động khi một trang có chú thích được hiển thị.
Mở và đóng Ribbon từ thanh công cụ của bạn bằng annBar.open() và annBar.close(). API công cộng của nó:
| Method | Purpose |
|---|---|
attach(objViewer) | Kết nối Ribbon với viewer đã khởi tạo; cần thực hiện một lần |
open() / close() | Bắt đầu hoặc kết thúc việc chỉnh sửa chú thích |
reset() | Đưa Ribbon trở lại trạng thái đóng, không chỉnh sửa |
isOpen() / annotating() | Đọc trạng thái của Ribbon / trạng thái chỉnh sửa chú thích của viewer |
reopenEditable() | Tải lại các chú thích của trang hiện tại dưới dạng đối tượng có thể chỉnh sửa |
updateActionState() | Làm mới các điều khiển lưu và xóa sau khi máy chủ thay đổi |
headerSlot() | Khe tiêu đề tùy chọn cho các điều khiển thuộc sở hữu của máy chủ |
onStatus, onToast, onLayout, onEditStart, và onEditEnd là các callback tùy chọn của máy chủ. Đối tượng endpoints cũng có thể cung cấp exportPdf, exportPng, imageUpload, và imageList; các điều khiển không có endpoint được cấu hình sẽ bị ẩn.
Hai loại callback, được kết nối theo hai cách
Mã chú thích sử dụng hai kiểu callback, và mỗi kiểu sẽ thất bại một cách im lặng khi được kết nối theo cách còn lại:
onAnnLoaded,onAnnSaved,onAnnSaveError,onAnnClosedlà các tùy chọn củadocViewer({...}), giống như mọi callback của viewer.Created,Changed,Deleted, vàPropertieslà các hàm toàn cục được tìm theo tên: id của viewer được đặt trước sự kiện. Đối với viewer trong#div_ctlDocid làctlDoc, vì vậy các hàm sẽ làctlDoc_Created,ctlDoc_Changed,ctlDoc_Deleted, vàctlDoc_Properties. Một hàm bị viết sai chính tả hoặc đổi tên sẽ đơn giản không bao giờ được gọi.
SaveAnnotations() không trả về việc nó đã lưu hay chưa: kết quả sẽ đến qua onAnnSaved hoặc onAnnSaveError. Mã đọc giá trị trả về của nó sẽ không bao giờ xóa cờ "các thay đổi chưa lưu".
Xây dựng chú thích trong C#
Lấy một manager được ràng buộc với phiên mở, thêm các chú thích, và tải chúng (với using Doconut.Annotations; cho các kiểu và using System.Drawing; cho Rectangle và 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);
}Các loại chú thích
Tất cả các loại nằm trong Doconut.Annotations và kế thừa từ BaseAnnotation (số trang cộng với Rectangle bao quanh):
| Loại | Ghi chú |
|---|---|
StampAnnotation | Dấu tem văn bản với kích thước phông, viền, màu; hỗ trợ Opacity, Rotate |
NoteAnnotation | Ghi chú dán với văn bản, màu nền, kích thước phông, TitleColor |
RectangleAnnotation | Màu viền và màu nền, Title/ShowTitle |
CircleAnnotation | Viền và nền, ShowBorder |
EllipseAnnotation | Viền và nền, ShowBorder |
TriangleAnnotation | Màu viền, BackColor, ShowBorder |
LineAnnotation | Đường thẳng với độ rộng và màu |
ArrowAnnotation | Đường có mũi tên; có thể đặt Direction (ArrowDirection, các hướng bắc, nam, …, mặc định E) |
FreehandAnnotation | Nét vẽ tự do từ các điểm FreehandData đã mã hoá |
ImageAnnotation | Hình ảnh từ một URL. Một URL tương đối sẽ được giải quyết dựa trên host của yêu cầu hiện tại khi chú thích được thêm — xem ghi chú xuất khẩu bên dưới |
API của AnnotationManager
| Thành viên | Mục đích |
|---|---|
Add(BaseAnnotation) | Đưa một chú thích vào hàng đợi |
GetAnnotations() / GetAnnotations(int page) | Kiểm tra những gì manager đang giữ |
ClearAnnotations() / ClearAnnotations(int page) | Xóa tất cả / theo trang |
GetAnnotationData() / GetAnnotationData(int page) | Dữ liệu chú thích đã mã hoá — phong bì mà widget tiêu thụ |
GetAnnotationXml() | Dạng XML |
Viewer phản chiếu các thao tác tải và đọc đối với một phiên: LoadAnnotationData(token, manager) hoặc LoadAnnotationData(token, encodedData), LoadAnnotationXML(token, xml), và GetAnnotationXML(token).
Xuất với các chú thích đã được nhúng
// 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");
}Quá trình xuất sử dụng cùng bộ công cụ đốt như khi render trên màn hình, và cùng giấy phép và quyết định watermark tùy chỉnh.
Chú thích hình ảnh trên máy chủ nội bộ khi xuất hiển thị "Image blocked"
Trình xem và quá trình xuất tải ảnh của ImageAnnotation từ các vị trí khác nhau:
| Bề mặt | Ai tải ảnh | Trên máy chủ nội bộ hoặc môi trường phát triển |
|---|---|---|
| Trình xem trong trình duyệt | Trình duyệt của người dùng | Hình ảnh hiển thị |
ExportAnnotationsToPdfAsync / …PngZipAsync | Máy chủ | Một chỗ giữ chỗ màu đỏ "Image blocked" |
Khi máy chủ tải một hình ảnh để nhúng vào, nó từ chối các địa chỉ trỏ lại vào mạng nội bộ: vòng lặp (localhost, 127.0.0.1), 0.0.0.0, link‑local (169.254.x.x), các dải riêng 10.x, 172.16–31.x, và 192.168.x, NAT cấp nhà mạng (100.64.0.0/10), và các địa chỉ IPv6 link‑local và unique‑local. Điều này bảo vệ máy chủ khỏi việc bị sử dụng để truy cập các hệ thống nội bộ thông qua một URL hình ảnh được tạo ra, và không có công tắc nào để tắt tính năng này.
Một URL hình ảnh tương đối được giải quyết dựa trên máy chủ của yêu cầu hiện tại, vì vậy trên localhost, trên máy chủ nội bộ, hoặc trên bất kỳ trang nào được truy cập qua địa chỉ riêng, hình ảnh sẽ hiển thị trong trình xem và bản xuất sẽ chứa chỗ giữ chỗ thay vì hình ảnh. Đó là cơ chế bảo vệ đang hoạt động, không phải lỗi hiển thị. Nếu bản xuất cần chứa hình ảnh, hãy phục vụ nó từ một máy chủ có địa chỉ công cộng và sử dụng URL tuyệt đối. Một URL hình ảnh không thể giải quyết được sẽ khiến chú thích đó biến mất mà không có lỗi — hãy kiểm tra các pixel đã render hoặc nhật ký mạng, không phải mã trạng thái.
Quy trình lưu trữ
- Mở tài liệu và lấy token của nó.
- Tải XML hoặc dữ liệu đã mã hoá đã lưu trước vào token đó.
- Cho widget đọc và chỉnh sửa các chú thích của phiên.
- Lấy XML bằng
GetAnnotationXML(token)khi ứng dụng của bạn quyết định lưu trữ. - Xuất PDF hoặc PNG khi cần một tài liệu đã được làm phẳng.
- Đóng phiên tài liệu.
Không sử dụng token của trình xem làm định danh chú thích cố định — nó sẽ mất khi phiên kết thúc, và việc tái khởi động pool ứng dụng sẽ kết thúc mọi phiên trên một máy chủ. Lưu dữ liệu chú thích dựa trên tài liệu và định danh phiên bản của riêng bạn.
Ghi chú bảo mật và hiển thị
- Yêu cầu chú thích sử dụng cùng cơ chế bảo mật session và token như các yêu cầu trang.
- Xác thực và kiểm soát bất kỳ URL hình ảnh do người dùng cung cấp.
- Các payload tự do lớn và xuất ở độ phân giải cao làm tăng việc sử dụng bộ nhớ; hãy thử nghiệm với tài liệu thực tế và các giá trị phóng to.
Khắc phục sự cố
| Triệu chứng | Kiểm tra |
|---|---|
| Ribbon chú thích bị thiếu | Khả năng Annotation và bốn cờ CSS/script của chú thích |
| Lưu báo cáo lỗi | Token hoặc phiên đã hết hạn, và BasePath của widget |
ctlDoc_Created (hoặc hàm sự kiện khác) không bao giờ chạy | Tên hàm: id của trình xem + _ + sự kiện, dưới dạng hàm toàn cục |
| Chú thích C# không hiển thị | Số trang bắt đầu từ 1, và dữ liệu đã được tải vào token đang hoạt động |
| Chú thích hình ảnh hiển thị trên màn hình nhưng không có trong bản xuất | Máy chủ của hình ảnh có địa chỉ riêng hoặc vòng lặp — xem phần trên |
| Tài liệu mở lại không có chú thích | Lưu XML hoặc dữ liệu bên ngoài phiên trình xem, sau đó tải vào token mới |
Trang này có hữu ích không?