The Old Way Was Painful
Представьте сотрудника по соблюдению нормативов, которому поручено убедиться, что каждый контракт в общей папке содержит слово “CONFIDENTIAL” и логотип компании на каждой странице. Текущий процесс выглядит так:
- Открыть файл в просмотрщике.
- Перелистывать каждую страницу в поисках фразы или изображения.
- Делать заметки в таблице.
- Повторять для тысяч 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
Сначала нам нужен полный инвентарь. Метод 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}");
}
}
Key point: Цикл выполняется менее чем за секунду для типичного PDF‑файла в 50 страниц.
Step 2 – Verify Required Text Watermark
Политика соответствия часто требует конкретную фразу (например, “CONFIDENTIAL”). TextSearchCriteria с включённым SkipUnreadableCharacters автоматически обрабатывает разорванный или вращённый текст.
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.
Step 3 – Verify Company Logo
Логотипы сохраняются как растровые изображения, и их внешний вид может слегка отличаться из‑за сжатия. ImageDctHashSearchCriteria создаёт перцептивный хеш эталонного логотипа и сравнивает его с настраиваемой толерантностью.
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
Реальные политики объединяют несколько требований. Первый блок проверяет четыре правила форматирования — наличие текста, шрифт, размер и полужирный стиль — каждое из которых комбинирует 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 или к прямой передаче в систему тикетов.
Side‑by‑Side: Before vs. After
| Manual Review | Automated Audit | |
|---|---|---|
| Time | Hours per batch | Seconds per file |
| Accuracy | Human error prone | Deterministic API |
| Scalability | Limited to a few documents | Handles thousands |
| Code required | None (but labor intensive) | ~30 lines of C# |
| Output | Visual inspection only | Structured PASS/FAIL report |
Контраст очевиден: то, что раньше занимало целый рабочий день, теперь работает как фоновая задача.
Real‑World Example: Legal Contract Library
Юридическая фирма хранит 15 000 контрактов в общей папке. Их политика требует фразу “CONFIDENTIAL – CLIENT XYZ” и печать фирмы на каждой странице. Интегрировав приведённые выше фрагменты в ночной скрипт PowerShell, фирма достигла:
- 100 % обнаружения отсутствующих знаков (ранее 8 % ускользали).
- Ноль ручных часов на аудит.
- Аудиторский журнал, сохранённый во внутренний список SharePoint для будущих регуляторных проверок.
// 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?
Помимо аудита, пример проекта показывает, как заменять и удалять водяные знаки программно. Скриншоты ниже демонстрируют обе операции на реальном PDF:
Другие сценарии, которые можно построить с тем же 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.
Каждый сценарий использует один и тот же базовый API — просто меняйте критерии поиска или тип водяного знака.
Conclusion
То, что раньше требовало от команды листать страницы, делать заметки и рисковать пропустить знаки, теперь превращается в несколько секунд кода, генерирующего проверяемый отчёт PASS/FAIL. С GroupDocs.Watermark for .NET вы получаете:
- Полную видимость каждого водяного знака.
- Надёжное обнаружение текста, стилизованного текста и логотипов.
- Автоматическую генерацию отчётов о соответствии.
- Возможность программно обновлять или удалять водяные знаки.
Попробуйте и превратите процесс контроля водяных знаков из головной боли в повторяемый сервис.
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