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)
- Right-click your project → Manage NuGet Packages
- Search for “GroupDocs.Watermark”
- 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 operationsContents.Diagram: Diagram-specific content handlingOptions.Diagram: Configuration options for diagram processingSearch: Watermark detection functionalitySearchCriteria: 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
ImageDctHashSearchCriteriauses 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
usingstatement 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:
- Start with a simple test file—practice loading and searching before removing anything
- Experiment with different search criteria to understand what gets detected
- Build a small utility tool for your team to batch-process files
- Integrate watermark removal into your existing document workflow
- 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
usingstatements 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
- Complete Documentation - Full API reference and guides
- API Reference - Detailed class and method documentation
- Download Library - Latest releases and older versions
Support & Licensing
- Free Support Forum - Community help and official support
- Temporary License - 30-day full-featured trial
- Purchase Options - Licensing for production use