従来の方法は苦痛だった
共有ドライブ内のすべての契約書に “CONFIDENTIAL” という語と会社ロゴが各ページに入っているかを確認することを任されたコンプライアンス担当者を想像してください。現在のプロセスは次のとおりです。
- ビューアでファイルを開く。
- 各ページをめくりながらフレーズや画像を探す。
- スプレッドシートにメモを取る。
- 数千の PDF、Word ファイル、プレゼンテーションに対して繰り返す。
1 つのウォーターマークの見落としが高額な再レビューを引き起こし、手作業の労力は小規模チームでも 週 8 時間 を簡単に超えてしまいます。さらに、回転したテキスト、分割された単語、画像として保存されたロゴは人間の目に捕らえられにくく、組織を危険にさらします。
より良い方法があります
GroupDocs.Watermark for .NET はすべての推測ステップを排除します。そのフォーマットに依存しないエンジンは 100 種類以上のドキュメントを読み取り、テキスト、画像、さらにはスタイル付きウォーターマークを検出し、クリーンな API を通じてすべての関連メタデータを提供します。以下のチュートリアルでは、数行のコードで手作業のループを自動化・再利用可能なワークフローに置き換える方法を示します。
前提条件
- .NET 6.0 以降。
- GroupDocs.Watermark NuGet パッケージ (
dotnet add package GroupDocs.Watermark)。 - (任意)一時ライセンス – 記事末尾のリンクをご参照ください。
新しい方法: 自動ウォーターマーク監査
以下では 4 つのコア操作を順に解説します。各ブロックはコンソール アプリ、CI ステップ、またはバックグラウンド サービスにそのまま組み込める自己完結型の例です。
Step 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}");
}
}
Key point: 典型的な 50 ページ PDF でも 1 秒未満でループが完了します。
Step 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;
}
フレーズが 1 回以上出現すれば true を返し、即座に PASS/FAIL フラグを出します。
Step 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;
}
低解像度のコピーでも認識されます。
Step 4 – 完全なコンプライアンス レポートを実行
実務では複数の要件が組み合わさります。以下のブロックはテキストの有無、フォント、サイズ、太字スタイルという 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++;
5 番目のルールはページカバレッジを検証します — ウォーターマークはすべてのページに存在しなければなりません。最後に、判定結果を集計します。
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))
{
// Step 1‑4 のメソッドを再利用
ScanAll(pdf);
VerifyText(pdf, "CONFIDENTIAL – CLIENT XYZ");
VerifyLogo(pdf, @"C:\Logos\firm-seal.png");
RunReport(pdf);
}
このスクリプトは無人で実行され、毎朝サマリーがメールで送信されます。
GroupDocs.Watermark でさらにできることは?
監査以外にも、サンプル プロジェクトはプログラムで 置換 と 削除 を行う方法を示しています。以下のスクリーンショットは実際の PDF で両操作を実演したものです。
同じ 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