حاشیه‌نویسی‌ها

افزودن پشتیبانی از حاشیه‌نویسی به نمایشگر

حاشیه‌نویسی‌ها در Doconut به دو جهت کار می‌کنند: کاربران آن‌ها را در ویجت مرورگر می‌کشند و سرور آن‌ها را به ازای هر صفحه ذخیره می‌کند، یا کد شما آن‌ها را به‌صورت برنامه‌نویسی ساخته و در یک جلسه باز بارگذاری می‌کند. به هر حال، این حاشیه‌نویسی‌ها بر روی صفحات رندر می‌شوند و می‌توانند در خروجی‌های PDF/PNG تعبیه شوند.

پشتیبانی از حاشیه‌نویسی توسط قابلیت مجوز Annotation محدود شده است (به‌صورت خودکار تحت یک مجوز موقت فعال اعطا می‌شود).

فعال‌سازی رابط کاربری حاشیه‌نویسی

حاشیه‌نویسی یک ماژول Viewer است، نه یک نوار ابزار مستقل. صفحه کامل باید شامل منابع Viewer، نوار ابزار Viewer، نقطه نصب Viewer و objViewer اولیه باشد؛ سپس نوار Ribbon حاشیه‌نویسی نصب و به همان نمونه متصل می‌شود.

بسته‌های حاشیه‌نویسی را همراه با بسته‌های Viewer صادر کنید — آن‌ها تحت مجوز هستند، بنابراین برچسب‌ها فقط زمانی ظاهر می‌شوند که قابلیت موجود باشد:

html
@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) قابل مشاهده نگه دارید:

html
<nav id="toolbar" aria-label="Document viewer controls">
    <!-- کنترل‌های Viewer، شامل دکمه‌ای که Annotation را باز می‌کند -->
</nav>
<div id="annBarMount"></div>
<div id="divDocViewer"><div id="div_ctlDoc"></div></div>

بسته حاشیه‌نویسی DOM نوار Ribbon را داخل annBarMount تولید می‌کند؛ نیازی به کپی کردن دکمه‌ها یا نشانه‌گذاری دیالوگ آن ندارید. ابتدا docViewer را مقداردهی اولیه کنید، سپس نوار Ribbon را فقط زمانی ایجاد کنید که سرور تأیید کند حاشیه‌نویسی دارای مجوز است:

html
<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 داده‌ها را از طریق میانی‌افزار (AnnSave) ارسال می‌کند، که آن را در جلسه سند به ازای هر صفحه ذخیره می‌نماید. بارگذاری (AnnLoad) به‌صورت خودکار زمانی که صفحه‌ای با حاشیه‌نویسی‌ها رندر می‌شود، انجام می‌شود. چهار کال‌بک onAnn* نوار Ribbon را با چرخه حیات Viewer همگام نگه می‌دارند.

باز و بسته کردن آن از هر نوار ابزار Viewer متعلق به میزبان:

javascript
annBar.open();
annBar.close();

API عمومی نوار Ribbon عبارت است از:

متدهدف
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 را فراهم کند؛ کنترل‌هایی که نقطه انتهایی پیکربندی نشده دارند، مخفی می‌مانند. برای توالی راه‌اندازی ترکیبی Viewer، Search و Annotation، به شروع سریع مراجعه کنید.

بسته حاشیه‌نویسی ابزارهای نویسندگی مرورگر را اضافه می‌کند، اما داده‌ها همچنان متعلق به جلسه سند سمت سرور هستند که با توکن شناسایی می‌شود. باز کردن مجدد منبع یک جلسه جدید ایجاد می‌کند؛ اگر حاشیه‌نویسی‌ها باید پس از پایان عمر جلسه باقی بمانند، XML یا پاکت حاشیه‌نویسی رمزگذاری‌شده را در برنامه خود نگهداری کنید.

ساخت حاشیه‌نویسی‌ها در C#

