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):

csharp
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:

html
<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
@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ó:

MethodPurpose
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, onAnnClosed là các tùy chọn của docViewer({...}), giống như mọi callback của viewer.
  • Created, Changed, Deleted, và Properties là 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_ctlDoc id 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):

csharp
// 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ạiGhi chú
StampAnnotationDấu tem văn bản với kích thước phông, viền, màu; hỗ trợ Opacity, Rotate
NoteAnnotationGhi chú dán với văn bản, màu nền, kích thước phông, TitleColor
RectangleAnnotationMàu viền và màu nền, Title/ShowTitle
CircleAnnotationViền và nền, ShowBorder
EllipseAnnotationViền và nền, ShowBorder
TriangleAnnotationMà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)
FreehandAnnotationNét vẽ tự do từ các điểm FreehandData đã mã hoá
ImageAnnotationHì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ênMụ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

csharp
// 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ặtAi tải ảnhTrên máy chủ nội bộ hoặc môi trường phát triển
Trình xem trong trình duyệtTrình duyệt của người dùngHình ảnh hiển thị
ExportAnnotationsToPdfAsync / …PngZipAsyncMá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ữ

  1. Mở tài liệu và lấy token của nó.
  2. Tải XML hoặc dữ liệu đã mã hoá đã lưu trước vào token đó.
  3. Cho widget đọc và chỉnh sửa các chú thích của phiên.
  4. Lấy XML bằng GetAnnotationXML(token) khi ứng dụng của bạn quyết định lưu trữ.
  5. Xuất PDF hoặc PNG khi cần một tài liệu đã được làm phẳng.
  6. Đó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ứngKiểm tra
Ribbon chú thích bị thiếuKhả năng Annotation và bốn cờ CSS/script của chú thích
Lưu báo cáo lỗiToken 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ạyTê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ấtMá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íchLư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?