التعليقات التوضيحية
إضافة دعم التعليقات التوضيحية إلى العارض
تعمل التعليقات التوضيحية في Doconut في اتجاهين: يرسمها المستخدمون في عنصر المتصفح وتحتفظ بها الخادم لكل صفحة، أو يبنيها الكود الخاص بك ويحمّلها في جلسة مفتوحة. في كلتا الحالتين تُعرض على الصفحات ويمكن دمجها في تصديرات PDF أو PNG.
يتم التحكم في دعم التعليقات التوضيحية بقدرة الترخيص Annotation (تُمنح تلقائيًا بموجب ترخيص مؤقت نشط).
تمكين واجهة المستخدم للتعليقات التوضيحية
التعليق هو وحدة في العارض، وليس شريط أدوات مستقل. يجب أن تتضمن الصفحة موارد العارض، شريط أدوات العارض، نقطة تثبيت العارض، و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 // شريط التعليقات التوضيحية المدمج
});
ViewBag.IsAnnotationEnabled = viewer.IsAnnotationEnabled;احتفظ بتكوين العارض الكامل في العلامات:
<nav id="toolbar" aria-label="Document viewer controls">
<!-- عناصر تحكم العارض، بما في ذلك الزر الذي يفتح التعليقات التوضيحية -->
</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(). واجهته العامة:
| الطريقة | الغرض |
|---|---|
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>();
// مرتبط بأبعاد صفحات الجلسة المفتوحة
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));
// تحميل إلى الجلسة - المشاهد يجلبها، والعارض يدمجها في
// تصديرات صورة/PDF.
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 نسبي بالنسبة لمضيف الطلب الحالي عندما تُضاف التعليقة — انظر ملاحظة التصدير أدناه |
واجهة برمجة تطبيقات 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 لجميع الصفحات مع التعليقات التوضيحية المرسومة عليها
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");
}
// أو ملف ZIP لصور PNG لكل صفحة
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" |
عند قيام الخادم بجلب صورة لإدماجها، يرفض العناوين التي تعيد إلى الشبكات الخاصة: حلقة رجوع (localhost, 127.0.0.1), 0.0.0.0, رابط‑محلي (169.254.x.x), النطاقات الخاصة 10.x, 172.16–31.x, و 192.168.x, NAT من نوع الناقل (100.64.0.0/10), وعناوين IPv6 رابط‑محلية وفريدة‑محلية. هذا يحمي الخادم من أن يُستَخدم للوصول إلى الأنظمة الداخلية عبر عنوان صورة مُصمم، ولا يوجد مفتاح لإيقاف هذا السلوك.
عنوان URL نسبي للصورة يُحلّ ضد مضيف الطلب الحالي، لذا على localhost، أو على خادم إنترانت، أو على أي موقع يُوصل عبر عنوان خاص، تُظهر الصورة في العارض وتحتوي عملية التصدير على العنصر النائب بدلاً من ذلك. هذا هو الحماية تعمل، وليس فشلًا في العرض. إذا كان يجب أن تحتوي الصادرات على الصورة، قدّمها من مضيف له عنوان عام واستخدم عنوان URL مطلق. عنوان URL للصورة لا يمكن حله نهائيًا يجعل ذلك التوضيح يختفي دون خطأ — تحقق من البكسلات المُرسَمة أو سجل الشبكة، وليس من رمز الحالة.
سير عمل الحفظ
- افتح المستند واحصل على رمزه.
- حمّل XML أو البيانات المشفرة المخزنة مسبقًا إلى ذلك الرمز.
- اسمح للأداة بقراءة وتحرير توضيحات الجلسة.
- استرجع XML باستخدام
GetAnnotationXML(token)عندما يقرر تطبيقك حفظ البيانات. - صدّر PDF أو PNG عندما يكون مطلوبًا ملف مسطح.
- أغلق جلسة المستند.
لا تستخدم رمز العارض كمعرّف دائم للتوضيح — فهو ينتهي مع الجلسة، وإعادة تدوير مجموعة التطبيقات تُنهي كل الجلسات على خادم واحد. احفظ بيانات التوضيح مقابل مستندك ومعرفات الإصدار الخاصة بك.
ملاحظات الأمان والعرض
- طلبات التوضيح تستخدم نفس أمان الجلسة والرمز مثل طلبات الصفحة.
- تحقق من صحة أي عنوان URL لصورة مقدم من المستخدم وتحكم فيه.
- الأحمال الكبيرة للرسومات الحرة والتصديرات عالية الدقة تستهلك المزيد من الذاكرة؛ اختبر مستندات واقعية وقيم التكبير.
استكشاف الأخطاء وإصلاحها
| العَرَض | التحقق |
|---|---|
| شريط التوضيح مفقود | خاصية Annotation وأعلام CSS/سكريبت الأربعة للتوضيح |
| الحفظ يُظهر خطأ | انتهاء صلاحية الرمز أو الجلسة، ومسار BasePath الخاص بالأداة |
ctlDoc_Created (أو دالة حدث أخرى) لا تُنفّذ أبدًا | اسم الدالة: معرف العارض + _ + الحدث، كدالة عامة |
| توضيحات C# لا تظهر | أرقام الصفحات تبدأ من 1، وتم تحميل البيانات إلى الرمز النشط |
| توضيح صورة يظهر على الشاشة لكنه غير موجود في التصدير | مضيف الصورة لديه عنوان خاص أو حلقة رجوع — راجع أعلاه |
| المستند المفتوح مرة أخرى لا يحتوي على توضيحات | احفظ XML أو البيانات خارج جلسة العارض، ثم حمّلها إلى الرمز الجديد |
هل كانت هذه الصفحة مفيدة؟