یک مدیر مرتبط با جلسه باز دریافت کنید، حاشیه‌نویسی‌ها را اضافه کنید و آنها را بارگذاری کنید (با using Doconut.Annotations; برای انواع و using System.Drawing; برای Rectangle/Color):

csharp
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));

    // بارگذاری در جلسه — ویجت آن‌ها را از طریق 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 (آنچه ویجت مصرف می‌کند)
GetAnnotationXml()فرم XML

Viewer عملیات بارگذاری/خواندن را نسبت به یک جلسه بازتاب می‌دهد: LoadAnnotationData(token, manager) یا LoadAnnotationData(token, encodedData) (پاکت Base64 از GetAnnotationData())، LoadAnnotationXML(token, xml)، GetAnnotationXML(token).

خروجی با حاشیه‌نویسی‌های سوزانده‌شده

csharp
// 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");
});

خروجی‌ها از همان موتور سوزاندن که برای رندر روی صفحه استفاده می‌شود استفاده می‌کنند، بنابراین آنچه کاربران می‌بینند همان چیزی است که فایل شامل آن است.

جریان کار حفظ‌سازی

  1. سند را باز کنید و توکن آن را دریافت کنید.
  2. XML یا داده‌های رمزگذاری‌شده قبلاً ذخیره‌شده را در آن توکن بارگذاری کنید.
  3. اجازه دهید ویجت حاشیه‌نویسی‌های جلسه را بخواند و ویرایش کند.
  4. XML را با GetAnnotationXML(token) دریافت کنید وقتی برنامه شما تصمیم به حفظ‌سازی می‌گیرد.
  5. PDF/PNG را خروجی بگیرید وقتی یک تحویل مسطح (flattened) مورد نیاز است.
  6. جلسه سند را ببندید.

از توکن شفاف Viewer به عنوان شناسه دائمی حاشیه‌نویسی استفاده نکنید. داده‌های حاشیه‌نویسی حفظ‌شده را با شناسه‌های سند و نسخه خودتان مرتبط کنید.

نکات امنیتی و رندرینگ

  • درخواست‌های حاشیه‌نویسی از همان امنیت جلسه/توکن همانند درخواست‌های صفحه استفاده می‌کنند.
  • یک URL نسبی ImageAnnotation از میزبان درخواست حل می‌شود و باید در زمان پردازش برای سرور قابل دسترسی بماند.
  • هر URL تصویری که توسط کاربر ارائه می‌شود را اعتبارسنجی و کنترل کنید تا از جعل درخواست سمت سرور جلوگیری شود.
  • خروجی‌ها همان تصمیم‌گیری دربارهٔ لایسنس/آب‌نشان سفارشی را که در رندر صفحه روی صفحه نمایش اعمال می‌شود، به کار می‌برند.
  • بارهای بزرگ دست‌نویس و خروجی‌های با وضوح بالا مصرف حافظه را افزایش می‌دهند؛ اسناد واقعی و مقادیر زوم را تست کنید.

عیب‌یابی

علامتبررسی
نوار حاشیه‌نویسی گم شده استقابلیت Annotation و چهار پرچم CSS/اسکریپت حاشیه‌نویسی
بازگشت‌تابع ذخیره خطایی گزارش می‌دهدانقضای توکن/جلسه و میانی‌افزار BasePath
حاشیه‌نویسی‌های C# ظاهر نمی‌شوندشماره‌گذاری صفحات از یک شروع می‌شود و داده‌ها به توکن فعال بارگذاری شده‌اند
حاشیه‌نویسی تصویر روی صفحه نمایش ظاهر می‌شود اما در خروجی نیستسرور می‌تواند در زمان پردازش به URL تصویر دسترسی داشته باشد
سند بازگشایی شده حاشیه‌نویسی نداردXML/داده‌ها را خارج از جلسه نمایش‌گر ذخیره کنید، سپس آن را به توکن جدید بارگذاری کنید

آیا این صفحه مفید بود؟