Cách Cũ Đầy Khó Khăn
Hãy tưởng tượng một nhân viên tuân thủ được giao nhiệm vụ xác minh rằng mọi hợp đồng trong một ổ đĩa chung đều có từ “CONFIDENTIAL” và logo công ty trên mỗi trang. Quy trình hiện tại trông như sau:
- Mở tệp trong một trình xem.
- Lướt qua từng trang để tìm cụm từ hoặc hình ảnh.
- Ghi chú vào bảng tính.
- Lặp lại cho hàng ngàn tệp PDF, Word và bản trình chiếu.
Một dấu nước bị bỏ lỡ duy nhất cũng có thể gây ra một cuộc rà soát tốn kém, và công sức thủ công dễ dàng vượt quá 8 giờ mỗi tuần cho một đội nhỏ. Hơn nữa, văn bản xoay, từ bị tách ra hoặc logo được lưu dưới dạng hình ảnh thường thoát khỏi tầm mắt con người, để lại tổ chức trong tình trạng không được bảo vệ.
Có Cách Tốt Hơn
GroupDocs.Watermark for .NET loại bỏ mọi bước đoán mò. Động cơ không phụ thuộc vào định dạng của nó có thể đọc hơn 100 loại tài liệu, xác định văn bản, hình ảnh và thậm chí các dấu nước có kiểu dáng, và cung cấp tất cả siêu dữ liệu liên quan qua một API sạch sẽ. Hướng dẫn sau đây cho thấy cách một vài đoạn mã ngắn gọn thay thế vòng lặp thủ công bằng một quy trình làm việc tự động, có thể lặp lại.
Yêu Cầu Trước
- .NET 6.0 trở lên.
- Gói NuGet GroupDocs.Watermark (
dotnet add package GroupDocs.Watermark). - (Tùy chọn) giấy phép tạm thời – xem liên kết ở cuối bài viết.
Cách Mới: Kiểm Tra Dấu Nước Tự Động
Dưới đây chúng tôi sẽ đi qua bốn thao tác cốt lõi. Mỗi khối là một ví dụ độc lập mà bạn có thể chèn vào một ứng dụng console, một bước CI, hoặc một dịch vụ nền.
Bước 1 – Quét Tất Cả Dấu Nước
Đầu tiên chúng ta cần một danh mục đầy đủ. Phương thức Search() trả về một tập hợp trong đó mỗi mục chứa văn bản (hoặc hình ảnh), vị trí, góc quay, số trang và kích thước ảnh thô.
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}");
}
}
Điểm chính: Vòng lặp chạy dưới một giây cho một PDF khoảng 50 trang.
Bước 2 – Xác Minh Dấu Nước Văn Bản Yêu Cầu
Chính sách tuân thủ thường yêu cầu một cụm từ cụ thể (ví dụ, “CONFIDENTIAL”). TextSearchCriteria với SkipUnreadableCharacters tự động xử lý văn bản bị tách ra hoặc xoay.
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;
}
Phương thức trả về true khi cụm từ xuất hiện ít nhất một lần, cung cấp cho bạn một cờ PASS/FAIL ngay lập tức.
Bước 3 – Xác Minh Logo Công Ty
Logo tồn tại dưới dạng hình raster, và hình ảnh của chúng có thể hơi khác nhau do nén. ImageDctHashSearchCriteria tạo một hàm băm nhận thức của logo tham chiếu và so khớp nó với độ dung sai có thể cấu hình.
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;
}
Ngay cả một bản sao logo độ phân giải thấp cũng sẽ được nhận diện.
Bước 4 – Chạy Báo Cáo Tuân Thủ Toàn Diện
Các chính sách thực tế kết hợp nhiều yêu cầu. Khối đầu tiên kiểm tra bốn quy tắc định dạng — sự hiện diện của văn bản, phông chữ, kích thước và kiểu đậm — mỗi quy tắc kết hợp TextSearchCriteria với TextFormattingSearchCriteria qua .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++;
Quy tắc thứ năm xác minh độ phủ trang — dấu nước phải xuất hiện trên mọi trang. Cuối cùng, phán quyết tổng hợp tất cả các kết quả:
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)");
}
Báo cáo đã sẵn sàng để xuất ra dạng JSON, CSV hoặc đưa trực tiếp vào hệ thống ticket.
So Sánh: Trước và Sau
| Kiểm Tra Thủ Công | Kiểm Tra Tự Động | |
|---|---|---|
| Thời Gian | Giờ cho mỗi lô | Giây cho mỗi tệp |
| Độ Chính Xác | Dễ mắc lỗi con người | API xác định |
| Khả Năng Mở Rộng | Giới hạn vài tài liệu | Xử lý hàng ngàn |
| Mã Cần Thiết | Không (nhưng tốn công sức) | ~30 dòng C# |
| Kết Quả | Chỉ kiểm tra bằng mắt | Báo cáo PASS/FAIL có cấu trúc |
Sự tương phản rất rõ rệt: những gì trước đây chiếm cả ngày làm việc giờ chỉ chạy như một công việc nền.
Ví Dụ Thực Tế: Thư Viện Hợp Đồng Pháp Lý
Một công ty luật lưu trữ 15 000 hợp đồng trong một thư mục chung. Chính sách của họ yêu cầu cụm từ “CONFIDENTIAL – CLIENT XYZ” và con dấu của công ty trên mọi trang. Bằng cách tích hợp các đoạn mã trên vào một script PowerShell chạy hàng đêm, công ty đã đạt được:
- Phát hiện 100 % các dấu thiếu (trước đây 8 % trượt qua).
- Không tốn giờ thủ công cho việc kiểm tra.
- Một dấu vết kiểm toán được lưu vào danh sách SharePoint nội bộ để các cuộc rà soát quy định trong tương lai.
// 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);
}
Script chạy không cần giám sát và gửi email tóm tắt mỗi sáng.
Bạn Có Thể Làm Gì Khác Với GroupDocs.Watermark?
Ngoài việc kiểm toán, dự án mẫu cho thấy cách thay thế và xóa dấu nước một cách lập trình. Các ảnh chụp màn hình dưới đây minh họa cả hai thao tác trên một PDF thực tế:
Các kịch bản khác bạn có thể xây dựng với cùng một API:
- Thêm dấu nước theo dõi ẩn để nhúng ID duy nhất cho việc truy vết rò rỉ.
- Thay thế hàng loạt các logo lỗi thời trên toàn bộ kho lưu trữ.
- Tạo chứng chỉ tuân thủ sẵn sàng cho PDF sau một cuộc kiểm tra thành công.
- Tích hợp với Azure Functions hoặc AWS Lambda để xử lý không máy chủ.
Mỗi kịch bản đều sử dụng cùng một API cốt lõi – chỉ cần thay đổi tiêu chí tìm kiếm hoặc loại dấu nước.
Kết Luận
Những gì trước đây yêu cầu một đội ngũ lướt qua các trang, ghi chú và có nguy cơ bỏ sót dấu giờ nay chỉ cần vài giây mã để tạo ra một báo cáo PASS/FAIL có thể kiểm toán. Với GroupDocs.Watermark for .NET, bạn sẽ có:
- Tầm nhìn đầy đủ về mọi dấu nước.
- Phát hiện đáng tin cậy các dấu nước văn bản, văn bản có kiểu và logo.
- Báo cáo tuân thủ tự động.
- Khả năng cập nhật hoặc xóa dấu nước một cách lập trình.
Hãy thử và biến quy trình kiểm tra dấu nước của bạn từ một cơn đau đầu thành một dịch vụ lặp lại được.
Các Bước Tiếp Theo
- Thử bản dùng thử API miễn phí – nhận giấy phép tạm thời tại đây: Temp License
- Đọc tài liệu đầy đủ cho các tùy chọn nâng cao: Docs
- Khám phá tham chiếu API .NET cho tất cả các lớp và phương thức: API Reference
- Sao chép dự án mẫu trên GitHub để xem một ứng dụng console đầy đủ: GitHub Samples
- Đặt câu hỏi hoặc chia sẻ trường hợp sử dụng của bạn trên diễn đàn cộng đồng: Forum