The Old Way Was Painful

Imagine a compliance officer tasked with verifying that every contract in a shared drive carries the word “CONFIDENTIAL” and the company logo on each page. The current process looks like this:

  1. Open a file in a viewer.
  2. Flip through each page looking for the phrase or image.
  3. Jot notes in a spreadsheet.
  4. Repeat for thousands of PDFs, Word files, and presentations.

یک علامت آب‌رنگی که از دست برود می‌تواند بازبینی پرهزینه‌ای را به دنبال داشته باشد و تلاش دستی به راحتی ۸ ساعت در هفته برای یک تیم کوچک را می‌گذارد. علاوه بر این، متن‌های چرخیده، کلمات شکسته یا لوگوهای ذخیره‌شده به‌صورت تصویر اغلب از چشم انسان فرار می‌کنند و سازمان را در معرض خطر می‌گذارند.

There’s a Better Way

GroupDocs.Watermark for .NET تمام مراحل حدسی را حذف می‌کند. موتور بدون وابستگی به فرمت آن می‌تواند بیش از ۱۰۰ نوع سند را بخواند، متن، تصویر و حتی آب‌رنگ‌های قالب‌بندی‌شده را پیدا کند و تمام متادیتای مرتبط را از طریق یک API تمیز در دسترس بگذارد. آموزش زیر نشان می‌دهد چگونه چند قطعه کد کوتاه، حلقه دستی را با یک جریان کاری خودکار و قابل تکرار جایگزین می‌کند.

Prerequisites

  • .NET 6.0 یا بالاتر.
  • بسته NuGet GroupDocs.Watermark (dotnet add package GroupDocs.Watermark).
  • (اختیاری) یک لایسنس موقت – لینک در انتهای این مقاله را ببینید.

The New Way: Automated Watermark Audit

Below we walk through four core operations. Each block is a self‑contained example that you can drop into a console app, a CI step, or a background service.

Step 1 – Scan Every Watermark

First we need a full inventory. The Search() method returns a collection where each entry contains text (or image), location, rotation, page number and raw image size.

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 معمولی ۵۰ صفحه‌ای اجرا می‌شود.

Step 2 – Verify Required Text Watermark

Compliance policies often demand a specific phrase (e.g., “CONFIDENTIAL”). The TextSearchCriteria with SkipUnreadableCharacters handles split or rotated text automatically.

using (var wk = new Watermarker(filePath))
{
    var crit = new TextSearchCriteria(expectedPhrase);
    crit.SkipUnreadableCharacters = true;   // ignore OCR artefacts
    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 فوری را به شما می‌دهد.

Logos survive as raster images, and their appearance may vary slightly due to compression. ImageDctHashSearchCriteria creates a perceptual hash of the reference logo and matches it with a configurable tolerance.

using (var wk = new Watermarker(filePath))
{
    var crit = new ImageDctHashSearchCriteria(logoPath);
    crit.MaxDifference = 0.9;   // tolerate moderate scaling / colour shift
    var matches = wk.Search(crit);
    bool ok = matches.Count > 0;
    Console.WriteLine($"  [{(ok ? "PASS" : "FAIL")}] " +
        $"logo instances: {matches.Count}");
    return ok;
}

حتی یک نسخه کم‌رزولوشن از لوگو نیز شناسایی می‌شود.

Step 4 – Run a Full Compliance Report

Real-world policies combine several requirements. The first block checks four formatting rules — text presence, font, size, and bold style — each combining TextSearchCriteria with TextFormattingSearchCriteria via .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++;

قانون پنجم پوشش صفحات را بررسی می‌کند — آب‌رنگ باید در هر صفحه ظاهر شود. در نهایت، verdict تمام نتایج را جمع‌بندی می‌کند:

    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 صادر شود یا مستقیماً به یک سیستم تیکت‌گذاری ارسال گردد.


Side‑by‑Side: Before vs. After

بررسی دستی حسابرسی خودکار
زمان ساعت‌ها برای هر دسته ثانیه‌ها برای هر فایل
دقت مستعد خطای انسانی API تعیین‌پذیر
قابلیت مقیاس‌پذیری محدود به چند سند توانایی پردازش هزاران سند
کد مورد نیاز هیچ (اما کار دستی زیاد) حدود ۳۰ خط C#
خروجی فقط بازرسی بصری گزارش ساختار یافته PASS/FAIL

تفاوت چشمگیر است: کاری که قبلاً یک روز کاری کامل می‌برد، اکنون به‌عنوان یک کار پس‌زمینه اجرا می‌شود.


A law firm stores 15 000 contracts in a shared folder. Their policy requires the phrase “CONFIDENTIAL – CLIENT XYZ” and the firm’s seal on every page. By integrating the snippets above into a nightly PowerShell script, the firm achieved:

  • 100 % detection of missing marks (previously 8 % slipped through).
  • Zero manual hours spent on the audit.
  • An audit trail saved to an internal SharePoint list for future regulatory reviews.
// Example of the nightly job entry point
var folder = @"\\fileserver\Contracts";
foreach (var pdf in Directory.GetFiles(folder, "*.pdf", SearchOption.AllDirectories))
{
    // reuse the methods from steps 1‑4
    ScanAll(pdf);
    VerifyText(pdf, "CONFIDENTIAL – CLIENT XYZ");
    VerifyLogo(pdf, @"C:\Logos\firm-seal.png");
    RunReport(pdf);
}

اسکریپت بدون نظارت اجرا می‌شود و هر صبح خلاصه‌ای ایمیل می‌کند.


What Else Can You Do with GroupDocs.Watermark?

Beyond auditing, the sample project shows how to replace and remove watermarks programmatically. The screenshots below demonstrate both operations on a real PDF:

جایگزینی متن — آب‌رنگ قدیمی با متن، قلم و رنگ جدید به‌روزرسانی می‌شود

حذف هدفمند — فقط آب‌رنگ‌هایی که هم متن و هم قالب‌بندی معیارها را دارند حذف می‌شوند

Other scenarios you can build with the same API:

  • Add invisible tracking watermarks that embed a unique ID for leak tracing.
  • Bulk replace outdated logos across an entire archive.
  • Generate PDF‑ready compliance certificates after a successful audit.
  • Integrate with Azure Functions or AWS Lambda for serverless processing.

Each scenario uses the same core API – just swap the search criteria or the watermark type.


Conclusion

What once required a team to flip through pages, write notes, and risk missed marks is now a few seconds of code that produces an auditable PASS/FAIL report. With GroupDocs.Watermark for .NET you gain:

  • Full visibility of every watermark.
  • Reliable detection of text, styled text and logos.
  • Automatic compliance reporting.
  • The ability to update or remove watermarks programmatically.

Give it a try and transform your watermark compliance process from a headache into a repeatable service.


Next Steps

  • Try the free API trial – get a temporary license here: Temp License
  • Read the full documentation for advanced options: Docs
  • Explore the .NET API reference for all classes and methods: API Reference
  • Clone the sample project on GitHub to see a complete console app: GitHub Samples
  • Ask questions or share your use case on the community forum: Forum