过去的方式很痛苦

想象一下,一名合规官员的任务是验证共享驱动器中的每份合同是否在每页都带有 “CONFIDENTIAL” 字样以及公司徽标。当前的流程如下:

  1. 在查看器中打开文件。
  2. 翻阅每一页寻找该短语或图像。
  3. 在电子表格中记录笔记。
  4. 对成千上万的 PDF、Word 文件和演示文稿重复上述步骤。

一次漏掉的水印可能会触发昂贵的复审,而人工工作量轻易就会超过 每周 8 小时,即使是小团队也是如此。此外,旋转的文字、被拆分的词语或以图像形式保存的徽标常常逃过肉眼,导致组织面临风险。

有更好的办法

GroupDocs.Watermark for .NET 消除了所有猜测步骤。它的格式无关引擎能够读取 100 多种文档类型,定位文本、图像甚至样式化的水印,并通过简洁的 API 暴露所有相关元数据。下面的教程展示了几个简短代码片段如何用自动化、可重复的工作流取代手动循环。

前置条件

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

关键点:对于典型的 50 页 PDF,循环在一秒钟内完成。

步骤 2 – 验证必需的文本水印

合规政策通常要求出现特定短语(例如 “CONFIDENTIAL”)。TextSearchCriteriaSkipUnreadableCharacters 能自动处理被拆分或旋转的文字。

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 – 生成完整的合规报告

实际政策往往结合多项要求。下面的代码块检查四条格式规则——文本存在、字体、大小以及粗体样式——每条规则都通过 .And()TextSearchCriteriaTextFormattingSearchCriteria 组合:

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. 自动

手动审查 自动审计
时间 每批数小时 每个文件秒级
准确性 人为错误频发 确定性 API
可扩展性 只能处理少量文档 能处理成千上万
所需代码 无(但劳动密集) 约 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 上的两种操作:

Text replacement — the old watermark is updated with new text, font, and color

Targeted removal — only watermarks matching both text and formatting criteria are deleted

您可以基于同一 API 构建的其他场景:

  • 添加不可见追踪水印,嵌入唯一 ID 用于泄漏追踪。
  • 批量替换过时徽标,覆盖整个档案库。
  • 生成合规证书(PDF 就绪),在审计通过后自动生成。
  • 与 Azure Functions 或 AWS Lambda 集成,实现无服务器处理。

每个场景都使用相同的核心 API——只需更换搜索条件或水印类型即可。


结论

过去需要团队逐页翻阅、记录笔记并冒漏检风险的工作,如今只需 几秒钟的代码 就能生成可审计的 PASS/FAIL 报告。使用 GroupDocs.Watermark for .NET,您将获得:

  • 对每个水印的完整可视化。
  • 对文本、样式化文本和徽标的可靠检测。
  • 自动化的合规报告。
  • 通过代码对水印进行更新或删除的能力。

试一试吧,让您的水印合规流程从头疼的手工操作转变为可重复的服务。


后续步骤

  • 试用免费 API 试用版 – 在此获取临时许可证:Temp License
  • 阅读完整文档,了解高级选项:Docs
  • 浏览 .NET API 参考,获取所有类和方法的细节:API Reference
  • 克隆 GitHub 示例项目,查看完整的控制台应用实现:GitHub Samples
  • 在社区论坛提问 或分享您的使用案例:Forum