How to Remove Watermark from Diagram Files in .NET

Introduction

Ever received a diagram file with an outdated watermark that you need to remove? Maybe it’s a client’s old branding, a “DRAFT” stamp that shouldn’t be there anymore, or confidential markings that need to be cleared before sharing. If you’ve tried manually editing these watermarks in Visio, you know it’s tedious—especially when dealing with dozens (or hundreds) of files.

Here’s the good news: GroupDocs.Watermark for .NET makes it ridiculously easy to search for, identify, and remove watermarks from diagram files programmatically. Whether you’re working with VSDX, VSD, or other Visio formats, you can automate the entire process in just a few lines of C# code.

In this guide, you’ll learn exactly how to remove watermarks from diagram files using .NET. We’ll cover everything from basic setup to advanced troubleshooting, so you can handle watermark removal confidently in production environments.

What You’ll Master:

  • Loading and processing diagram files (VSDX, VSD, VSS, and more)
  • Searching for specific watermarks (both text and image-based)
  • Removing unwanted watermarks efficiently
  • Handling common issues and edge cases
  • Optimizing performance for batch processing

Let’s start with what you’ll need before diving into the code.

Prerequisites

Before you can remove watermarks from diagrams, make sure you have these basics covered:

Required Libraries and Dependencies

  • GroupDocs.Watermark for .NET (version 23.x or later recommended)
  • .NET Framework 4.6.1+ or .NET Core 3.1+ (or .NET 5/6/7 if you’re on newer versions)

Environment Setup

  • Visual Studio 2019 or later (or your preferred IDE)
  • Access to diagram files for testing (VSDX files work great)
  • Basic development environment with NuGet package manager

Knowledge Prerequisites

  • Comfortable with C# basics (variables, methods, using statements)
  • Familiarity with file I/O operations
  • Understanding of how diagram files are structured (helpful but not required)

Don’t worry if you’re not a Visio expert—the library handles all the heavy lifting for you.

Setting Up GroupDocs.Watermark for .NET

Getting started is straightforward. You’ll install the package, set up licensing, and configure your project in just a few minutes.

Installation Options

Choose whichever method fits your workflow:

.NET CLI (fastest for command-line fans)

dotnet add package GroupDocs.Watermark

Package Manager Console (if you’re in Visual Studio)

Install-Package GroupDocs.Watermark

NuGet Package Manager UI (visual approach)

  1. Right-click your project → Manage NuGet Packages
  2. Search for “GroupDocs.Watermark”
  3. Click Install on the latest stable version

License Acquisition

GroupDocs.Watermark isn’t a free library, but you have options:

  • Free Trial: Start with a temporary license that gives you full access for 30 days (perfect for evaluating or completing a project)
  • Full License: Purchase from GroupDocs for production use
  • Limitations Without License: Trial mode adds evaluation watermarks and has page/file restrictions

Pro tip: If you’re just experimenting, the temporary license is your best friend. It removes all restrictions so you can properly test performance with real files.

Basic Initialization and Setup

Once installed, add these using directives at the top of your C# file:

using GroupDocs.Watermark;
using GroupDocs.Watermark.Contents.Diagram;
using GroupDocs.Watermark.Options.Diagram;
using GroupDocs.Watermark.Search;
using GroupDocs.Watermark.Search.SearchCriteria;

Why these namespaces matter:

  • GroupDocs.Watermark: Core watermark operations
  • Contents.Diagram: Diagram-specific content handling
  • Options.Diagram: Configuration options for diagram processing
  • Search: Watermark detection functionality
  • SearchCriteria: Criteria for finding specific watermarks

Now you’re ready to start working with diagram files. Let’s jump into the implementation.

Implementation Guide

This section breaks down the watermark removal process into digestible steps. We’ll start with loading files, then searching for watermarks, and finally removing them.

Load and Initialize Watermarker

What This Does: Opens your diagram file and prepares it for watermark operations. Think of it as opening a document in Word before you can edit it—you need this first step before anything else works.

Step 1: Set Up File Paths

string documentPath = "YOUR_DOCUMENT_DIRECTORY/sample.vsdx"; // Your input diagram
string outputDirectory = "YOUR_OUTPUT_DIRECTORY"; // Where to save the cleaned file

