Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 44 additions & 47 deletions compare-pdf/AGENTS.md

Large diffs are not rendered by default.

138 changes: 66 additions & 72 deletions compare-pdf/batch-pdf-comparison-parallel.cs
Original file line number Diff line number Diff line change
@@ -1,99 +1,93 @@
using System;
using System.IO;
using System.Collections.Generic;
using System.Threading;
using System.Collections.Concurrent;
using System.Threading.Tasks;
using Aspose.Pdf;
using Aspose.Pdf.Comparison;
using Aspose.Pdf.Text;

class BatchPdfComparer
class PdfBatchComparer
{
static void Main()
// Compare two PDFs: page count and full text content
private static bool ComparePdf(string referencePath, string targetPath)
{
// Base directory of the application (works for both console and VS debugging)
string baseDir = AppDomain.CurrentDomain.BaseDirectory;
// Ensure both files exist
if (!File.Exists(referencePath) || !File.Exists(targetPath))
return false;

// Path to the reference PDF against which all others will be compared
string referencePath = Path.Combine(baseDir, "reference.pdf");
if (!File.Exists(referencePath))
// Load reference PDF
using (Document refDoc = new Document(referencePath))
// Load target PDF
using (Document tgtDoc = new Document(targetPath))
{
Console.Error.WriteLine($"Reference PDF not found at '{referencePath}'. Execution stopped.");
return;
// Compare page counts (1‑based indexing)
if (refDoc.Pages.Count != tgtDoc.Pages.Count)
return false;

// Extract full text from reference PDF
TextAbsorber refAbsorber = new TextAbsorber();
refDoc.Pages.Accept(refAbsorber);
string refText = refAbsorber.Text;

// Extract full text from target PDF
TextAbsorber tgtAbsorber = new TextAbsorber();
tgtDoc.Pages.Accept(tgtAbsorber);
string tgtText = tgtAbsorber.Text;

// Compare text content (exact match)
return string.Equals(refText, tgtText, StringComparison.Ordinal);
}
}

static void Main()
{
// Path to the reference PDF against which all others are compared
const string referencePdfPath = "reference.pdf";

// Directory containing PDFs to compare
const string pdfDirectory = "PdfBatch";

// Directory containing the PDFs to be compared
string inputDir = Path.Combine(baseDir, "InputPdfs");
if (!Directory.Exists(inputDir))
if (!File.Exists(referencePdfPath))
{
Console.WriteLine($"Input directory '{inputDir}' not found. Falling back to current working directory.");
inputDir = Directory.GetCurrentDirectory();
Console.Error.WriteLine($"Reference PDF not found: {referencePdfPath}");
return;
}
string[] pdfFiles = Directory.GetFiles(inputDir, "*.pdf");
if (pdfFiles.Length == 0)

if (!Directory.Exists(pdfDirectory))
{
Console.WriteLine($"No PDF files found in '{inputDir}'. Execution stopped.");
Console.Error.WriteLine($"Directory not found: {pdfDirectory}");
return;
}

// Directory where comparison result PDFs will be saved
string outputDir = Path.Combine(baseDir, "ComparisonResults");
Directory.CreateDirectory(outputDir);

// Maximum number of concurrent comparisons (adjust based on memory constraints)
const int maxConcurrency = 4;
SemaphoreSlim semaphore = new SemaphoreSlim(maxConcurrency);
// Gather all PDF files in the directory (non‑recursive)
string[] pdfFiles = Directory.GetFiles(pdfDirectory, "*.pdf", SearchOption.TopDirectoryOnly);

var tasks = new List<Task>();
// Thread‑safe collection for results
ConcurrentBag<string> results = new ConcurrentBag<string>();

foreach (string file in pdfFiles)
// Limit concurrency to avoid excessive memory usage
ParallelOptions options = new ParallelOptions
{
// Skip the reference file itself if it appears in the list
if (string.Equals(Path.GetFullPath(file), Path.GetFullPath(referencePath), StringComparison.OrdinalIgnoreCase))
continue;

// Queue a comparison task
tasks.Add(Task.Run(async () =>
{
await semaphore.WaitAsync();
try
{
try
{
CompareAndSave(referencePath, file, outputDir);
}
catch (Exception ex)
{
Console.Error.WriteLine($"Error comparing '{Path.GetFileName(file)}': {ex.Message}");
}
}
finally
{
semaphore.Release();
}
}));
}
MaxDegreeOfParallelism = Math.Max(1, Environment.ProcessorCount / 2) // adjust as needed
};

// Wait for all comparisons to finish
Task.WaitAll(tasks.ToArray());

Console.WriteLine("Batch PDF comparison completed.");
}

