Анотації
Додайте підтримку анотацій до переглядача
Анотації в Doconut працюють у два напрямки: користувачі малюють їх у віджеті браузера, а сервер зберігає їх по сторінках, або ваш код створює їх і завантажує у відкриту сесію. У будь‑якому випадку вони відображаються на сторінках і можуть бути вбудовані у PDF або PNG експорт.
Підтримка анотацій обмежена можливістю ліцензії Annotation (надається автоматично при активній тимчасовій ліцензії).
Увімкнути інтерфейс анотацій
Анотація — це модуль Viewer, а не окрема панель інструментів. Сторінка повинна включати ресурси Viewer, панель інструментів Viewer, монтаж Viewer та ініціалізований objViewer; потім стрічка анотацій монтується та приєднується до того ж екземпляра.
Виведіть файли анотацій разом з файлами переглядача. Вони обмежені ліцензією, тому їхні теги з’являються лише коли можливість надана. У 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 у розмітці:
<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>Файли анотацій генерують стрічку всередині annBarMount; ви не копіюєте її кнопки чи діалогові вікна. Спочатку ініціалізуйте docViewer, а потім створюйте стрічку лише коли сервер підтвердить, що анотація ліцензована:
@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 не включає скрипт стрічки, тому серверна умова if захищає виклик doconutAnnotationBar(...) від поломки сторінки. У Web Forms напишіть таку ж захисну умову: <% if (IsAnnotationEnabled) { %> … <% } %>.
Збереження зі стрічки надсилає дані до DocImage.axd (AnnSave), який зберігає їх у сесії документа по сторінках. Завантаження (AnnLoad) відбувається автоматично, коли сторінка з анотаціями відображається.
Відкривайте та закривайте стрічку зі своєї панелі інструментів за допомогою annBar.open() та annBar.close(). Її публічний API:
| Method | Purpose |
|---|---|
attach(objViewer) | Підключає стрічку до ініціалізованого переглядача; потрібно один раз |
open() / close() | Відкрити або закрити режим редагування анотацій |
reset() | Повертає стрічку у закрите, не редагувальне стан |
isOpen() / annotating() | Отримати стан стрічки / стан редагування анотацій у переглядачі |
reopenEditable() | Перезавантажити анотації поточної сторінки як редаговані об’єкти |
updateActionState() | Оновити елементи управління збереженням та видаленням після змін хоста |
headerSlot() | Опціональний слот заголовка для елементів, керованих хостом |
onStatus, onToast, onLayout, onEditStart та onEditEnd — це необов’язкові зворотні виклики хоста. Об’єкт endpoints також може містити exportPdf, exportPng, imageUpload та imageList; елементи без налаштованого кінцевого пункту залишаються прихованими.
Два типи зворотних викликів, підключені двома способами
Код анотацій використовує два стилі зворотних викликів, і кожен безшумно не працює, коли підключений іншим способом:
onAnnLoaded,onAnnSaved,onAnnSaveError,onAnnClosedє опціямиdocViewer({...}), як і будь‑який зворотний виклик переглядача.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):
| Тип | Примітки |
|---|---|
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) | Закодовані дані анотацій — обгортка, яку споживає віджет |
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");
}Експорти використовують той самий процес вбудовування, що й відображення на екрані, а також ту ж ліцензію та рішення щодо користувацького водяного знака.
Анотації зображень на інтранет‑хості експортуються як «Image blocked»
Переглядач і процес експорту отримують зображення ImageAnnotation з різних джерел:
| Середовище | Хто завантажує зображення | На інтранет‑хості або сервері розробки |
|---|---|---|
| Переглядач у браузері | Браузер користувача | Зображення відображається |
ExportAnnotationsToPdfAsync / …PngZipAsync | Сервер | Червоний "Image blocked" заповнювач |
Коли сервер завантажує зображення для вбудовування, він відхиляє адреси, що повертаються у приватні мережі: 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‑адреса зображення, яку взагалі не вдається розв’язати, призведе до того, що ця анотація зникне без помилки — перевіряйте відрендерені пікселі або мережевий журнал, а не код статусу.
Робочий процес збереження
- Відкрийте документ і отримайте його токен.
- Завантажте раніше збережений XML або закодовані дані у цей токен.
- Дайте віджету прочитати та редагувати анотації сеансу.
- Отримайте XML за допомогою
GetAnnotationXML(token), коли ваш застосунок вирішить зберегти дані. - Експортуйте PDF або PNG, коли потрібен сплощений результат.
- Закрийте сеанс документа.
Не використовуйте токен переглядача як постійний ідентифікатор анотації — він зникає разом із сеансом, а перезапуск пулу застосунків завершує кожен сеанс на окремому сервері. Зберігайте дані анотацій у зв’язку зі своїми ідентифікаторами документів та їх версій.
Примітки щодо безпеки та рендерингу
- Запити анотацій використовують ту ж безпеку сеансу та токену, що й запити сторінок.
- Перевіряйте та контролюйте будь‑яку URL‑адресу зображення, яку надає користувач.
- Великі довільні дані та експорти високої роздільної здатності збільшують використання пам’яті; тестуйте реалістичні документи та значення масштабування.
Усунення проблем
| Симптом | Перевірка |
|---|---|
| Панель інструментів анотацій відсутня | Наявність можливості Annotation та чотири прапорці CSS/скриптів анотації |
| При збереженні виникає помилка | Термін дії токену або сеансу, а також BasePath віджету |
ctlDoc_Created (або інша функція події) ніколи не виконується | Правильна назва функції: ідентифікатор переглядача + _ + назва події, оголошена глобально |
| C# анотації не відображаються | Номери сторінок починаються з 1, і дані були завантажені в активний токен |
| Анотація зображення відображається на екрані, але не в експорті | Хост зображення має приватну або loopback‑адресу — див. вище |
| При повторному відкритті документа анотації відсутні | Збережіть XML або дані поза сеансом переглядача, потім завантажте їх у новий токен |
Чи була ця сторінка корисною?