Real-World Context: In production, these paths typically come from configuration files, database records, or user uploads. Hard-coding paths like this is fine for development, but consider using Path.Combine() and environment variables when deploying.

Step 2: Initialize Watermarker

DiagramLoadOptions diagramLoadOptions = new DiagramLoadOptions();
using (Watermarker watermarker = new Watermarker(documentPath, diagramLoadOptions))
{
    // Your watermark operations go here
    // The 'using' statement ensures proper cleanup even if errors occur
}

Why This Matters: The using statement is crucial here. It automatically disposes of the Watermarker object when you’re done, releasing file locks and freeing memory. If you skip this and just create a Watermarker object normally, you might run into “file in use” errors when trying to save or delete files.

Common Scenario: Let’s say you’re processing uploaded diagrams from users. You’d wrap this in a try-catch block and load the file path from your upload directory:

try 
{
    string uploadedFile = Path.Combine(Server.MapPath("~/Uploads"), fileName);
    using (Watermarker watermarker = new Watermarker(uploadedFile, new DiagramLoadOptions()))
    {
        // Process watermarks here
    }
}
catch (Exception ex)
{
    // Log error and show user-friendly message
    LogError($"Failed to process {fileName}: {ex.Message}");
}

Search for Watermarks on a Specific Page

What This Does: Scans a particular page of your diagram to find watermarks matching your criteria. You can search for text-based watermarks (like “CONFIDENTIAL”), image watermarks (like logos), or both simultaneously.

Step 1: Retrieve Diagram Content

DiagramContent diagramContent = watermarker.GetContent<DiagramContent>();

Explanation: This line gives you access to the diagram’s internal structure. DiagramContent exposes pages, shapes, headers, and other elements where watermarks might hide. It’s similar to how you’d access worksheets in an Excel file.

Step 2: Define Search Criteria

// Search for an image watermark (like a logo)
ImageSearchCriteria imageSearchCriteria = new ImageDctHashSearchCriteria("YOUR_DOCUMENT_DIRECTORY/company_logo.png");

// Search for text watermark (like "DRAFT" or "Company Name")
TextSearchCriteria textSearchCriteria = new TextSearchCriteria("Company Name");

When to Use Each Type:

  • ImageSearchCriteria: Use when you need to remove logos, stamps, or any visual watermark. The ImageDctHashSearchCriteria uses perceptual hashing, so it finds images even if they’ve been slightly resized or compressed.
  • TextSearchCriteria: Perfect for removing text like “DRAFT”, “CONFIDENTIAL”, dates, or company names. It’s case-insensitive by default.

Pro Tip: You can combine multiple criteria. For example, if you want to remove both your old logo AND the old company name in one pass:

var combinedCriteria = imageSearchCriteria.Or(textSearchCriteria);

Step 3: Execute Search on a Page

// Search the first page (index 0)
PossibleWatermarkCollection possibleWatermarks = diagramContent.Pages[0].Search(textSearchCriteria.Or(imageSearchCriteria));

Console.WriteLine($"Found {possibleWatermarks.Count} potential watermarks on page 1");

Understanding Page Indexing: Diagram pages are zero-indexed. So Pages[0] is page 1, Pages[1] is page 2, and so on. If you need to process all pages, use a loop:

for (int i = 0; i < diagramContent.Pages.Count; i++)
{
    var watermarks = diagramContent.Pages[i].Search(combinedCriteria);
    Console.WriteLine($"Page {i + 1}: Found {watermarks.Count} watermarks");
}

Real-World Example: Imagine you’re processing diagrams from multiple departments. Some have “DRAFT” stamps, others have old logos. This search approach lets you find all of them in one scan:

// Search for multiple text variations
var draftCriteria = new TextSearchCriteria("DRAFT");
var confidentialCriteria = new TextSearchCriteria("CONFIDENTIAL");
var oldBrandCriteria = new TextSearchCriteria("OldCompanyName Inc.");

var allTextCriteria = draftCriteria.Or(confidentialCriteria).Or(oldBrandCriteria);
var allWatermarks = diagramContent.Pages[0].Search(allTextCriteria);

Remove Found Watermarks

What This Does: Deletes all watermarks that matched your search criteria. This is where the actual cleanup happens—you’re modifying the diagram file to remove unwanted elements.

