الطريقة القديمة كانت مؤلمة
تخيل موظف امتثال مكلف بالتحقق من أن كل عقد في محرك أقراص مشترك يحتوي على كلمة “CONFIDENTIAL” وشعار الشركة على كل صفحة. العملية الحالية تبدو هكذا:
- فتح ملف في عارض.
- تصفح كل صفحة بحثًا عن العبارة أو الصورة.
- تدوين الملاحظات في جدول بيانات.
- تكرار العملية لآلاف ملفات PDF وWord والعروض التقديمية.
يمكن أن يؤدي فقدان علامة مائية واحدة إلى مراجعة مكلفة، والجهد اليدوي يتجاوز بسهولة 8 ساعات في الأسبوع لفريق صغير. علاوة على ذلك، النص المتدوّر أو الكلمات المقسمة أو الشعارات المحفوظة كصور غالبًا ما تتفلت من عين الإنسان، مما يترك المنظمة معرضة للخطر.
هناك طريقة أفضل
GroupDocs.Watermark for .NET يزيل كل خطوة تخمين. محركه غير معتمد على الصيغة يمكنه قراءة أكثر من 100 نوع من المستندات، وتحديد النصوص والصور وحتى العلامات المائية المنسقة، ويكشف جميع البيانات الوصفية ذات الصلة عبر واجهة برمجة تطبيقات نظيفة. يوضح البرنامج التعليمي التالي كيف تستبدل بضع مقتطفات مختصرة الحلقة اليدوية بسير عمل آلي وقابل للتكرار.
المتطلبات المسبقة
- .NET 6.0 أو أحدث.
- حزمة GroupDocs.Watermark من NuGet (
dotnet add package GroupDocs.Watermark). - (اختياري) ترخيص مؤقت – انظر الرابط في نهاية هذه المقالة.
الطريقة الجديدة: تدقيق العلامات المائية الآلي
أدناه نستعرض أربع عمليات أساسية. كل كتلة هي مثال مستقل يمكنك وضعه في تطبيق كونسول، خطوة CI، أو خدمة خلفية.
الخطوة 1 – فحص كل علامة مائية
أولاً نحتاج إلى جرد كامل. تُعيد طريقة Search() مجموعة حيث يحتوي كل عنصر على النص (أو الصورة)، الموقع، الدوران، رقم الصفحة وحجم الصورة الخام.
using (var wk = new Watermarker(filePath))
{
var all = wk.Search();
Console.WriteLine($"Found {all.Count} watermark(s) in " +
$"'{Path.GetFileName(filePath)}':");
int i = 0;
foreach (var wm in all)
{
Console.WriteLine($" #{++i}: {(wm.Text ?? "[image]")} ");
Console.WriteLine($" Page {wm.PageNumber}, " +
$"Pos X={wm.X}, Y={wm.Y}, Rot={wm.RotateAngle}°");
Console.WriteLine($" Size {wm.Width}×{wm.Height}");
if (wm.ImageData != null)
Console.WriteLine($" Image bytes {wm.ImageData.Length}");
}
}
نقطة رئيسية: الحلقة تُنفّذ في أقل من ثانية لملف PDF نموذجي مكوّن من 50 صفحة.
الخطوة 2 – التحقق من علامة مائية نصية مطلوبة
غالبًا ما تتطلب سياسات الامتثال عبارة محددة (مثال: “CONFIDENTIAL”). TextSearchCriteria مع SkipUnreadableCharacters يتعامل مع النص المقسّم أو المتدوّر تلقائيًا.
using (var wk = new Watermarker(filePath))
{
var crit = new TextSearchCriteria(expectedPhrase);
crit.SkipUnreadableCharacters = true; // تجاهل عيوب OCR
var hits = wk.Search(crit);
bool ok = hits.Count > 0;
Console.WriteLine($" [{(ok ? "PASS" : "FAIL")}] " +
$"'{expectedPhrase}' found {hits.Count} time(s)");
return ok;
}
الطريقة تُعيد true عندما تظهر العبارة مرة واحدة على الأقل، مما يمنحك علامة PASS/FAIL فورية.
الخطوة 3 – التحقق من شعار الشركة
تُحفظ الشعارات كصور نقطية، وقد يختلف مظهرها قليلًا بسبب الضغط. ImageDctHashSearchCriteria يُنشئ تجزئة إدراكية للشعار المرجعي ويطابقها مع تسامح قابل للتكوين.
using (var wk = new Watermarker(filePath))
{
var crit = new ImageDctHashSearchCriteria(logoPath);
crit.MaxDifference = 0.9; // تحمل تحجيمًا متوسطًا / تغير لون
var matches = wk.Search(crit);
bool ok = matches.Count > 0;
Console.WriteLine($" [{(ok ? "PASS" : "FAIL")}] " +
$"logo instances: {matches.Count}");
return ok;
}
حتى نسخة منخفضة الدقة من الشعار سيتم التعرف عليها.
الخطوة 4 – تشغيل تقرير امتثال كامل
تجمع السياسات الواقعية بين عدة متطلبات. يتحقق الكتلة الأولى من أربع قواعد تنسيق — وجود النص، الخط، الحجم، والنمط الغامق — كل منها يجمع بين TextSearchCriteria وTextFormattingSearchCriteria عبر .And():
using (var wk = new Watermarker(filePath))
{
int passed = 0, failed = 0;
var txtCrit = new TextSearchCriteria(expectedPhrase);
bool hasText = wk.Search(txtCrit).Count > 0;
Console.WriteLine($" [{(hasText ? "PASS" : "FAIL")}] Text present");
if (hasText) passed++; else failed++;
var fontCrit = new TextFormattingSearchCriteria { FontName = expFont };
bool hasFont = wk.Search(txtCrit.And(fontCrit)).Count > 0;
Console.WriteLine($" [{(hasFont ? "PASS" : "FAIL")}] Font {expFont}");
if (hasFont) passed++; else failed++;
var sizeCrit = new TextFormattingSearchCriteria { MinFontSize = minSize };
bool hasSize = wk.Search(txtCrit.And(sizeCrit)).Count > 0;
Console.WriteLine($" [{(hasSize ? "PASS" : "FAIL")}] Size >= {minSize}");
if (hasSize) passed++; else failed++;
var boldCrit = new TextFormattingSearchCriteria { FontBold = true };
bool hasBold = wk.Search(txtCrit.And(boldCrit)).Count > 0;
Console.WriteLine($" [{(hasBold ? "PASS" : "FAIL")}] Bold formatting");
if (hasBold) passed++; else failed++;
القاعدة الخامسة تتحقق من تغطية الصفحات — يجب أن تظهر العلامة المائية على كل صفحة. أخيرًا، يجمع الحكم جميع النتائج:
var perPage = wk.Search(txtCrit);
var pages = new HashSet<int>();
foreach (var wm in perPage)
if (wm.PageNumber.HasValue) pages.Add(wm.PageNumber.Value);
var allPages = wk.Search();
int max = 0;
foreach (var wm in allPages)
max = Math.Max(max, wm.PageNumber ?? 0);
bool full = max > 0 && pages.Count == max;
Console.WriteLine($" [{(full ? "PASS" : "FAIL")}] " +
$"Pages covered {pages.Count}/{max}");
if (full) passed++; else failed++;
string verdict = failed == 0 ? "COMPLIANT" : "NON-COMPLIANT";
Console.WriteLine($"\nResult: {verdict} " +
$"({passed} passed, {failed} failed)");
}
التقرير جاهز للتصدير كـ JSON أو CSV أو إرساله مباشرة إلى نظام التذاكر.
مقارنة جنبًا إلى جنب: قبل vs. بعد
| مراجعة يدوية | تدقيق آلي | |
|---|---|---|
| الوقت | ساعات لكل دفعة | ثوانٍ لكل ملف |
| الدقة | عرضة لأخطاء بشرية | واجهة برمجة تطبيقات حتمية |
| القابلية للتوسع | محدودة بعدد قليل من المستندات | يتعامل مع آلاف المستندات |
| الكود المطلوب | لا شيء (لكن العمل شاق) | ~30 سطرًا من C# |
| الناتج | فحص بصري فقط | تقرير PASS/FAIL منظم |
التباين واضح: ما كان يستغرق يوم عمل كامل الآن يُنفّذ كوظيفة خلفية.
مثال واقعي: مكتبة العقود القانونية
تخزن شركة محاماة 15 000 عقد في مجلد مشترك. تتطلب سياستهم العبارة “CONFIDENTIAL – CLIENT XYZ” وختم الشركة على كل صفحة. من خلال دمج المقتطفات أعلاه في سكريبت PowerShell يُنفّذ ليلاً، حققت الشركة:
- اكتشاف 100 % للعلامات المائية المفقودة (كان 8 % يمرون سابقًا).
- صفر ساعات يدوية تُقضى على التدقيق.
- سجل تدقيق محفوظ في قائمة SharePoint داخلية للمراجعات التنظيمية المستقبلية.
// مثال على نقطة دخول الوظيفة الليلية
var folder = @"\\fileserver\Contracts";
foreach (var pdf in Directory.GetFiles(folder, "*.pdf", SearchOption.AllDirectories))
{
// إعادة استخدام الطرق من الخطوات 1‑4
ScanAll(pdf);
VerifyText(pdf, "CONFIDENTIAL – CLIENT XYZ");
VerifyLogo(pdf, @"C:\Logos\firm-seal.png");
RunReport(pdf);
}
يعمل السكريبت دون تدخل ويُرسل ملخصًا بالبريد كل صباح.
ماذا يمكنك أن تفعل أيضًا مع GroupDocs.Watermark؟
إلى جانب التدقيق، يُظهر مشروع العينة كيف يمكن استبدال وإزالة العلامات المائية برمجيًا. توضح اللقطات أدناه كلا العمليتين على ملف PDF حقيقي:
سيناريوهات أخرى يمكنك بناؤها باستخدام نفس الـ API:
- إضافة علامات مائية تتبع غير مرئية تُدمج معرفًا فريدًا لتتبع التسريبات.
- استبدال جماعي للشعارات القديمة عبر أرشيف كامل.
- إنشاء شهادات امتثال جاهزة للـ PDF بعد تدقيق ناجح.
- التكامل مع Azure Functions أو AWS Lambda للمعالجة بدون خادم.
كل سيناريو يستخدم نفس الـ API الأساسي – فقط غيّر معايير البحث أو نوع العلامة المائية.
الخلاصة
ما كان يتطلب فريقًا لتقليب الصفحات، كتابة ملاحظات، ومخاطرة بفقدان العلامات أصبح الآن بضع ثوانٍ من الشيفرة تُنتج تقرير PASS/FAIL قابل للتدقيق. مع GroupDocs.Watermark for .NET تحصل على:
- رؤية كاملة لكل علامة مائية.
- اكتشاف موثوق للنص، النص المنسق، والشعارات.
- تقارير امتثال تلقائية.
- القدرة على تحديث أو إزالة العلامات المائية برمجيًا.
جرّبه وحوّل عملية امتثال العلامات المائية من صداع إلى خدمة قابلة للتكرار.
الخطوات التالية
- جرّب النسخة التجريبية المجانية – احصل على ترخيص مؤقت هنا: Temp License
- اقرأ الوثائق الكاملة للخيارات المتقدمة: Docs
- استكشف مرجع API لـ .NET لجميع الفئات والطرق: API Reference
- استنساخ مشروع العينة على GitHub لرؤية تطبيق كونسول كامل: GitHub Samples
- اطرح أسئلة أو شارك حالتك على منتدى المجتمع: Forum