הדרך הישנה הייתה כואבת
דמיינו קצין ציות שמוטל עליו לוודא שכל חוזה בכונן משותף מכיל את המילה “CONFIDENTIAL” ואת לוגו החברה על כל דף. התהליך הנוכחי נראה כך:
- פותחים קובץ בתצוגה.
- עוברים על כל דף בחיפוש אחר הביטוי או התמונה.
- רושמים הערות בגיליון אלקטרוני.
- חוזרים על הפעולה עבור אלפי קבצי PDF, Word והצגות.
סימן מים אחד שפספסו יכול לגרום לביקורת יקרה, והמאמץ הידני בקלות חורג מ‑8 שעות לשבוע עבור צוות קטן. בנוסף, טקסט מסובב, מילים מפוצלות או לוגואים שנשמרים כתמונות לעיתים קרובות נעלמים מעיני האדם, מה שמחשף את הארגון לסיכון.
יש דרך טובה יותר
GroupDocs.Watermark for .NET מסיר כל שלב של ניחוש. המנוע האגרגטי שלו יכול לקרוא יותר מ‑100 סוגי מסמכים, לאתר טקסט, תמונה ואפילו סימני מים מעוצבים, ולחשוף את כל המטא‑נתונים הרלוונטיים דרך API נקי. המדריך הבא מציג כיצד כמה קטעי קוד קצרים מחליפים את הלולאה הידנית בעבודה אוטומטית, חוזרת על עצמה וניתנת לשימוש חוזר.
דרישות מוקדמות
- .NET 6.0 או גרסה מאוחרת יותר.
- חבילת NuGet GroupDocs.Watermark (
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}");
}
}
נקודה מרכזית: הלולאה רצה בפחות משנייה עבור קובץ PDF של 50 דפים טיפוסיים.
שלב 2 – אימות סימן מים טקסטואלי נדרש
מדיניות ציות דורשת לעיתים ביטוי ספציפי (למשל “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 מיידי.
שלב 3 – אימות לוגו החברה
לוגואים נשמרים כתמונות רסטר, והופעתם עשויה להשתנות במקצת עקב דחיסה. ImageDctHashSearchCriteria יוצר hash תפיסתי של הלוגו הרפרנס ומשווה אותו עם סובלנות מוגדרת.
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;
}
גם עותק ברזולוציה נמוכה של הלוגו יזוהה.
שלב 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++;
הכלל החמישי בודק כיסוי דפים — סימן המים חייב להופיע בכל דף. לבסוף, ההחלטה מאגדת את כל התוצאות:
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 או להעברה ישירה למערכת ניהול תקלות.
השוואה ציד‑ציד: לפני ואחרי
| ביקורת ידנית | ביקורת אוטומטית | |
|---|---|---|
| זמן | שעות לכל אצווה | שניות לכל קובץ |
| דיוק | רגיש לטעויות אנוש | API דטרמיניסטי |
| סקלאביליות | מוגבלת למספר מסמכים קטן | מטפל באלפים |
| קוד נדרש | אין (אך עבודה אינטנסיבית) | ~30 שורות C# |
| פלט | בדיקה ויזואלית בלבד | דוח PASS/FAIL מובנה |
ההבדל בולט: מה שהיה תופס יום עבודה שלם כעת רץ כמשימה ברקע.
דוגמה מהעולם האמיתי: ספריית חוזים משפטיים
משרד עורכי דין שומר 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);
}
הסקריפט פועל ללא השגחה ושולח סיכום במייל כל בוקר.
מה עוד אפשר לעשות עם GroupDocs.Watermark?
מעבר לביקורת, פרויקט הדוגמה מציג כיצד להחליף ו‑להסיר סימני מים תכנותית. הצילומים למטה מדגימים את שתי הפעולות על קובץ PDF אמיתי:
תסריטים נוספים שניתן לבנות עם אותו API:
- הוספת סימני מים בלתי נראים שמכילים מזהה ייחודי למעקב דליפות.
- החלפת לוגואים מיושנים במרוכז בכל הארכיון.
- יצירת תעודות ציות מוכנות ל‑PDF לאחר ביקורת מוצלחת.
- אינטגרציה עם Azure Functions או AWS Lambda לעיבוד ללא שרת.
כל תסריט משתמש באותו API בסיסי – רק משנים את קריטריוני החיפוש או את סוג סימן המים.
סיכום
מה שהיה דורש צוות שלם לעבור על דפים, לכתוב הערות ולסכן סימני מים שלא נצפו, הפך ל‑כמה שניות של קוד שמייצרות דוח PASS/FAIL שניתן לביקורת. עם GroupDocs.Watermark for .NET אתם מקבלים:
- נראות מלאה של כל סימן מים.
- זיהוי אמין של טקסט, טקסט מעוצב ולוגואים.
- דוחות ציות אוטומטיים.
- אפשרות לעדכן או להסיר סימני מים תכנותית.
נסו זאת והפכו את תהליך ציות סימני המים שלכם ממקור כאב לשירות חוזר וניתן.
הצעדים הבאים
- נסו את גרסת ה‑API החינמית – קבלו רישיון זמני כאן: Temp License
- קראו את התיעוד המלא לאפשרויות מתקדמות: Docs
- חקור את רפרנס ה‑API של .NET לכל המחלקות והמתודות: API Reference
- שיבטו את פרויקט הדוגמה ב‑GitHub כדי לראות אפליקציית קונסול שלמה: GitHub Samples
- שאלו שאלות או שתפו מקרה שימוש בפורום הקהילה: Forum