Step 1: Clear Watermarks

possibleWatermarks.Clear(); // Removes all found watermarks from the collection

Important Note: The Clear() method removes ALL watermarks in the collection. If you want selective removal, iterate through the collection and remove specific items:

// Remove only watermarks containing specific text
for (int i = possibleWatermarks.Count - 1; i >= 0; i--)
{
    if (possibleWatermarks[i].Text.Contains("DRAFT"))
    {
        possibleWatermarks.RemoveAt(i);
    }
}

Why Loop Backwards? When removing items from a collection while iterating, going backwards prevents index shifting issues. It’s a common C# pattern for safe in-place removal.

Step 2: Save Changes

string outputFileName = Path.Combine(outputDirectory, "cleaned_diagram.vsdx");
watermarker.Save(outputFileName);

Console.WriteLine($"Watermarks removed successfully! Saved to: {outputFileName}");

Save Options: By default, Save() keeps the original format. If you need to convert formats, use SaveOptions:

// Save as a different format (if needed)
watermarker.Save(outputFileName, new SaveOptions { Format = FileFormat.Vsdx });

Production Consideration: Always save to a different filename or directory initially. This lets you verify the output before overwriting originals:

// Better approach for production
string tempOutput = Path.Combine(outputDirectory, $"temp_{Guid.NewGuid()}.vsdx");
watermarker.Save(tempOutput);

// Verify the output is valid
if (File.Exists(tempOutput) && new FileInfo(tempOutput).Length > 0)
{
    File.Copy(tempOutput, finalOutputPath, overwrite: true);
    File.Delete(tempOutput);
}

Practical Applications

Now that you know the mechanics, here’s how you’d actually use this in real-world scenarios:

1. Bulk Watermark Removal for Document Migration

Scenario: Your company rebranded, and you have 500 Visio diagrams with the old logo and company name. Manually updating each one would take days.

Solution:

string[] diagramFiles = Directory.GetFiles(@"C:\Diagrams", "*.vsdx");
var oldLogo = new ImageDctHashSearchCriteria(@"C:\Assets\old_logo.png");
var oldName = new TextSearchCriteria("OldCompany Inc.");

foreach (string file in diagramFiles)
{
    using (Watermarker watermarker = new Watermarker(file, new DiagramLoadOptions()))
    {
        var content = watermarker.GetContent<DiagramContent>();
        foreach (var page in content.Pages)
        {
            var watermarks = page.Search(oldLogo.Or(oldName));
            watermarks.Clear();
        }
        watermarker.Save(file); // Overwrites original after removing watermarks
    }
}

2. Confidentiality Management Before External Sharing

Scenario: You’re sharing technical diagrams with a partner, but they contain internal “CONFIDENTIAL” watermarks that should be removed.

Solution: Create a “sanitization” function:

public static void SanitizeDiagramForExternal(string inputPath, string outputPath)
{
    var confidentialMarkers = new[] { "CONFIDENTIAL", "INTERNAL", "DO NOT DISTRIBUTE" };
    
    using (Watermarker watermarker = new Watermarker(inputPath, new DiagramLoadOptions()))
    {
        var content = watermarker.GetContent<DiagramContent>();
        
        foreach (var marker in confidentialMarkers)
        {
            var criteria = new TextSearchCriteria(marker);
            foreach (var page in content.Pages)
            {
                page.Search(criteria).Clear();
            }
        }
        
        watermarker.Save(outputPath);
    }
}

3. Automated Document Version Control

Scenario: Each diagram version has a watermark like “Version 1.2 - Draft”. When finalizing, you want to remove version watermarks but keep the final approval stamp.

Solution: Use selective removal based on text patterns:

using (Watermarker watermarker = new Watermarker(diagramPath, new DiagramLoadOptions()))
{
    var content = watermarker.GetContent<DiagramContent>();
    var versionPattern = new TextSearchCriteria("Version");
    
    foreach (var page in content.Pages)
    {
        var watermarks = page.Search(versionPattern);
        
        // Remove only draft versions, keep final approval
        for (int i = watermarks.Count - 1; i >= 0; i--)
        {
            if (watermarks[i].Text.Contains("Draft") || watermarks[i].Text.Contains("Review"))
            {
                watermarks.RemoveAt(i);
            }
        }
    }
    
    watermarker.Save(outputPath);
}