static void CompareAndSave(string referencePath, string targetPath, string outputDir)
{
// Load the reference and target PDFs using Aspose.Pdf's Document class
using (Document referenceDoc = new Document(referencePath))
using (Document targetDoc = new Document(targetPath))
// Compare each PDF to the reference in parallel
Parallel.ForEach(pdfFiles, options, pdfPath =>
{
// Default comparison options; customize if needed
ComparisonOptions options = new ComparisonOptions();
bool isMatch = ComparePdf(referencePdfPath, pdfPath);
string fileName = Path.GetFileName(pdfPath);
string result = isMatch
? $"{fileName}: MATCH"
: $"{fileName}: DIFFERENT";

// Construct a result file name that identifies the compared pair
string resultFileName = $"{Path.GetFileNameWithoutExtension(targetPath)}_vs_{Path.GetFileNameWithoutExtension(referencePath)}.pdf";
string resultPath = Path.Combine(outputDir, resultFileName);
results.Add(result);
});

// Perform side‑by‑side comparison and let the API write the result PDF
SideBySidePdfComparer.Compare(referenceDoc, targetDoc, resultPath, new SideBySideComparisonOptions { });
// Output results
Console.WriteLine("Comparison results:");
foreach (string line in results)
{
Console.WriteLine(line);
}
}
}
}
69 changes: 43 additions & 26 deletions compare-pdf/compare-encrypted-pdfs-side-by-side.cs
Original file line number Diff line number Diff line change
@@ -1,44 +1,61 @@
using System;
using System.IO;
using System.Linq;
using Aspose.Pdf;
using Aspose.Pdf.Comparison;

class Program
{
static void Main()
{
// Paths to the encrypted PDF files
const string firstPdfPath = "encrypted1.pdf";
const string secondPdfPath = "encrypted2.pdf";
// Passwords for the encrypted PDFs (user passwords)
const string firstPassword = "userPass1";
const string secondPassword = "userPass2";
// Path where the comparison result will be saved
const string resultPdfPath = "comparison_result.pdf";
const string pdfPath1 = "encrypted1.pdf";
const string pdfPath2 = "encrypted2.pdf";
const string password1 = "userPass1";
const string password2 = "userPass2";

// Verify that input files exist
if (!File.Exists(firstPdfPath))
if (!File.Exists(pdfPath1) || !File.Exists(pdfPath2))
{
Console.Error.WriteLine($"File not found: {firstPdfPath}");
return;
}
if (!File.Exists(secondPdfPath))
{
Console.Error.WriteLine($"File not found: {secondPdfPath}");
Console.Error.WriteLine("One or both PDF files not found.");
return;
}

// Load the encrypted documents by providing the passwords to the constructors
using (Document doc1 = new Document(firstPdfPath, firstPassword))
using (Document doc2 = new Document(secondPdfPath, secondPassword))
try
{
// Create comparison options (default settings)
SideBySideComparisonOptions options = new SideBySideComparisonOptions();
// Load the encrypted PDFs by providing passwords to the constructors
using (Document doc1 = new Document(pdfPath1, password1))
using (Document doc2 = new Document(pdfPath2, password2))
{
// ---------- Text‑based comparison (gives diff operations) ----------
var diffs = TextPdfComparer.CompareDocumentsPageByPage(
doc1,
doc2,
new ComparisonOptions()
);

// Perform side‑by‑side comparison and save the result PDF
SideBySidePdfComparer.Compare(doc1, doc2, resultPdfPath, options);
}
bool isEqual = diffs.All(pageDiff => pageDiff.Count == 0);
int differencesCount = diffs.Sum(pageDiff => pageDiff.Count);

Console.WriteLine($"Documents are equal: {isEqual}");
Console.WriteLine($"Number of differences: {differencesCount}");

Console.WriteLine($"Comparison completed. Result saved to '{resultPdfPath}'.");
// ---------- Visual side‑by‑side comparison (produces a PDF) ----------
const string visualDiffPath = "comparison_result.pdf";
SideBySidePdfComparer.Compare(
doc1,
doc2,
visualDiffPath,
new SideBySideComparisonOptions()
);
Console.WriteLine($"Visual comparison saved to '{visualDiffPath}'.");
}
}
catch (InvalidPasswordException ex)
{
Console.Error.WriteLine($"Invalid password: {ex.Message}");
}
catch (Exception ex)
{
Console.Error.WriteLine($"Error: {ex.Message}");
}
}
}
}
73 changes: 55 additions & 18 deletions compare-pdf/compare-pdf-documents-page-by-page-diff.cs
Original file line number Diff line number Diff line change
@@ -1,41 +1,78 @@
using System;
using System.IO;
using Aspose.Pdf;
using Aspose.Pdf.Comparison;
using Aspose.Pdf.Text;

