The Old Way Was Painful

Уявіть, що співробітник з комплаєнсу має перевіряти, чи кожен контракт у спільному диску містить слово “CONFIDENTIAL” і логотип компанії на кожній сторінці. Поточний процес виглядає так:

  1. Відкрити файл у переглядачі.
  2. Перегортати кожну сторінку в пошуках фрази або зображення.
  3. Записувати нотатки у електронну таблицю.
  4. Повторювати для тисяч PDF‑файлів, Word‑документів і презентацій.

Одне пропущене водяне позначення може викликати дорогий перегляд, а ручна праця легко перевищує 8 годин на тиждень для невеликої команди. Крім того, обернений текст, розділені слова або логотипи, збережені як зображення, часто залишаються непоміченими, залишаючи організацію вразливою.

There’s a Better Way

GroupDocs.Watermark for .NET усуває всі кроки, що базуються на здогадках. Його форматно‑агностичний движок може читати понад 100 типів документів, знаходити текстові, графічні та навіть стилізовані водяні позначки, а також надавати всю відповідну метадану через чистий API. Наведений нижче підручник показує, як кілька стислих фрагментів коду замінюють ручний цикл автоматизованим, повторюваним робочим процесом.

Prerequisites

  • .NET 6.0 або новіша версія.
  • GroupDocs.Watermark пакет NuGet (dotnet add package GroupDocs.Watermark).
  • (Опціонально) тимчасова ліцензія – дивіться посилання в кінці статті.

The New Way: Automated Watermark Audit

Нижче ми розглядаємо чотири основні операції. Кожен блок – це самодостатній приклад, який можна вставити у консольний застосунок, крок CI або фонову службу.

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

Key point: Цикл виконується менш ніж за секунду для типового PDF у 50 сторінок.

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++;

The fifth rule verifies page coverage — the watermark must appear on every page. Finally, the verdict aggregates all results:

    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
Масштабованість Обмежена кількома документами Обробляє тисячі
Необхідний код Немає (але трудомістко) ~30 рядків 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:

Заміна тексту — старе водяне позначення оновлено новим текстом, шрифтом і кольором

Цілеспрямоване видалення — видаляються лише водяні позначки, що відповідають і тексту, і форматуванню

Інші сценарії, які можна реалізувати за допомогою того ж API:

  • Додати невидимі трекінгові водяні позначки, що вбудовують унікальний ID для відстеження витоків.
  • Масово замінити застарілі логотипи у всьому архіві.
  • Генерувати сертифікати відповідності у форматі PDF після успішного аудиту.
  • Інтегрувати з Azure Functions або AWS Lambda для безсерверної обробки.

Кожен сценарій використовує той самий базовий API – лише змінюються критерії пошуку або тип водяної позначки.


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.

Спробуйте і перетворіть процес комплаєнсу водяних позначок з головного болю на повторювану службу.


Next Steps

  • Спробуйте безкоштовну пробну версію API – отримайте тимчасову ліцензію тут: Тимчасова ліцензія
  • Прочитайте повну документацію для розширених параметрів: Docs
  • Ознайомтеся з .NET API reference для всіх класів і методів: API Reference
  • Клонувати приклад проекту на GitHub, щоб побачити повний консольний застосунок: GitHub Samples
  • Задавайте питання або діліться своїм випадком використання на форумі спільноти: Forum