4. Integration with Digital Asset Management (DAM) Systems

Scenario: Your DAM automatically applies watermarks to downloaded assets. When users purchase full rights, the system should remove watermarks automatically.

Implementation Pattern:

public class WatermarkRemovalService
{
    public async Task ProcessPurchasedAsset(int assetId, int userId)
    {
        // Fetch asset details from database
        var asset = await _assetRepository.GetByIdAsync(assetId);
        
        // Create user-specific output path
        string outputPath = $"~/UserDownloads/{userId}/{asset.FileName}";
        
        // Remove watermarks
        using (Watermarker watermarker = new Watermarker(asset.FilePath, new DiagramLoadOptions()))
        {
            var content = watermarker.GetContent<DiagramContent>();
            foreach (var page in content.Pages)
            {
                // Remove all watermarks (user purchased full rights)
                var allWatermarks = page.Search();
                allWatermarks.Clear();
            }
            watermarker.Save(outputPath);
        }
        
        // Log transaction and send download link to user
        await _auditService.LogWatermarkRemoval(assetId, userId);
        await _emailService.SendDownloadLink(userId, outputPath);
    }
}

Troubleshooting Common Issues

Here are the most common problems you’ll encounter and how to fix them quickly:

Issue 1: “File is being used by another process”

Symptoms: You get an IOException when trying to save the output file.

Causes:

  • Forgot to use using statement with Watermarker
  • File is open in Visio or another application
  • Antivirus is scanning the file

Solutions:

// Always use 'using' statements
using (Watermarker watermarker = new Watermarker(inputPath, new DiagramLoadOptions()))
{
    // Do your work
    watermarker.Save(outputPath);
} // File is automatically released here

// If you must save to the same path, use a temp file
string tempPath = Path.GetTempFileName();
watermarker.Save(tempPath);
File.Copy(tempPath, originalPath, overwrite: true);
File.Delete(tempPath);

Issue 2: Watermarks Not Found (Search Returns Zero Results)

Symptoms: possibleWatermarks.Count is always 0, even though you can see watermarks in Visio.

Causes:

  • Watermark is embedded in a different format than expected
  • Text search is case-sensitive in some scenarios
  • Image hash doesn’t match due to compression

Solutions:

// Try broader search criteria
var broadTextSearch = new TextSearchCriteria(""); // Finds all text

// For images, try searching without specific hash
var content = watermarker.GetContent<DiagramContent>();
foreach (var page in content.Pages)
{
    // Search for all possible watermarks
    var allWatermarks = page.Search();
    
    // Inspect what was found
    foreach (var wm in allWatermarks)
    {
        Console.WriteLine($"Found: Type={wm.GetType().Name}, Text={wm.Text}");
    }
}

Issue 3: Performance Issues with Large Files

Symptoms: Processing takes forever or crashes with large VSDX files (10MB+).

Causes:

  • Loading entire file into memory
  • Processing all pages when only one needs cleaning
  • Not disposing objects properly

Solutions:

// Process only specific pages if possible
using (Watermarker watermarker = new Watermarker(largeDiagramPath, new DiagramLoadOptions()))
{
    var content = watermarker.GetContent<DiagramContent>();
    
    // Process only page 1 instead of all pages
    var watermarks = content.Pages[0].Search(criteria);
    watermarks.Clear();
    
    watermarker.Save(outputPath);
} // Memory freed automatically

// For very large batches, process asynchronously
await Task.Run(() => ProcessDiagram(filePath));

Issue 4: Output File is Corrupted

Symptoms: Saved file won’t open in Visio or shows errors.

Causes:

  • Saved before all operations completed
  • File format mismatch
  • Removed too many elements (structural shapes, not just watermarks)

Solutions:

// Always validate before removing
foreach (var watermark in possibleWatermarks)
{
    // Only remove if it's definitely a watermark, not structural content
    if (IsDefinitelyWatermark(watermark))
    {
        possibleWatermarks.Remove(watermark);
    }
}

