البدء السريع

اعرض مستندك الأول في دقائق

يأخذك هذا الدليل من تطبيق ASP.NET Core فارغ في Program.cs إلى مستند يُعرض في المتصفح: تسجيل الخادم، حزمة المشاهد الكاملة (شريط أدوات المشاهد، تثبيت المشاهد، وأشرطة البحث/التعليق الاختيارية)، مراجع الأصول، تهيئة العميل، فتح المستند، والتنفيذ.

إعداد الخادم

AddDoconut() يسجل الخدمات؛ UseDoconutResources() و UseDoconut() يربطان الطبقة الوسيطة. يجب أن يأتي استدعاء الموارد أولاً. استدعاءات الجلسة مطلوبة أيضًا — أمان المستند الافتراضي لـ Doconut يتحقق من كل طلب صفحة مقابل حالة جلسة ASP.NET. تحتاج حالة الجلسة إلى ذاكرة تخزين مؤقت: AddDistributedMemoryCache() توفر واحدة. تطبيق MVC أو Razor Pages يسجلها بالفعل، لكن تطبيق minimal‑API لا يفعل ذلك، وبدونها يتوقف التطبيق عند بدء التشغيل مع الخطأ Unable to resolve service for type 'IDistributedCache'. هل قمت بتسجيل Doconut بالفعل خلال التثبيت؟ انتقل إلى القسم التالي.

csharp
builder.Services.AddDoconut(options =>
{
    options.LicensePath = "Doconut.Viewer.lic";
});
builder.Services.AddDistributedMemoryCache(); // حالة الجلسة تحتاج إلى ذاكرة تخزين مؤقت (MVC/Razor Pages يضيفها بالفعل)
builder.Services.AddSession(); // أمان مستند Doconut يعتمد على حالة جلسة ASP.NET

app.UseSession();          // استدعِ UseSession() قبل UseDoconut()
app.UseDoconutResources(); // يجب تسجيله قبل UseDoconut()
app.UseDoconut();

للتخطيط على نمط الإنتاج، قم بربط طبقة الوسيط للمستند بفرع صريح واحفظ إعدادات المسارات الأربعة متناسقة:

csharp
builder.Services.AddDoconut(options =>
{
    options.LicensePath = Path.Combine(AppContext.BaseDirectory, "wwwroot", "Doconut.Viewer.lic");
    options.MiddlewarePath = "/doconut";
    options.ResourcesPath = "/doconut-res";
    options.UnsafeMode = false;
});
builder.Services.AddDistributedMemoryCache();
builder.Services.AddSession();

app.UseRouting();
app.UseSession();
app.UseDoconutResources();
app.Map("/doconut", branch => branch.UseDoconut());

MiddlewarePath هي قيمة تنسيق؛ لا تقوم بإنشاء فرع ASP.NET Core بنفسها. في هذا المثال يقوم المضيف بربط /doconut، لذا يستخدم العميل BasePath: 'doconut'. ResourcesPath يقدم الحزمة المدمجة على /doconut-res، وبالتالي يكون مسار موارد صورة الودجت هو ResPath: 'doconut-res/images'. اكتب كلاهما بدون شرطة مائلة في البداية: المشاهد يضيف واحدة بنفسه، لذا سيتحول '/doconut' إلى //doconut/DocImage.axd، والذي يقرأه المتصفح كطلب إلى مضيف يُدعى doconut، وسيفشل المشاهد مع الرسالة "The viewer handler could not be reached".

إضافة المشاهد إلى صفحة

المشاهد هو النواة المطلوبة للصفحة. سطح العرض الخاص به يستخدم عنصرين div متداخلين:

html
<div id="divDocViewer">
    <div id="div_ctlDoc"></div>
</div>

عامل شريط الأدوات، تثبيت الوحدات، وسطح المشاهد كتركيب صفحة واحد. يقوم البحث والتعليق بإدخال أشرطةهم المدمجة إلى التثبيتات الاختيارية، لكن هذه الوحدات لا تكون مستقلة أبدًا: فهي دائمًا ما تُرفق بالمشاهد على نفس الصفحة. استخدم نفس الترتيب كما في Doconut.TestApp و Doconut.TestApp.Distributed:

html
<nav id="toolbar" aria-label="Document viewer controls">
    <!-- تنقل المشاهد، التكبير، البحث، وأزرار التعليق -->
</nav>

<div id="searchBarMount"></div>
<div id="annBarMount"></div>

<div id="divDocViewer">
    <div id="div_ctlDoc"></div>
</div>

الإشارة إلى موارد المشاهد

في عرض Razor، تقوم خدمة Viewer المُحقنة بإصدار وسوم <link> و <script> الخاصة بالمشاهد بترتيب الاعتماد — الودجت هو مكوّن jQuery، لذا يجب تحميل jQuery قبل سكريبتات المشاهد:

html
@inject Doconut.Viewer Viewer

@Html.Raw(Viewer.ReferenceCss(new CssConfig
{
    IncludeBootstrapCss = true,
    IncludeViewerCss    = true
}))

@Html.Raw(Viewer.ReferenceScripts(new ScriptConfig
{
    IncludeJQuery        = true,
    IncludeBootstrap     = true,
    IncludeViewerScripts = true
}))

للحصول على حزمة المشاهد الكاملة، اطلب موارد المشاهد والوحدة معًا:

html
@Html.Raw(Viewer.ReferenceCss(new CssConfig
{
    IncludeBootstrapCss  = true,
    IncludeViewerCss     = true,
    IncludeSearchCss     = true,
    IncludeAnnotationCss = true
}))

@Html.Raw(Viewer.ReferenceScripts(new ScriptConfig
{
    IncludeJQuery             = true,
    IncludeBootstrap         = true,
    IncludeViewerScripts     = true,
    IncludeSearchScripts     = true,
    IncludeSearchBar         = true,
    IncludeAnnotationScripts = true,
    IncludeAnnotationBar     = true
}))

IncludeViewerCss و IncludeViewerScripts هما العلامتان الأساسيتان الإجباريتان. لا تنشر أبدًا مثالًا لشريط البحث أو التعليق دونهما، أو دون تثبيت المشاهد، أو دون كائن docViewer. تقوم ReferenceCss و ReferenceScripts بحذف موارد الوحدة الاختيارية عندما لا تمنح الرخصة الحالية هذه القدرة؛ يظل المشاهد الأساسي يبدأ.

تهيئة العارض

القطعة الجانبية للعميل هي مكوّن jQuery. هذه مجموعة قليلة من خيارات التهيئة الحقيقية (ليس كودًا تجريديًا):

javascript
let searchBar = null;
let annBar = null;

const objViewer = $('#div_ctlDoc').docViewer({
    showThumbs: true,
    autoLoad:   false,
    pageZoom:   100,
    FitType:    'width',
    BasePath:   'doconut',             // لا يوجد '/' في البداية
    ResPath:    'doconut-res/images',  // لا يوجد '/' في البداية
    onViewerReady: function () {
        // الصفحات مرئية؛ من الآمن إخفاء مؤشر التحميل هنا
    },
    // نقل أحداث دورة حياة التعليقات إلى الشريط المدمج عندما يكون موجودًا.
    onAnnLoaded:    () => annBar?.handleAnnLoaded(),
    onAnnSaved:     () => annBar?.handleAnnSaved(),
    onAnnSaveError: () => annBar?.handleAnnSaveError(),
    onAnnClosed:    () => annBar?.handleAnnClosed(),
    onError: function (message) {
        console.error('Doconut viewer error:', message);
    }
});

خيارات التسمية مختلطة حقًا — showThumbs، autoLoad، و pageZoom هي camelCase، بينما FitType، BasePath، و ResPath هي PascalCase. لا توجد قاعدة ثابتة؛ إذا أخطأت في التسمية سيتم تجاهل الخيار بصمت (يعود القطعة الجانبية إلى الإعداد الافتراضي بدلاً من إلقاء خطأ).

تجميع حزمة العارض الكاملة

كل من تطبيقات المرجع .NET 6 تثبت الأجزاء التالية معًا في صفحة واحدة:

جزء من الحزمةالمتطلباتكيفية الاتصال
موارد العارض، التركيب، و objViewerمطلوبعارض المستند الأساسي
شريط أدوات العارضمطلوب في تركيبة المرجععلامة استضافة؛ الأزرار تستدعي نفس objViewer
شريط البحثاختياري، وحدة مرخصةdoconutSearchBar(...).attach(objViewer)
شريط التعليقاتاختياري، وحدة مرخصةdoconutAnnotationBar(...).attach(objViewer)

على الرغم من أن شريط أدوات العارض الرئيسي هو علامة استضافة، فإنه يُثبت جنبًا إلى جنب مع العارض ولا يجب توثيقه كعنصر منفصل. هذا يحافظ على تخطيطه، تسمياته، أيقوناته، وقواعد التفويض تحت سيطرة تطبيقك بينما كل زر يتحكم في نفس نسخة العارض:

html
<nav id="toolbar" aria-label="Document viewer controls">
    <button type="button" onclick="objViewer.GotoPage(1)">First</button>
    <button type="button" onclick="objViewer.Next(false)">Previous</button>
    <button type="button" onclick="objViewer.Next(true)">Next</button>
    <button type="button" onclick="objViewer.GotoPage(objViewer.TotalPages())">Last</button>
    <button type="button" onclick="objViewer.Zoom(false)">Zoom out</button>
    <button type="button" onclick="objViewer.Zoom(true)">Zoom in</button>
    <button type="button" onclick="objViewer.FitType('width')">Fit width</button>
    <button type="button" onclick="objViewer.FitType('height')">Fit height</button>
    <button type="button" id="openSearch">Search</button>
    <button type="button" id="openAnnotations">Annotations</button>
</nav>

الشريط المرجعي الكامل ينسخ أيضًا wwwroot/js/viewerToolbar.js إلى تطبيق الاستضافة لتوفير أدوات الدوران، المصغرات، الطباعة، ملء الشاشة، التخطيط، ومساعدي حالة الأزرار. حمّل ملف الاستضافة هذا بعد Viewer.ReferenceScripts(...). احتفظ بالمساعد وعلامة <nav id="toolbar"> معًا عند نسخ تنفيذ العرض الكامل.

احتفظ بترتيب تهيئة الحزمة المستخدم في كل من تطبيقات المرجع:

  1. إصدار CSS للعارض والوحدات المرخصة.
  2. عرض شريط أدوات العارض، تركيبات البحث/التعليقات، وتركيب العارض معًا.
  3. إصدار السكريبتات للعارض والوحدات المرخصة.
  4. تحميل viewerToolbar.js لتطبيق الاستضافة.
  5. تهيئة docViewer والاحتفاظ بـ objViewer الناتج.
  6. تهيئة كل شريط بحث أو تعليقات مرخص.
  7. استدعاء attach(objViewer) على كل شريط.
  8. فتح المستند والاحتفاظ بالرمز المميز الخاص به لطلبات العارض والوحدات.

Doconut.TestApp.Distributed يحافظ على هذه التركيبة الدقيقة للواجهة ومساعد شريط أدوات العارض نفسه. قيمة طلب access الإضافية وإعدادات إعادة المحاولة للعرض غير المتزامن تنتمي إلى النقل الموزع؛ لا تغير طريقة تجميع العارض أو شريط الأدوات أو الأشرطة.

الحراس على جانب الخادم مهمة: عندما تكون القدرة الاختيارية غير متوفرة، لا يُصدر السكريبت الخاص بها، وبالتالي لا توجد دالة مكوّن jQuery.

html
<script>
    let currentToken = '';

    const refitViewer = () =>
        requestAnimationFrame(() => objViewer.Refit());

    @if (Viewer.IsSearchEnabled)
    {
        <text>
    searchBar = $('#searchBarMount').doconutSearchBar({
        docId: 'ctlDoc',
        getRequestParams: () => ({ token: currentToken }),
        onLayout: refitViewer
    });
    searchBar.attach(objViewer);
        </text>
    }

    @if (Viewer.IsAnnotationEnabled)
    {
        <text>
    annBar = $('#annBarMount').doconutAnnotationBar({
        docId: 'ctlDoc',
        getRequestParams: () => ({ token: currentToken }),
        onLayout: refitViewer
    });
    annBar.attach(objViewer);
        </text>
    }

    document.getElementById('openSearch').addEventListener('click', () => {
        if (!searchBar) return;
        searchBar.isOpen() ? searchBar.close() : searchBar.open();
    });

    document.getElementById('openAnnotations').addEventListener('click', () => {
        if (!annBar) return;
        annBar.isOpen() ? annBar.close() : annBar.open();
    });
