Úvod
Podniky často potřebují označit nebo chránit tisíce souborů – smlouvy, prezentace, faktury – v jedné operaci. Provádět to ručně znamená otevřít každý dokument, vložit logo nebo upozornění na důvěrnost a znovu jej uložit. Nejenže je proces časově náročný, zavádí lidské chyby a zvyšuje riziko duplicitních vodoznaků nebo vynechaných souborů.
GroupDocs.Watermark for .NET řeší tento problém jednotným API, které funguje napříč PDF, DOCX, PPTX, XLSX a běžnými formáty obrázků. Vzorkový projekt obsahuje čtyři typy dokumentů (DOCX, PDF, XLSX, PPTX), takže každý režim pipeline běží na reálných formátech. V tomto tutoriálu projdeme kompletní dávkový pipeline pro vodoznaky, který:
- Načte licenci (nebo přejde do režimu hodnocení).
- Prohledá složku a filtruje pouze formáty, které knihovna dokáže zpracovat.
- Aplikuje dlaždicový textový vodoznak na každý dokument.
- Aplikuje dlaždicový logo vodoznak s vlastní neprůhledností a rotací.
- Přidá vodoznak pouze pokud již není přítomen (idempotentní zpracování).
- Najde a nahradí zastaralé firemní logo novým.
Na konci budete mít připravené řešení, které lze vložit do libovolného .NET projektu.
Proč je dávkové vodoznakování důležité
- Škálovatelnost – Zpracujte desítky nebo tisíce souborů jedním cyklem.
- Konzistence – Stejný vizuální styl se použije na každý dokument, čímž se eliminuje odchylka značky.
- Bezpečnost – Idempotentní logika zabraňuje duplicitním vodoznakům při opakovaném spuštění pipeline.
- Budoucí připravenost – Kód pro výměnu loga vám umožní provést rebrand bez ručního zásahu do každého souboru.
Předpoklady
- .NET 6.0 nebo novější.
- GroupDocs.Watermark NuGet balíček (
dotnet add package GroupDocs.Watermark). - Licenční soubor (dočasný nebo trvalý). Příklady fungují v režimu hodnocení, pokud soubor chybí.
- Dvě složky na disku:
InputFolder– obsahuje zdrojové dokumenty.OutputFolder– kam budou uloženy vodoznakované kopie.
Krok 1 – Načtení licence
Knihovna vyžaduje licenci, aby mohla běžet bez omezení hodnocení. Níže uvedený úryvek se pokusí načíst licenční soubor a v případě, že soubor není nalezen, tiše přejde do režimu hodnocení.
try
{
var license = new License();
license.SetLicense(LicensePath);
Console.WriteLine("License applied successfully.");
}
catch
{
Console.WriteLine("Warning: License not found. Running in evaluation mode.");
}
Klíčový bod: Proměnná LicensePath by měla ukazovat na váš soubor .lic. Pokud soubor chybí, kód pokračuje, což je užitečné pro rychlé testování.
Krok 2 – Zjištění podporovaných souborů
GroupDocs.Watermark dokáže zpracovat jen konkrétní sadu přípon. Pomocná metoda níže prohledá složku, vytvoří hash‑set podporovaných přípon pomocí FileType.GetSupportedFileTypes() a vrátí jen soubory, které odpovídají.
if (!Directory.Exists(folderPath))
{
Console.WriteLine($"Folder not found: {folderPath}");
return new List<string>();
}
var supportedExtensions = FileType.GetSupportedFileTypes()
.Select(ft => ft.Extension.ToLowerInvariant())
.ToHashSet();
var supportedFiles = Directory.GetFiles(folderPath, "*.*", SearchOption.AllDirectories)
.Where(f => supportedExtensions.Contains(Path.GetExtension(f).ToLowerInvariant()))
.ToList();
Console.WriteLine($"Found {supportedFiles.Count} supported file(s) in '{folderPath}'.");
return supportedFiles;
Klíčový bod: Metoda zajišťuje, že pozdější smyčky pro vodoznakování nikdy nenarazí na nepodporovaný formát, což by jinak vyvolalo výjimku za běhu.
Krok 3 – Aplikace dlaždicového textového vodoznaku
Následující kód vytvoří červený, poloprůhledný „CONFIDENTIAL“ vodoznak, otočí jej o ‑30° a dlaždicově rozmístí po všech stránkách pomocí TileOptions.
Directory.CreateDirectory(outputFolder);
int processed = 0, failed = 0;
foreach (var filePath in files)
{
try
{
using var watermarker = new Watermarker(filePath);
var watermark = new TextWatermark(watermarkText,
new Font("Arial", 19, FontStyle.Bold))
{
ForegroundColor = Color.Red,
Opacity = 0.3,
RotateAngle = -30,
TileOptions = new TileOptions
{
LineSpacing = new MeasureValue
{
MeasureType = TileMeasureType.Percent,
Value = 12
},
WatermarkSpacing = new MeasureValue
{
MeasureType = TileMeasureType.Percent,
Value = 10
}
}
};
watermarker.Add(watermark);
var outPath = Path.Combine(outputFolder, Path.GetFileName(filePath));
watermarker.Save(outPath);
processed++;
Console.WriteLine($"[OK] {Path.GetFileName(filePath)}");
}
catch (Exception ex)
{
failed++;
Console.WriteLine($"[FAIL] {Path.GetFileName(filePath)}: {ex.Message}");
}
}
Console.WriteLine($"Text watermark: {processed} processed, {failed} failed");
Klíčové body:
TileOptionsvytváří vzor podobný cihlové zdi, což ztěžuje odstranění vodoznaku bez poškození podkladového obsahu.- Stejný úryvek funguje pro PDF, Word, tabulky i obrázky, protože
Watermarkerabstrahuje formát.
Krok 4 – Aplikace dlaždicového logo vodoznaku
Pokud dáváte přednost vizuální značce, nahraďte textový vodoznak obrázkem. Kód níže ověří, že soubor s logem existuje, a poté jej dlaždicově rozmístí s 40 % neprůhledností a rotací ‑30°.
if (!File.Exists(logoPath))
{
Console.WriteLine($"Logo not found: {logoPath}. Skipping image mode.");
return;
}
Directory.CreateDirectory(outputFolder);
int processed = 0, failed = 0;
foreach (var filePath in files)
{
try
{
using var watermarker = new Watermarker(filePath);
using var watermark = new ImageWatermark(logoPath)
{
Opacity = 0.4,
RotateAngle = -30,
TileOptions = new TileOptions
{
LineSpacing = new MeasureValue
{
MeasureType = TileMeasureType.Percent,
Value = 15
},
WatermarkSpacing = new MeasureValue
{
MeasureType = TileMeasureType.Percent,
Value = 12
}
}
};
watermarker.Add(watermark);
var outPath = Path.Combine(outputFolder, Path.GetFileName(filePath));
watermarker.Save(outPath);
processed++;
Console.WriteLine($"[OK] {Path.GetFileName(filePath)} - logo applied");
}
catch (Exception ex)
{
failed++;
Console.WriteLine($"[FAIL] {Path.GetFileName(filePath)}: {ex.Message}");
}
}
Console.WriteLine($"Logo watermark: {processed} processed, {failed} failed");
Klíčové body:
- Stejná logika
TileOptionspoužitá pro text funguje i pro obrázky, což zajišťuje jednotný vzhled napříč všemi stránkami. Opacityumožňuje, aby byl podklad čitelný a zároveň zobrazoval značku.
Krok 5 – Idempotentní vodoznakování (přeskočit existující značky)
Spuštění pipeline vícekrát by nemělo vrstvit vodoznaky na sebe. Tento úryvek vyhledá přesnou instanci textového vodoznaku před přidáním nového.
Directory.CreateDirectory(outputFolder);
int applied = 0, skipped = 0, failed = 0;
foreach (var filePath in files)
{
try
{
using var watermarker = new Watermarker(filePath);
var criteria = new TextSearchCriteria(watermarkText, false);
var existing = watermarker.Search(criteria);
if (existing.Count > 0)
{
skipped++;
Console.WriteLine($"[SKIP] {Path.GetFileName(filePath)} – already contains watermark");
continue;
}
var watermark = new TextWatermark(watermarkText,
new Font("Arial", 19, FontStyle.Bold))
{
ForegroundColor = Color.Red,
Opacity = 0.3,
RotateAngle = -30,
TileOptions = new TileOptions
{
LineSpacing = new MeasureValue
{
MeasureType = TileMeasureType.Percent,
Value = 12
},
WatermarkSpacing = new MeasureValue
{
MeasureType = TileMeasureType.Percent,
Value = 10
}
}
};
watermarker.Add(watermark);
var outPath = Path.Combine(outputFolder, Path.GetFileName(filePath));
watermarker.Save(outPath);
applied++;
Console.WriteLine($"[OK] {Path.GetFileName(filePath)} – watermark applied");
}
catch (Exception ex)
{
failed++;
Console.WriteLine($"[FAIL] {Path.GetFileName(filePath)}: {ex.Message}");
}
}
Console.WriteLine($"Smart batch: {applied} applied, {skipped} skipped, {failed} failed");
Klíčový bod: TextSearchCriteria s false pro rozlišení velkých a malých písmen zajišťuje, že přeskočíme jen dokumenty, které již obsahují přesně ten vodoznak, který chceme přidat.
Krok 6 – Nahrazení zastaralého loga ve složce
Když se firma rebranduje, může být potřeba vyměnit každé staré logo za nové. Kód kombinuje dvě strategie vyhledávání obrázků – DCT‑hash pro přesnost a barevný histogram pro toleranci – a poté přepíše data obrázku u každé nalezené shody.
if (!File.Exists(oldLogoPath) || !File.Exists(newLogoPath))
{
Console.WriteLine("Old or new logo file missing – aborting replacement.");
return;
}
Directory.CreateDirectory(outputFolder);
byte[] newLogoData = File.ReadAllBytes(newLogoPath);
int replaced = 0, notFound = 0;
var settings = new WatermarkerSettings
{
SearchableObjects = new SearchableObjects
{
PdfSearchableObjects = PdfSearchableObjects.All
}
};
foreach (var filePath in files)
{
try
{
using var watermarker = new Watermarker(filePath, settings);
var dct = new ImageDctHashSearchCriteria(oldLogoPath) { MaxDifference = 0.4 };
var hist = new ImageColorHistogramSearchCriteria(oldLogoPath) { MaxDifference = 0.5 };
var criteria = dct.Or(hist);
var found = watermarker.Search(criteria);
if (found.Count == 0)
{
notFound++;
Console.WriteLine($"[--] {Path.GetFileName(filePath)} – old logo not found");
continue;
}
foreach (PossibleWatermark wm in found)
{
try
{
wm.ImageData = newLogoData;
}
catch
{
// Some watermark types cannot be overwritten – ignore.
}
}
var outPath = Path.Combine(outputFolder, Path.GetFileName(filePath));
watermarker.Save(outPath);
replaced++;
Console.WriteLine($"[OK] {Path.GetFileName(filePath)} – {found.Count} logo(s) replaced");
}
catch (Exception ex)
{
Console.WriteLine($"[FAIL] {Path.GetFileName(filePath)}: {ex.Message}");
}
}
Console.WriteLine($"Logo replacement: {replaced} updated, {notFound} without old logo");
Klíčové body:
WatermarkerSettingssPdfSearchableObjects.Allumožňuje vyhledávat loga uložená jako PDF artefakty.- Kombinace kritérií DCT‑hash a barevného histogramu zachytí jak přesná vektorová loga (Office), tak rasterizované verze (PDF).
Nejlepší postupy a tipy
- Vytvořte výstupní složku jednou (
Directory.CreateDirectory) – metoda je idempotentní a zabraňuje závodním podmínkám. - Logujte průběh – výstup do konzole v každém kroku usnadňuje sledovat, které soubory uspěly nebo selhaly.
- Laděte
OpacityaRotateAnglepodle pokynů značky; hodnota mezi 0.3–0.5 je obvykle dostatečná, aby byla viditelná, ale ne rušivá. - Používejte idempotentní chytrý batch pro jakýkoli opakovaný úkol (např. noční aktualizace značky).
- Otestujte výměnu loga na malé vzorku před spuštěním na celém úložišti, abyste se ujistili, že kritéria vyhledávání jsou správně nastavená.
Řešení běžných problémů
| Příznak | Pravděpodobná příčina | Oprava |
|---|---|---|
| Žádné soubory nejsou zpracovány | ScanFolderForSupportedFiles vrátil prázdný seznam |
Ověřte cestu InputFolder a že složka obsahuje podporované formáty (PDF, DOCX, PPTX, XLSX, PNG, JPG, atd.) |
| Vodoznak není viditelný | Neprůhlednost nastavena příliš nízko nebo barva splývá s pozadím | Zvyšte Opacity (např. 0.5) nebo změňte ForegroundColor na kontrastní odstín |
| Loga v PDF nebyla nalezena při výměně | Loga přidána jako operátory v content‑streamu (nevyhledatelná) | Při vkládání loga použijte PdfArtifactWatermarkOptions, aby se stala vyhledatelným artefaktem |
Výjimka System.Drawing.Common na Linuxu |
Chybějící nativní knihovny GDI+ | Nainstalujte libgdiplus na cílovém Linux serveru nebo povolte Unix podporu v .csproj (<RuntimeHostConfigurationOption Include="System.Drawing.EnableUnixSupport" Value="true" />). |
Závěr
Nyní máte kompletní, připravený pipeline, který dokáže:
- Licencovat knihovnu.
- Automaticky detekovat podporované dokumenty.
- Aplikovat dlaždicové textové nebo logo vodoznaky.
- Bezpečně běžet opakovaně bez vytváření duplicit.
- Nahradit staré firemní logo v celé složce.
Tyto stavební bloky můžete kombinovat a přizpůsobit libovolnému workflow pro značkování nebo ochranu dokumentů v .NET.