// Validate file after saving
if (File.Exists(outputPath))
{
    try 
    {
        // Try loading it to ensure it's valid
        using (var validator = new Watermarker(outputPath, new DiagramLoadOptions()))
        {
            Console.WriteLine("Output file is valid!");
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Warning: Output file may be corrupted: {ex.Message}");
    }
}

Issue 5: “License not set” or Evaluation Watermarks

Symptoms: Output files have “Evaluation Only” watermarks or feature restrictions.

Solution:

// Set license before any operations
License license = new License();
license.SetLicense("path/to/GroupDocs.Watermark.lic");

// Or use temporary license
license.SetLicense("your-temporary-license-key");

// Now create Watermarker
using (Watermarker watermarker = new Watermarker(inputPath, new DiagramLoadOptions()))
{
    // Operations will work without limitations
}

Performance Considerations

When you’re moving from testing to production, performance becomes critical. Here’s how to optimize:

Optimize Load Times

Problem: Opening large diagram files takes too long.

Solution:

// Use DiagramLoadOptions to optimize loading
var loadOptions = new DiagramLoadOptions
{
    // Only load what you need
    LoadOnlyMetadata = false // Set true if you only need file info, not content
};

using (Watermarker watermarker = new Watermarker(inputPath, loadOptions))
{
    // Faster initialization
}

// For batch processing, reuse criteria objects
var criteria = new TextSearchCriteria("DRAFT");
foreach (string file in files)
{
    using (var watermarker = new Watermarker(file, loadOptions))
    {
        // Reusing 'criteria' is more efficient than recreating it
        var content = watermarker.GetContent<DiagramContent>();
        content.Pages[0].Search(criteria).Clear();
        watermarker.Save(GetOutputPath(file));
    }
}

Memory Management Best Practices

Key Principle: Always dispose of objects promptly to prevent memory leaks.

// BAD: Memory leak waiting to happen
Watermarker watermarker = new Watermarker(path, new DiagramLoadOptions());
// ...operations...
// Forgot to dispose!

// GOOD: Automatic disposal
using (Watermarker watermarker = new Watermarker(path, new DiagramLoadOptions()))
{
    // Operations
} // Automatically disposed here, even if exception occurs

// For async operations
public async Task ProcessDiagramAsync(string path)
{
    using (Watermarker watermarker = new Watermarker(path, new DiagramLoadOptions()))
    {
        await Task.Run(() => 
        {
            // Heavy processing here
            var content = watermarker.GetContent<DiagramContent>();
            // ...
        });
        watermarker.Save(outputPath);
    } // Still properly disposed in async context
}

Batch Processing Guidelines

When processing multiple files, follow these patterns for optimal performance:

public class DiagramBatchProcessor
{
    private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(4); // Limit concurrent processing
    
    public async Task ProcessBatchAsync(IEnumerable<string> filePaths)
    {
        var tasks = filePaths.Select(async path =>
        {
            await _semaphore.WaitAsync(); // Limit to 4 concurrent files
            try
            {
                await ProcessSingleDiagramAsync(path);
            }
            finally
            {
                _semaphore.Release();
            }
        });
        
        await Task.WhenAll(tasks);
    }
    
    private async Task ProcessSingleDiagramAsync(string path)
    {
        await Task.Run(() =>
        {
            using (Watermarker watermarker = new Watermarker(path, new DiagramLoadOptions()))
            {
                // Process diagram
                var content = watermarker.GetContent<DiagramContent>();
                foreach (var page in content.Pages)
                {
                    var watermarks = page.Search(GetCriteria());
                    watermarks.Clear();
                }
                watermarker.Save(GetOutputPath(path));
            }
        });
    }
}

// Usage
var processor = new DiagramBatchProcessor();
await processor.ProcessBatchAsync(Directory.GetFiles(@"C:\Diagrams", "*.vsdx"));

Why This Works: Processing 4 files concurrently balances CPU usage without overwhelming memory. Adjust the semaphore count based on your server’s specs.

Best Practices for Production Use

Here’s what separates hobby projects from production-ready code:

1. Always Validate Input Files

public bool IsValidDiagram(string filePath)
{
    if (!File.Exists(filePath))
        return false;
    
    var extension = Path.GetExtension(filePath).ToLowerInvariant();
    var validExtensions = new[] { ".vsdx", ".vsd", ".vss", ".vssx" };
    
    if (!validExtensions.Contains(extension))
        return false;
    
    // Try loading to ensure it's not corrupted
    try
    {
        using (Watermarker watermarker = new Watermarker(inputPath, new DiagramLoadOptions()))
        {
            var content = watermarker.GetContent<DiagramContent>();
            
            // Edge case 2: Diagram has no pages
            if (content.Pages.Count == 0)
            {
                throw new InvalidOperationException("Diagram contains no pages");
            }
            
            foreach (var page in content.Pages)
            {
                var watermarks = page.Search(GetCriteria());
                
                // Edge case 3: Verify we're not removing structural elements
                var safeToRemove = watermarks.Where(w => !IsStructuralElement(w)).ToList();
                
                foreach (var watermark in safeToRemove)
                {
                    watermarks.Remove(watermark);
                }
            }
            
            watermarker.Save(workingPath);
        }
        
        // Edge case 4: If in-place editing, replace original
        if (isInPlace)
        {
            File.Delete(inputPath);
            File.Move(workingPath, inputPath);
        }
    }
    catch
    {
        // Clean up temp file if something went wrong
        if (isInPlace && File.Exists(workingPath))
        {
            try { File.Delete(workingPath); } catch { }
        }
        throw;
    }
}

private bool IsStructuralElement(PossibleWatermark watermark)
{
    // Implement logic to detect if this is part of the diagram structure
    // rather than an actual watermark
    return false; // Simplified for this example
}

When to Use This vs Manual Removal

Not sure if programmatic removal is right for your situation? Here’s a quick decision matrix:

Use GroupDocs.Watermark When:

  • ✅ You have more than 10 files to process (automation pays off)
  • ✅ You need consistent, repeatable results across many files
  • ✅ Watermarks follow predictable patterns (same text, same logo)
  • ✅ You’re building a document workflow system that processes diagrams
  • ✅ Time is critical and manual editing would take hours or days
  • ✅ You need to audit or log what was removed and when

Use Manual Removal When:

  • ❌ You have 1-5 files (manual might be faster than setting up code)
  • ❌ Each watermark is unique and requires human judgment
  • ❌ Watermarks are embedded in complex shapes that need visual inspection
  • ❌ You’re not familiar with C# and would need to learn it first
  • ❌ Files contain sensitive data and you can’t risk programmatic errors

Cost-Benefit Example: Let’s say manual removal takes 5 minutes per file, and you have 100 files. That’s 500 minutes (8.3 hours) of tedious work. Setting up this code might take 1-2 hours initially, but then processes all 100 files in minutes—and you can reuse it forever.

Pro Tips for Advanced Users

Once you’ve mastered the basics, these advanced techniques will level up your watermark management:

1. Selective Removal with Custom Logic

Remove only watermarks that meet specific criteria:

public void RemoveSelectiveWatermarks(string inputPath, string outputPath)
{
    using (Watermarker watermarker = new Watermarker(inputPath, new DiagramLoadOptions()))
    {
        var content = watermarker.GetContent<DiagramContent>();
        
        foreach (var page in content.Pages)
        {
            var allWatermarks = page.Search();
            
            // Remove only if:
            // 1. Contains "DRAFT" or "CONFIDENTIAL"
            // 2. Is smaller than 100x100 pixels (likely a stamp, not structural)
            // 3. Was added in the last 30 days (if timestamp available)
            
            for (int i = allWatermarks.Count - 1; i >= 0; i--)
            {
                var wm = allWatermarks[i];
                
                bool shouldRemove = 
                    (wm.Text?.Contains("DRAFT") == true || wm.Text?.Contains("CONFIDENTIAL") == true) &&
                    (wm.Width < 100 && wm.Height < 100);
                
                if (shouldRemove)
                {
                    allWatermarks.RemoveAt(i);
                }
            }
        }
        
        watermarker.Save(outputPath);
    }
}

2. Parallel Processing with Progress Tracking

Process large batches efficiently with real-time progress:

public async Task<ProcessingReport> ProcessBatchWithProgress(
    IEnumerable<string> files, 
    IProgress<int> progress)
{
    var fileList = files.ToList();
    int completed = 0;
    int total = fileList.Count;
    var report = new ProcessingReport();
    
    var options = new ParallelOptions { MaxDegreeOfParallelism = 4 };
    
    await Task.Run(() =>
    {
        Parallel.ForEach(fileList, options, file =>
        {
            try
            {
                var result = RemoveWatermarks(file, GetOutputPath(file));
                
                lock (report)
                {
                    report.SuccessCount++;
                    report.TotalWatermarksRemoved += result.WatermarksRemoved;
                }
            }
            catch (Exception ex)
            {
                lock (report)
                {
                    report.FailureCount++;
                    report.Errors.Add($"{file}: {ex.Message}");
                }
            }
            finally
            {
                Interlocked.Increment(ref completed);
                progress?.Report((int)((completed / (double)total) * 100));
            }
        });
    });
    
    return report;
}

// Usage with progress bar
var progress = new Progress<int>(percent => 
{
    Console.Write($"\rProgress: {percent}% complete");
});

var report = await ProcessBatchWithProgress(files, progress);
Console.WriteLine($"\nProcessed {report.SuccessCount} files successfully");

3. Caching Search Criteria for Better Performance

When processing many files with the same watermarks, cache the criteria:

public class WatermarkRemovalCache
{
    private static readonly ConcurrentDictionary<string, SearchCriteria> _criteriaCache 
        = new ConcurrentDictionary<string, SearchCriteria>();
    
    public static SearchCriteria GetOrCreateCriteria(string watermarkText, string imagePath = null)
    {
        string cacheKey = $"{watermarkText}_{imagePath}";
        
        return _criteriaCache.GetOrAdd(cacheKey, key =>
        {
            SearchCriteria textCriteria = new TextSearchCriteria(watermarkText);
            
            if (!string.IsNullOrEmpty(imagePath) && File.Exists(imagePath))
            {
                var imageCriteria = new ImageDctHashSearchCriteria(imagePath);
                return textCriteria.Or(imageCriteria);
            }
            
            return textCriteria;
        });
    }
}

// Usage - much faster for batch processing
var criteria = WatermarkRemovalCache.GetOrCreateCriteria("DRAFT", "logo.png");
foreach (var file in files)
{
    // Reuses cached criteria instead of recreating
    using (var watermarker = new Watermarker(file, new DiagramLoadOptions()))
    {
        // Process with cached criteria...
    }
}

4. Smart Output Naming Convention

Automatically generate organized output filenames:

public string GenerateSmartOutputPath(string inputPath, string operation = "cleaned")
{
    string directory = Path.GetDirectoryName(inputPath);
    string fileNameWithoutExt = Path.GetFileNameWithoutExtension(inputPath);
    string extension = Path.GetExtension(inputPath);
    string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
    
    // Creates: "original_diagram_cleaned_20250102_143022.vsdx"
    return Path.Combine(
        directory, 
        "processed",
        $"{fileNameWithoutExt}_{operation}_{timestamp}{extension}"
    );
}

5. Differential Processing (Only Modified Files)

Skip files that haven’t changed since last processing:

public class DifferentialProcessor
{
    private readonly string _stateFile = "processing_state.json";
    private Dictionary<string, DateTime> _lastProcessed;
    
    public DifferentialProcessor()
    {
        _lastProcessed = LoadState();
    }
    
    public async Task ProcessOnlyNewOrModified(IEnumerable<string> files)
    {
        var filesToProcess = files.Where(f =>
        {
            var lastWriteTime = File.GetLastWriteTimeUtc(f);
            
            return !_lastProcessed.ContainsKey(f) || 
                   _lastProcessed[f] < lastWriteTime;
        }).ToList();
        
        Console.WriteLine($"Processing {filesToProcess.Count} of {files.Count()} files (others unchanged)");
        
        foreach (var file in filesToProcess)
        {
            await ProcessSingleDiagramAsync(file);
            _lastProcessed[file] = DateTime.UtcNow;
        }
        
        SaveState();
    }
    
    private Dictionary<string, DateTime> LoadState()
    {
        if (!File.Exists(_stateFile))
            return new Dictionary<string, DateTime>();
        
        var json = File.ReadAllText(_stateFile);
        return JsonConvert.DeserializeObject<Dictionary<string, DateTime>>(json);
    }
    
    private void SaveState()
    {
        var json = JsonConvert.SerializeObject(_lastProcessed, Formatting.Indented);
        File.WriteAllText(_stateFile, json);
    }
}

Conclusion

You now have everything you need to confidently remove watermarks from diagram files using GroupDocs.Watermark for .NET. Whether you’re processing a handful of files or automating watermark removal for thousands of diagrams, you’ve learned:

  • How to load and search diagram files efficiently
  • Multiple ways to find specific watermarks (text, image, or both)
  • Best practices for removing watermarks safely in production
  • Troubleshooting techniques for common issues
  • Performance optimization strategies for batch processing

Next Steps to Master This:

  1. Start with a simple test file—practice loading and searching before removing anything
  2. Experiment with different search criteria to understand what gets detected
  3. Build a small utility tool for your team to batch-process files
  4. Integrate watermark removal into your existing document workflow
  5. Explore the GroupDocs documentation for advanced features like adding custom watermarks

Remember: always test on copies first, validate your output, and keep backups of original files. Programmatic watermark removal is powerful, but it’s worth double-checking results before deploying to production.

FAQ Section

1. How do I add watermarks instead of removing them?

Use the Add method on the Watermarker object. Here’s a quick example:

using (Watermarker watermarker = new Watermarker(inputPath, new DiagramLoadOptions()))
{
    TextWatermark watermark = new TextWatermark("CONFIDENTIAL", new Font("Arial", 36));
    watermark.ForegroundColor = Color.Red;
    watermark.Opacity = 0.5;
    
    watermarker.Add(watermark);
    watermarker.Save(outputPath);
}

2. Can I search for multiple text criteria simultaneously?

Absolutely! Combine criteria using .Or() or .And() operators:

var draft = new TextSearchCriteria("DRAFT");
var review = new TextSearchCriteria("REVIEW");
var confidential = new TextSearchCriteria("CONFIDENTIAL");

var combinedCriteria = draft.Or(review).Or(confidential);
var watermarks = page.Search(combinedCriteria);

3. Is it possible to target a different page than the first one?

Yes, pages are zero-indexed. Access any page through the Pages collection:

var content = watermarker.GetContent<DiagramContent>();

// Process page 3 (index 2)
var page3Watermarks = content.Pages[2].Search(criteria);

// Or process all pages
for (int i = 0; i < content.Pages.Count; i++)
{
    var watermarks = content.Pages[i].Search(criteria);
    watermarks.Clear();
}

4. What if I encounter performance issues with large files?

Try these optimization techniques:

  • Process only specific pages instead of all pages
  • Use parallel processing for batches (limit to 4-6 concurrent operations)
  • Ensure you’re using using statements to release resources promptly
  • Consider breaking very large files into smaller chunks
  • Cache search criteria objects when processing multiple files

See the Performance Considerations section above for detailed code examples.

5. How do I handle errors during watermark operations?

Wrap operations in try-catch blocks and return structured results:

try
{
    using (Watermarker watermarker = new Watermarker(inputPath, new DiagramLoadOptions()))
    {
        // Your operations
    }
}
catch (IOException ex)
{
    // File access issues
    Console.WriteLine($"File error: {ex.Message}");
}
catch (InvalidOperationException ex)
{
    // Invalid diagram format
    Console.WriteLine($"Invalid file: {ex.Message}");
}
catch (Exception ex)
{
    // Unexpected errors
    Console.WriteLine($"Unexpected error: {ex.Message}");
    // Log for debugging
}

6. Can I remove watermarks from password-protected diagrams?

No, you’ll need to remove password protection first. GroupDocs.Watermark doesn’t support opening encrypted/password-protected files directly.

7. What diagram formats are supported besides VSDX?

GroupDocs.Watermark supports: VSDX, VSD, VSS, VST, VSX, VTX, VSSX, VSTX, VSDM, VSSM, and VSTM. Basically, all modern and legacy Visio formats.

8. How accurate is image watermark detection?

Very accurate for exact matches. The ImageDctHashSearchCriteria uses perceptual hashing, which can handle minor variations (slight resizing, compression), but significant modifications might not be detected. For best results, use the original watermark image.

Resources

Documentation & Downloads

Support & Licensing