class Program
{
static void Main()
{
const string firstPdfPath = "first.pdf";
const string secondPdfPath = "second.pdf";
const string diffPdfPath = "diff_output.pdf";
const string diffPdfPath = "diff.pdf";

// Verify that both source files exist
if (!File.Exists(firstPdfPath) || !File.Exists(secondPdfPath))
{
Console.Error.WriteLine("One or both input PDF files were not found.");
Console.Error.WriteLine("One or both input files are missing.");
return;
}

try
// Load the two source documents inside using blocks for deterministic disposal.
using (Document doc1 = new Document(firstPdfPath))
using (Document doc2 = new Document(secondPdfPath))
{
// Load the two documents inside using blocks for deterministic disposal
using (Document doc1 = new Document(firstPdfPath))
using (Document doc2 = new Document(secondPdfPath))
// Determine the larger page count to cover all pages.
int maxPages = Math.Max(doc1.Pages.Count, doc2.Pages.Count);

// Create a new document that will hold the differences.
using (Document diffDoc = new Document())
{
// Default comparison options
ComparisonOptions options = new ComparisonOptions();
// Iterate pages using 1‑based indexing (Aspose.Pdf requirement).
for (int pageIndex = 1; pageIndex <= maxPages; pageIndex++)
{
string text1 = ExtractPageText(doc1, pageIndex);
string text2 = ExtractPageText(doc2, pageIndex);

// Perform page‑by‑page comparison and save the diff PDF
TextPdfComparer.CompareDocumentsPageByPage(doc1, doc2, options, diffPdfPath);
}
// If the texts are different (or one page is missing), record the diff.
if (!string.Equals(text1, text2, StringComparison.Ordinal))
{
// Add a new blank page to the diff document.
Page diffPage = diffDoc.Pages.Add();

Console.WriteLine($"Comparison diff PDF saved to '{diffPdfPath}'.");
}
catch (Exception ex)
{
Console.Error.WriteLine($"Error during comparison: {ex.Message}");
// Prepare a description of the difference.
string diffInfo = $"Page {pageIndex} differs.\n\n" +
$"--- {Path.GetFileName(firstPdfPath)} ---\n{text1}\n\n" +
$"--- {Path.GetFileName(secondPdfPath)} ---\n{text2}";

// Add the description as a text fragment.
TextFragment tf = new TextFragment(diffInfo)
{
// Position the text near the top‑left corner.
Position = new Position(50, diffPage.PageInfo.Height - 50),
// Use a readable font size.
TextState = { FontSize = 12 }
};
diffPage.Paragraphs.Add(tf);
}
}

// Save the diff document. No SaveOptions are needed because the output is PDF.
diffDoc.Save(diffPdfPath);
Console.WriteLine($"Diff PDF saved to '{diffPdfPath}'.");
}
}
}

// Helper method to extract text from a specific page.
// Returns an empty string if the page does not exist.
private static string ExtractPageText(Document doc, int pageNumber)
{
if (pageNumber > doc.Pages.Count)
return string.Empty;

TextAbsorber absorber = new TextAbsorber();
// Extract text from the specified page only.
doc.Pages[pageNumber].Accept(absorber);
return absorber.Text ?? string.Empty;
}
}
Loading
Loading