</script>

كل من المكوّنات المدمجة تُنشئ DOM الخاص بشريطها. يحتوي البحث على مجموعات Find، Options، و Results. يحتوي التعليق على أدوات التأليف، عناصر التحكم في النمط، إجراءات الحفظ، وإجراءات التصدير/الصورة الاختيارية. الأشرطة تُظهر الدوال open(), close(), reset(), و isOpen()؛ استدعِ دائمًا attach(objViewer) مرة واحدة بعد إنشائها.

المثال أعلاه يحذف استدعاءات الاستضافة الاختيارية ونقاط النهاية لتصدير/صورة التعليقات لتقليل وقت بدء التشغيل. راجع البحث والتعليقات للإعداد الكامل الخاص بالميزات، أو سمات مخصصة لتنسيق أو استبدال شريط أدوات العارض المملوك للاستضافة.

فتح مستند

الجانب الخادمي هو نقطة نهاية واحدة: خدمة Viewer المُحقنة تفتح المستند وتعيد رمز جلسة.

csharp
app.MapPost("/api/open", async (Viewer viewer) =>
{
    // الرمز غير شفاف — قدمه إلى الودجت، ولا تسجله أو تحتفظ به.
    string token = await viewer.OpenDocumentAsync("wwwroot/files/Sample.pdf");
    return Results.Ok(new { token });
});

العميل يجلب هذا الرمز ويقدمه إلى الودجت باستخدام objViewer.View(token):

javascript
fetch('/api/open', { method: 'POST' })
    .then(resp => resp.json())
    .then(data => {
        currentToken = data.token;
        objViewer.View(currentToken);
    });

إغلاق المستند

استدعِ objViewer.Close() عندما يغادر المستخدم العارض أو يفتح مستندًا بديلاً. في سير العمل المدفوع من الخادم، viewer.CloseDocument(token) يزيل فورًا الجلسة المخزنة مؤقتًا، ويُفرغ محرك العرض، ويحذف علامة الأمان الخاصة به، ويلغي الرمز. انتهاء الصلاحية المتدرج يقوم في النهاية بنفس عملية التنظيف، لكن الإغلاق الصريح يُنصح به للمستندات الكبيرة.

تدفق الطلب المكتمل هو:

text
AddDoconut + middleware
    -> render CSS/scripts and mount div
    -> initialize docViewer
    -> OpenDocumentAsync
    -> return opaque token
    -> objViewer.View(token)
    -> page/search/annotation requests
    -> Close / CloseDocument

عامل الرمز كبيان حامل: لا تسجّله أبداً، ولا تحتفظ به، وقدّمه فقط إلى الودجت. هو يحدد جلسة مستند حية على الخادم ويتوقف عن العمل عندما تنتهي صلاحية تلك الجلسة — أعد فتح المستند للحصول على رمز جديد.

تشغيله

ضع ملف PDF في wwwroot/files/Sample.pdf، شغّل dotnet run، وافتح الصفحة التي تستضيف الودجت. الصفحة الأولى تُعرض في العارض، مع لوحة صور مصغرة على اليسار. إذا لم يحدث ذلك، راجع استكشاف الأخطاء وإصلاحها.

ما ستحصل عليه بدون ترخيص

غياب الترخيص لا يسبب استثناء. العارض يعرض بشكل طبيعي، لكن كل صفحة تحمل علامة مائية للتقييم. راجع إعداد الترخيص لمعرفة كيف يجد Doconut ترخيصًا وما يتغيّر بمجرد العثور عليه.

هل كانت هذه الصفحة مفيدة؟