Ú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ý:

  1. Načte licenci (nebo přejde do režimu hodnocení).
  2. Prohledá složku a filtruje pouze formáty, které knihovna dokáže zpracovat.
  3. Aplikuje dlaždicový textový vodoznak na každý dokument.
  4. Aplikuje dlaždicový logo vodoznak s vlastní neprůhledností a rotací.
  5. Přidá vodoznak pouze pokud již není přítomen (idempotentní zpracování).
  6. 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:

  • TileOptions vytváří 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 Watermarker abstrahuje formát.

Textová vodoznak aplikovaný na dokument


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 TileOptions použitá pro text funguje i pro obrázky, což zajišťuje jednotný vzhled napříč všemi stránkami.
  • Opacity umožňuje, aby byl podklad čitelný a zároveň zobrazoval značku.

Dlaždicový logo vodoznak aplikovaný na dokument


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:

  • WatermarkerSettings s PdfSearchableObjects.All umožň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).

Staré logo nahrazeno novým logem


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 Opacity a RotateAngle podle 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.

Další zdroje