diff --git a/working-with-attachments/add-attachment-to-protected-pdf.cs b/working-with-attachments/add-attachment-to-protected-pdf.cs new file mode 100644 index 00000000..c4673090 --- /dev/null +++ b/working-with-attachments/add-attachment-to-protected-pdf.cs @@ -0,0 +1,43 @@ +using System; +using System.IO; +using Aspose.Pdf; + +class Program +{ + static void Main() + { + // Paths and credentials + const string inputPdf = "protected.pdf"; + const string outputPdf = "protected_with_attachment.pdf"; + const string userPassword = "user123"; + const string attachmentFile = "attachment.txt"; + const string attachmentDescription = "Sample attachment"; + + // Validate input files + if (!File.Exists(inputPdf)) + { + Console.Error.WriteLine($"Input PDF not found: {inputPdf}"); + return; + } + if (!File.Exists(attachmentFile)) + { + Console.Error.WriteLine($"Attachment file not found: {attachmentFile}"); + return; + } + + // Open the encrypted PDF using the correct password + using (Document doc = new Document(inputPdf, userPassword)) + { + // Create a FileSpecification for the attachment + FileSpecification fileSpec = new FileSpecification(attachmentFile, attachmentDescription); + + // Add the attachment to the document's EmbeddedFiles collection + doc.EmbeddedFiles.Add(fileSpec); + + // Save the updated PDF (encryption is preserved) + doc.Save(outputPdf); + } + + Console.WriteLine($"Attachment added and saved to '{outputPdf}'."); + } +} \ No newline at end of file diff --git a/working-with-attachments/add-attachments-to-pdf-save-output.cs b/working-with-attachments/add-attachments-to-pdf-save-output.cs new file mode 100644 index 00000000..ecbb75e5 --- /dev/null +++ b/working-with-attachments/add-attachments-to-pdf-save-output.cs @@ -0,0 +1,54 @@ +using System; +using System.IO; +using Aspose.Pdf; + +class Program +{ + static void Main() + { + // Input PDF file + const string inputPdfPath = "input.pdf"; + + // Directory where the output PDF will be saved + const string outputDirectory = "output"; + + // Files to be added as attachments to the PDF + string[] attachmentFiles = { "attach1.txt", "attach2.jpg" }; + + // Ensure the input PDF exists + if (!File.Exists(inputPdfPath)) + { + Console.Error.WriteLine($"Input PDF not found: {inputPdfPath}"); + return; + } + + // Load the PDF document (using statement ensures proper disposal) + using (Document pdfDocument = new Document(inputPdfPath)) + { + // Add each existing file as an embedded file (attachment) + foreach (string filePath in attachmentFiles) + { + if (!File.Exists(filePath)) + { + Console.Error.WriteLine($"Attachment file not found: {filePath}"); + continue; + } + + // Create a FileSpecification for the attachment and add it to the document's EmbeddedFiles collection + FileSpecification attachment = new FileSpecification(filePath); + pdfDocument.EmbeddedFiles.Add(attachment); + } + + // Ensure the output directory exists + Directory.CreateDirectory(outputDirectory); + + // Build the full output file path + string outputPdfPath = Path.Combine(outputDirectory, "output_with_attachments.pdf"); + + // Save the modified PDF to the specified location + pdfDocument.Save(outputPdfPath); + } + + Console.WriteLine("PDF saved with attachments."); + } +} diff --git a/working-with-attachments/add-file-attachment-to-pdf.cs b/working-with-attachments/add-file-attachment-to-pdf.cs new file mode 100644 index 00000000..c0a7607f --- /dev/null +++ b/working-with-attachments/add-file-attachment-to-pdf.cs @@ -0,0 +1,58 @@ +using System; +using System.IO; +using Aspose.Pdf; +using Aspose.Pdf.Annotations; + +class Program +{ + static void Main() + { + const string inputPdf = "input.pdf"; + const string outputPdf = "output.pdf"; + const string attachmentFile = "attachment.txt"; + + if (!File.Exists(inputPdf)) + { + Console.Error.WriteLine($"Input PDF not found: {inputPdf}"); + return; + } + + if (!File.Exists(attachmentFile)) + { + Console.Error.WriteLine($"Attachment file not found: {attachmentFile}"); + return; + } + + // Load the existing PDF document + using (Document doc = new Document(inputPdf)) + { + // Create a FileSpecification for the attachment + FileSpecification fileSpec = new FileSpecification(attachmentFile); + + // Add the file specification to the document's embedded files collection + doc.EmbeddedFiles.Add(fileSpec); + + // Select the page where the annotation will be placed (first page) + Page page = doc.Pages[1]; + + // Define the rectangle that bounds the annotation + Aspose.Pdf.Rectangle rect = new Aspose.Pdf.Rectangle(100, 500, 200, 550); + + // Create the file attachment annotation using the page, rectangle, and file specification + FileAttachmentAnnotation attachment = new FileAttachmentAnnotation(page, rect, fileSpec) + { + // Optional visual properties + Contents = "Attached file", + Color = Aspose.Pdf.Color.Blue + }; + + // Add the annotation to the page's annotation collection + page.Annotations.Add(attachment); + + // Save the modified PDF to a new file + doc.Save(outputPdf); + } + + Console.WriteLine($"File attachment added and saved to '{outputPdf}'."); + } +} \ No newline at end of file diff --git a/working-with-attachments/add-file-attachment-to-pdf__v2.cs b/working-with-attachments/add-file-attachment-to-pdf__v2.cs new file mode 100644 index 00000000..d13cb15b --- /dev/null +++ b/working-with-attachments/add-file-attachment-to-pdf__v2.cs @@ -0,0 +1,76 @@ +using System; +using System.IO; +using Aspose.Pdf; +using Aspose.Pdf.Annotations; + +class Program +{ + static void Main() + { + const string pdfPath = "input.pdf"; + const string attachmentPath = "attachment.txt"; + const string outputPath = "output.pdf"; + + // Verify the source PDF exists + if (!File.Exists(pdfPath)) + { + Console.Error.WriteLine($"PDF file not found: {pdfPath}"); + return; + } + + try + { + // Load the PDF (lifecycle: using block ensures disposal) + using (Document doc = new Document(pdfPath)) + { + // Use the first page for the attachment annotation + Page page = doc.Pages[1]; + + // Define the rectangle for the attachment icon (fully qualified to avoid ambiguity) + Aspose.Pdf.Rectangle rect = new Aspose.Pdf.Rectangle(100, 500, 120, 520); + + // Check that the file to be attached exists + if (!File.Exists(attachmentPath)) + { + Console.Error.WriteLine($"Attachment file not found: {attachmentPath}"); + } + else + { + // Create a file specification for the attachment + FileSpecification fileSpec = new FileSpecification(attachmentPath); + + // Create the file attachment annotation + FileAttachmentAnnotation attachment = new FileAttachmentAnnotation(page, rect, fileSpec) + { + // Use the correct enum for the icon + Icon = FileIcon.Paperclip, + Contents = "Attached file", + Title = "Attachment" + }; + + // Add the annotation to the page + page.Annotations.Add(attachment); + } + + // Save the modified PDF (lifecycle: save inside using block) + doc.Save(outputPath); + Console.WriteLine($"PDF saved to '{outputPath}'."); + } + } + catch (FileNotFoundException ex) + { + // Handles missing files for both PDF and attachment + Console.Error.WriteLine($"File not found: {ex.FileName}"); + } + catch (PdfException ex) + { + // Handles errors specific to Aspose.Pdf operations + Console.Error.WriteLine($"PDF error: {ex.Message}"); + } + catch (Exception ex) + { + // Catch‑all for any other unexpected errors + Console.Error.WriteLine($"Unexpected error: {ex.Message}"); + } + } +} diff --git a/working-with-attachments/add-file-attachment-with-retry.cs b/working-with-attachments/add-file-attachment-with-retry.cs new file mode 100644 index 00000000..0bb868f5 --- /dev/null +++ b/working-with-attachments/add-file-attachment-with-retry.cs @@ -0,0 +1,98 @@ +using System; +using System.IO; +using System.Threading; +using Aspose.Pdf; +using Aspose.Pdf.Annotations; + +class PdfAttachmentHelper +{ + // Adds a file attachment to the first page of a PDF. + // Retries the whole operation if a transient I/O error occurs. + public static void AddAttachmentWithRetry( + string pdfPath, + string attachmentFilePath, + string outputPath, + int maxRetries = 3, + int initialDelayMs = 500) + { + if (!File.Exists(pdfPath)) + throw new FileNotFoundException($"PDF not found: {pdfPath}"); + + if (!File.Exists(attachmentFilePath)) + throw new FileNotFoundException($"Attachment not found: {attachmentFilePath}"); + + int attempt = 0; + int delay = initialDelayMs; + + while (true) + { + try + { + // Load the PDF (lifecycle rule: use Document constructor) + using (Document doc = new Document(pdfPath)) + { + // Create a file specification for the attachment + FileSpecification fileSpec = new FileSpecification(attachmentFilePath); + + // Define the rectangle where the attachment icon will appear + Aspose.Pdf.Rectangle rect = new Aspose.Pdf.Rectangle(100, 100, 200, 200); + + // Get the first page (Aspose.Pdf uses 1‑based indexing) + Page page = doc.Pages[1]; + + // Create the attachment annotation and add it to the page + FileAttachmentAnnotation attachment = new FileAttachmentAnnotation(page, rect, fileSpec); + page.Annotations.Add(attachment); + + // Save the modified PDF (lifecycle rule: use Document.Save) + doc.Save(outputPath); + } + + // If we reach this point the operation succeeded + break; + } + catch (IOException ex) when (attempt < maxRetries) + { + // Transient I/O error – wait and retry + attempt++; + Console.Error.WriteLine($"I/O error on attempt {attempt}: {ex.Message}. Retrying in {delay} ms..."); + Thread.Sleep(delay); + delay *= 2; // exponential back‑off + } + catch (UnauthorizedAccessException ex) when (attempt < maxRetries) + { + // Access issue – also retry (e.g., temporary lock on network share) + attempt++; + Console.Error.WriteLine($"Access error on attempt {attempt}: {ex.Message}. Retrying in {delay} ms..."); + Thread.Sleep(delay); + delay *= 2; + } + catch + { + // Non‑recoverable exception – rethrow + throw; + } + } + } +} + +// Example usage +class Program +{ + static void Main() + { + const string networkPdf = @"\\fileserver\share\documents\sample.pdf"; + const string attachment = @"C:\Temp\info.txt"; + const string outputPdf = @"\\fileserver\share\documents\sample_with_attachment.pdf"; + + try + { + PdfAttachmentHelper.AddAttachmentWithRetry(networkPdf, attachment, outputPdf); + Console.WriteLine("Attachment added successfully."); + } + catch (Exception ex) + { + Console.Error.WriteLine($"Failed to add attachment: {ex.Message}"); + } + } +} \ No newline at end of file diff --git a/working-with-attachments/add-in-memory-attachment-to-pdf.cs b/working-with-attachments/add-in-memory-attachment-to-pdf.cs new file mode 100644 index 00000000..805f9bed --- /dev/null +++ b/working-with-attachments/add-in-memory-attachment-to-pdf.cs @@ -0,0 +1,47 @@ +using System; +using System.IO; +using Aspose.Pdf; // Core PDF API +using Aspose.Pdf.Tagged; // For ITaggedContent if needed (not used here) + +class Program +{ + static void Main() + { + // Path to the source PDF (must exist) + const string inputPdfPath = "input.pdf"; + // Path where the resulting PDF with the attachment will be saved + const string outputPdfPath = "output_with_attachment.pdf"; + + if (!File.Exists(inputPdfPath)) + { + Console.Error.WriteLine($"Source PDF not found: {inputPdfPath}"); + return; + } + + // Create a memory stream containing the data to be attached. + // Here we embed a simple text file; replace the content as needed. + byte[] attachmentData = System.Text.Encoding.UTF8.GetBytes("This is the content of the in‑memory attachment."); + using (MemoryStream attachmentStream = new MemoryStream(attachmentData)) + { + // Ensure the stream position is at the beginning before using it. + attachmentStream.Position = 0; + + // Open the existing PDF inside a using block for deterministic disposal. + using (Document doc = new Document(inputPdfPath)) + { + // The FileSpecification constructor expects (Stream, string). + // The first argument is the stream containing the file data, + // the second argument is the name that will appear in the PDF attachment list. + FileSpecification fileSpec = new FileSpecification(attachmentStream, "SampleAttachment.txt"); + + // Add the file specification to the document's embedded files collection. + doc.EmbeddedFiles.Add(fileSpec); + + // Save the modified PDF to the desired output path. + doc.Save(outputPdfPath); + } + } + + Console.WriteLine($"PDF saved with in‑memory attachment: {outputPdfPath}"); + } +} diff --git a/working-with-attachments/add-multiple-attachments-to-pdf.cs b/working-with-attachments/add-multiple-attachments-to-pdf.cs new file mode 100644 index 00000000..071a84c3 --- /dev/null +++ b/working-with-attachments/add-multiple-attachments-to-pdf.cs @@ -0,0 +1,57 @@ +using System; +using System.IO; +using System.Collections.Generic; +using Aspose.Pdf; + +class Program +{ + static void Main() + { + // Input PDF to which attachments will be added + const string inputPdf = "input.pdf"; + // Output PDF containing the attachments + const string outputPdf = "output_with_attachments.pdf"; + + // Collection of file paths to attach + List attachmentPaths = new List + { + "file1.txt", + "image.png", + "document.docx" + }; + + // Verify the source PDF exists + if (!File.Exists(inputPdf)) + { + Console.Error.WriteLine($"Input PDF not found: {inputPdf}"); + return; + } + + // Verify each attachment file exists + foreach (string path in attachmentPaths) + { + if (!File.Exists(path)) + { + Console.Error.WriteLine($"Attachment not found: {path}"); + return; + } + } + + // Load the PDF, add attachments, and save + using (Document doc = new Document(inputPdf)) + { + foreach (string path in attachmentPaths) + { + // Create a file specification for the attachment + FileSpecification fileSpec = new FileSpecification(path); + // Add the specification to the document's embedded files collection + doc.EmbeddedFiles.Add(fileSpec); + } + + // Save the modified PDF + doc.Save(outputPdf); + } + + Console.WriteLine($"PDF saved with attachments to '{outputPdf}'."); + } +} \ No newline at end of file diff --git a/working-with-attachments/add-multiple-files-to-pdf-portfolio.cs b/working-with-attachments/add-multiple-files-to-pdf-portfolio.cs new file mode 100644 index 00000000..2401f80f --- /dev/null +++ b/working-with-attachments/add-multiple-files-to-pdf-portfolio.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Aspose.Pdf; // Core PDF API +using Aspose.Pdf.Facades; // For advanced features if needed (not used here) + +class PortfolioExample +{ + static void Main() + { + // List of file paths of various types to embed in the PDF portfolio + List filesToAdd = new List + { + "sample.pdf", + "image.png", + "document.docx", + "notes.txt" + }; + + // Create a new PDF document that will act as the portfolio container + using (Document portfolioDoc = new Document()) + { + // Ensure the EmbeddedFiles collection exists (it does by default) + foreach (string filePath in filesToAdd) + { + // Skip missing files to avoid runtime errors + if (!File.Exists(filePath)) + continue; + + // Create a file specification for the current file + FileSpecification fileSpec = new FileSpecification(filePath); + + // Add the file specification to the document's embedded files collection + portfolioDoc.EmbeddedFiles.Add(fileSpec); + } + + // Save the resulting PDF portfolio + portfolioDoc.Save("PortfolioOutput.pdf"); + } + + Console.WriteLine("Portfolio PDF created successfully."); + } +} \ No newline at end of file diff --git a/working-with-attachments/add-nested-pdf-to-portfolio.cs b/working-with-attachments/add-nested-pdf-to-portfolio.cs new file mode 100644 index 00000000..ee40730f --- /dev/null +++ b/working-with-attachments/add-nested-pdf-to-portfolio.cs @@ -0,0 +1,42 @@ +using System; +using System.IO; +using Aspose.Pdf; + +class Program +{ + static void Main() + { + const string portfolioPath = "portfolio.pdf"; // existing PDF Portfolio + const string fileToAdd = "nested.pdf"; // PDF to embed as a nested item + const string outputPath = "updated_portfolio.pdf"; + + // Verify that both source files exist + if (!File.Exists(portfolioPath)) + { + Console.Error.WriteLine($"Portfolio file not found: {portfolioPath}"); + return; + } + if (!File.Exists(fileToAdd)) + { + Console.Error.WriteLine($"File to embed not found: {fileToAdd}"); + return; + } + + // Load the existing portfolio PDF + using (Document doc = new Document(portfolioPath)) + { + // Create a file specification for the PDF to be embedded + FileSpecification fileSpec = new FileSpecification(fileToAdd); + fileSpec.Description = "Nested PDF document"; + + // Add the file specification to the portfolio's embedded files collection. + // Use the file name as the key. + doc.EmbeddedFiles.Add(Path.GetFileName(fileToAdd), fileSpec); + + // Save the updated portfolio + doc.Save(outputPath); + } + + Console.WriteLine($"Embedded '{fileToAdd}' into portfolio. Saved as '{outputPath}'."); + } +} \ No newline at end of file diff --git a/working-with-attachments/add-unicode-attachment-to-pdf.cs b/working-with-attachments/add-unicode-attachment-to-pdf.cs new file mode 100644 index 00000000..e0255f1f --- /dev/null +++ b/working-with-attachments/add-unicode-attachment-to-pdf.cs @@ -0,0 +1,64 @@ +using System; +using System.IO; +using Aspose.Pdf; +using Aspose.Pdf.Annotations; + +class Program +{ + static void Main() + { + const string inputPdfPath = "input.pdf"; // source PDF + const string attachmentPath = "attachment.bin"; // file to attach + const string outputPdfPath = "output_with_attachment.pdf"; + + // Ensure source files exist + if (!File.Exists(inputPdfPath)) + { + Console.Error.WriteLine($"Input PDF not found: {inputPdfPath}"); + return; + } + if (!File.Exists(attachmentPath)) + { + Console.Error.WriteLine($"Attachment file not found: {attachmentPath}"); + return; + } + + // Load the PDF document (lifecycle rule: use using) + using (Document doc = new Document(inputPdfPath)) + { + // Choose the page where the attachment annotation will appear + Page page = doc.Pages[1]; // 1‑based indexing + + // Define the rectangle for the annotation (fully qualified to avoid ambiguity) + Aspose.Pdf.Rectangle rect = new Aspose.Pdf.Rectangle(100, 500, 200, 600); + + // Create a file specification for the attachment + FileSpecification fileSpec = new FileSpecification(attachmentPath); + // Set a Unicode filename (e.g., Chinese characters) + fileSpec.UnicodeName = "文件附件.bin"; + + // Create the file attachment annotation + FileAttachmentAnnotation attachment = new FileAttachmentAnnotation(page, rect, fileSpec) + { + // Optional visual settings + Icon = FileIcon.Graph, // use enum type name, not instance reference + Color = Aspose.Pdf.Color.Blue, + Contents = "Embedded file with Unicode name" + }; + + // Add the annotation to the page + page.Annotations.Add(attachment); + + // Save the modified PDF (lifecycle rule: use Save) + doc.Save(outputPdfPath); + } + + Console.WriteLine($"PDF saved with attachment: {outputPdfPath}"); + // Simple verification: check that the annotation exists + using (Document verifyDoc = new Document(outputPdfPath)) + { + int count = verifyDoc.Pages[1].Annotations.Count; + Console.WriteLine($"Page 1 contains {count} annotation(s)."); + } + } +} diff --git a/working-with-attachments/add-word-document-to-pdf-portfolio.cs b/working-with-attachments/add-word-document-to-pdf-portfolio.cs new file mode 100644 index 00000000..16f2848a --- /dev/null +++ b/working-with-attachments/add-word-document-to-pdf-portfolio.cs @@ -0,0 +1,41 @@ +using System; +using System.IO; +using Aspose.Pdf; + +class Program +{ + static void Main() + { + const string wordPath = "sample.docx"; // input Word document + const string outputPdf = "portfolio.pdf"; // output PDF Portfolio + + if (!File.Exists(wordPath)) + { + Console.Error.WriteLine($"Word file not found: {wordPath}"); + return; + } + + // Create a new PDF document + using (Document pdfDoc = new Document()) + { + // Ensure the document has a collection (portfolio) initialized + if (pdfDoc.Collection == null) + pdfDoc.Collection = new Collection(); + + // Create a FileSpecification for the Word document + var fileSpec = new FileSpecification(Path.GetFileName(wordPath), "Embedded Word document") + { + // Load the file bytes into the specification + Contents = new MemoryStream(File.ReadAllBytes(wordPath)) + }; + + // Add the file specification to the PDF portfolio collection + pdfDoc.Collection.Add(fileSpec); + + // Save the PDF Portfolio + pdfDoc.Save(outputPdf); + } + + Console.WriteLine($"PDF Portfolio created: {outputPdf}"); + } +} diff --git a/working-with-attachments/agents.md b/working-with-attachments/agents.md new file mode 100644 index 00000000..10093174 --- /dev/null +++ b/working-with-attachments/agents.md @@ -0,0 +1,139 @@ +--- +name: working-with-attachments +description: C# examples for working-with-attachments using Aspose.PDF for .NET +language: csharp +framework: net10.0 +parent: ../agents.md +--- + +# AGENTS - working-with-attachments + +## Persona + +You are a C# developer specializing in PDF processing using Aspose.PDF for .NET, +working within the **working-with-attachments** category. +This folder contains standalone C# examples for working-with-attachments operations. +See the root [agents.md](../agents.md) for repository-wide conventions and boundaries. + +## Scope +- This folder contains examples for **working-with-attachments**. +- Files are standalone `.cs` examples stored directly in this folder. + +## Required Namespaces + +- `using Aspose.Pdf;` (49/50 files) ← category-specific +- `using Aspose.Pdf.Annotations;` (15/50 files) +- `using Aspose.Pdf.Text;` (2/50 files) +- `using Aspose.Pdf.Devices;` (1/50 files) +- `using Aspose.Pdf.Drawing;` (1/50 files) +- `using Aspose.Pdf.Facades;` (1/50 files) +- `using Aspose.Pdf.Optimization;` (1/50 files) +- `using Aspose.Pdf.Tagged;` (1/50 files) +- `using System;` (50/50 files) +- `using System.IO;` (49/50 files) +- `using System.Collections.Generic;` (6/50 files) +- `using System.Threading.Tasks;` (2/50 files) +- `using NUnit.Framework;` (1/50 files) +- `using System.Diagnostics;` (1/50 files) +- `using System.Drawing;` (1/50 files) +- `using System.IO.Compression;` (1/50 files) +- `using System.Linq;` (1/50 files) +- `using System.Net.Http;` (1/50 files) +- `using System.Reflection;` (1/50 files) +- `using System.Security.Cryptography;` (1/50 files) +- `using System.Text.Json;` (1/50 files) +- `using System.Threading;` (1/50 files) +- `using System.Xml.Linq;` (1/50 files) +- `using Xunit;` (1/50 files) + +## Common Code Pattern + +Most files follow this pattern: + +```csharp +using (Document doc = new Document("input.pdf")) +{ + // ... operations ... + doc.Save("output.pdf"); +} +``` + +## Files in this folder + +| File | Title | Key APIs | Description | +|------|-------|----------|-------------| +| [add-attachment-to-protected-pdf](./add-attachment-to-protected-pdf.cs) | Add Attachment to Password-Protected PDF | `Document`, `FileSpecification`, `EmbeddedFiles` | Shows how to open an encrypted PDF with a user password, embed a file attachment, and save the do... | +| [add-attachments-to-pdf-save-output](./add-attachments-to-pdf-save-output.cs) | Add Attachments to PDF and Save to Output Folder | `Document`, `FileSpecification`, `EmbeddedFiles` | Demonstrates how to embed files as attachments in a PDF document using Aspose.Pdf and save the mo... | +| [add-file-attachment-to-pdf](./add-file-attachment-to-pdf.cs) | Add File Attachment to PDF | `Document`, `FileSpecification`, `FileAttachmentAnnotation` | Demonstrates how to load an existing PDF, embed a file using FileSpecification, and create a file... | +| [add-file-attachment-to-pdf__v2](./add-file-attachment-to-pdf__v2.cs) | Add File Attachment to PDF with Error Handling | `Document`, `Page`, `Rectangle` | Demonstrates loading a PDF, checking for missing files, creating a file attachment annotation, an... | +| [add-file-attachment-with-retry](./add-file-attachment-with-retry.cs) | Add File Attachment to PDF with Retry | `Document`, `Page`, `FileSpecification` | Demonstrates how to attach a file to the first page of a PDF stored on a network share and retry ... | +| [add-in-memory-attachment-to-pdf](./add-in-memory-attachment-to-pdf.cs) | Add In-Memory Attachment to PDF | `Document`, `FileSpecification`, `Add` | Demonstrates embedding a file into an existing PDF using a MemoryStream, avoiding intermediate di... | +| [add-multiple-attachments-to-pdf](./add-multiple-attachments-to-pdf.cs) | Add Multiple Attachments to a PDF | `Document`, `FileSpecification`, `EmbeddedFiles` | Shows how to attach several files to an existing PDF by iterating a list of file paths and saving... | +| [add-multiple-files-to-pdf-portfolio](./add-multiple-files-to-pdf-portfolio.cs) | Add Multiple Files to a PDF Portfolio | `Document`, `FileSpecification`, `EmbeddedFiles` | Demonstrates embedding various file types into a single PDF portfolio using a loop with Aspose.Pdf. | +| [add-nested-pdf-to-portfolio](./add-nested-pdf-to-portfolio.cs) | Add Nested PDF to Existing PDF Portfolio | `Document`, `FileSpecification`, `EmbeddedFiles` | Demonstrates how to embed a PDF file as a nested item inside an existing PDF Portfolio using Aspo... | +| [add-unicode-attachment-to-pdf](./add-unicode-attachment-to-pdf.cs) | Add Unicode File Attachment to PDF | `Document`, `Page`, `Rectangle` | Demonstrates embedding a file with a Unicode filename as a file attachment annotation in a PDF us... | +| [add-word-document-to-pdf-portfolio](./add-word-document-to-pdf-portfolio.cs) | Add Word Document to PDF Portfolio | `Document`, `Collection`, `FileSpecification` | Shows how to embed a Word (.docx) file into a PDF portfolio by creating a FileSpecification and a... | +| [apply-custom-template-to-pdf-portfolio](./apply-custom-template-to-pdf-portfolio.cs) | Apply Custom Visual Template to PDF Portfolio | `Document`, `Page`, `Graph` | Loads an existing PDF portfolio, adds a semi‑transparent background rectangle and a header text t... | +| [attach-remote-file-to-pdf](./attach-remote-file-to-pdf.cs) | Attach Remote File to PDF as a File Annotation | `Document`, `Page`, `Rectangle` | Downloads a file from a remote URL into memory and embeds it in a PDF as a file‑attachment annota... | +| [batch-add-attachment-to-pdfs](./batch-add-attachment-to-pdfs.cs) | Batch Add Attachment to PDFs | `Document`, `FileSpecification`, `EmbeddedFilesCollection` | Shows how to iterate over a folder of PDF files and embed the same attachment into each document ... | +| [batch-add-watermark-to-pdf-pages](./batch-add-watermark-to-pdf-pages.cs) | Batch Add Watermark to PDF Pages | `Document`, `Page`, `WatermarkArtifact` | Loads a PDF, iterates through all pages, creates a centered red text watermark using WatermarkArt... | +| [batch-extract-pdf-attachments-zip](./batch-extract-pdf-attachments-zip.cs) | Batch Extract PDF Attachments to ZIP Archive | `Document`, `Page`, `FileAttachmentAnnotation` | Demonstrates how to iterate through multiple PDF files, collect file‑attachment annotations, and ... | +| [compress-pdf-portfolio-using-optimization-options](./compress-pdf-portfolio-using-optimization-options.cs) | Compress PDF Portfolio Using Optimization Options | `Document`, `OptimizationOptions`, `OptimizeResources` | Loads an existing PDF portfolio, enables object compression via OptimizationOptions, and saves th... | +| [create-pdf-portfolio-embed-files](./create-pdf-portfolio-embed-files.cs) | Create PDF Portfolio by Embedding Files | `Document`, `FileSpecification`, `EmbeddedFiles` | Shows how to convert a regular PDF into a PDF Portfolio by adding multiple embedded files using A... | +| [create-pdf-portfolio-with-attachments](./create-pdf-portfolio-with-attachments.cs) | Create PDF Portfolio with Attachments | `Document`, `Collection`, `FileSpecification` | Shows how to create an empty PDF document, initialize its collection, add files as attachments us... | +| [delete-pdf-attachment-by-filename](./delete-pdf-attachment-by-filename.cs) | Delete PDF Attachment by Filename | `Document`, `EmbeddedFiles`, `FindByName` | Demonstrates how to locate and remove an embedded file from a PDF using its filename with Aspose.... | +| [delete-pdf-outline-item-by-description](./delete-pdf-outline-item-by-description.cs) | Delete PDF Outline Item by Description | `Document`, `OutlineCollection`, `Delete` | Shows how to remove a PDF outline (bookmark) whose title matches a given string using Aspose.Pdf. | +| [embed-attachment-with-metadata-into-pdf](./embed-attachment-with-metadata-into-pdf.cs) | Embed Attachment with Metadata into PDF | `Document`, `FileSpecification`, `Add` | Demonstrates how to embed a file into an existing PDF using Aspose.Pdf and add custom attachment ... | +| [embed-file-attachment-from-byte-array](./embed-file-attachment-from-byte-array.cs) | Embed a File Attachment in PDF from a Byte Array | `Document`, `Page`, `Rectangle` | Shows how to create a FileSpecification from a byte array using a MemoryStream and add it to a PD... | +| [embed-hidden-xml-metadata-pdf-attachment](./embed-hidden-xml-metadata-pdf-attachment.cs) | Embed Hidden XML Metadata as PDF Attachment | `Document`, `Page`, `FileSpecification` | Shows how to serialize XML metadata to a memory stream and embed it as a hidden file attachment a... | +| [embed-image-into-pdf-portfolio](./embed-image-into-pdf-portfolio.cs) | Embed Image into PDF Portfolio with Display Name | `Document`, `FileSpecification`, `Add` | Shows how to create a PDF portfolio, embed an image file, and assign a custom display name to the... | +| [extract-attachments-from-encrypted-pdf](./extract-attachments-from-encrypted-pdf.cs) | Extract Attachments from Encrypted PDF | `Document`, `EmbeddedFiles`, `EmbeddedFileCollection` | Demonstrates how to open a password‑protected PDF with Aspose.Pdf, enumerate its embedded file at... | +| [extract-embedded-attachment-from-pdf](./extract-embedded-attachment-from-pdf.cs) | Extract Embedded Attachment from PDF | `Document`, `EmbeddedFiles`, `FileSpecification` | Demonstrates how to locate an embedded file in a PDF by name and save it to a specified directory... | +| [extract-embedded-file-by-index](./extract-embedded-file-by-index.cs) | Extract Embedded File from PDF by Index | `Document`, `FileSpecification`, `EmbeddedFiles` | Loads a PDF document, retrieves an embedded file at a given 1‑based index, and saves it to disk p... | +| [extract-files-from-pdf-portfolio](./extract-files-from-pdf-portfolio.cs) | Extract Files from PDF Portfolio | `Document`, `EmbeddedFiles`, `Save(string)` | Demonstrates how to enumerate embedded files in a PDF portfolio using Aspose.Pdf, preserve their ... | +| [extract-pdf-attachment-metadata](./extract-pdf-attachment-metadata.cs) | Extract PDF Attachment Metadata to JSON | `Document`, `Page`, `Annotation` | Shows how to read file attachment annotations from a PDF, retrieve each attachment's file name an... | +| ... | | | *and 20 more files* | + +## Category Statistics +- Total examples: 50 + +## Category-Specific Tips + +### Key API Surface +- `Aspose.Pdf.Document` +- `Aspose.Pdf.EmbeddedFileCollection` +- `Aspose.Pdf.EmbeddedFilesCollection` +- `Aspose.Pdf.Facades.PdfContentEditor` +- `Aspose.Pdf.FileEncoding` +- `Aspose.Pdf.FileSpecification` +- `Aspose.Pdf.FileSpecification.Params` +- `Aspose.Pdf.FileSpecificationParams` +- `PdfContentEditor.AddDocumentAttachment` +- `PdfContentEditor.BindPdf` +- `PdfContentEditor.Save` + +### Rules +- Create a {attachment_file} FileSpecification with a {string_literal} description and add it to {doc}.EmbeddedFiles via the Add method to embed the file in the PDF. +- After modifying the attachment collection, persist changes by calling {doc}.Save({output_pdf}). +- Bind a PDF document with PdfContentEditor.BindPdf({input_pdf}) before performing any edit operations. +- Add a file attachment using PdfContentEditor.AddDocumentAttachment({attachment_file}, {string_literal}) where the second argument is the attachment description. +- Persist the changes by calling PdfContentEditor.Save({output_pdf}). + +### Warnings +- The EmbeddedFiles collection is lazily instantiated; ensure {doc}.EmbeddedFiles is not null before adding. +- FileSpecification constructor expects the source file to exist on disk. +- AddDocumentAttachment only supports attaching external files; other attachment types are not covered in this example. +- The example assumes the PDF contains an EmbeddedFiles collection; calling Delete() on an empty collection is safe but may be unnecessary. +- The source file referenced in the FileSpecification must exist on disk; otherwise an exception will be thrown. + +## General Tips +- See parent [agents.md](../agents.md) for: + - **Boundaries** — Always / Ask First / Never rules for all examples + - **Common Mistakes** — verified anti-patterns that cause build failures + - **Domain Knowledge** — cross-cutting API-specific gotchas + - **Testing Guide** — build and run verification steps +- Review code examples in this folder for working-with-attachments patterns + + +Updated: 2026-05-08 | Run: `20260508_124239_26063e` + diff --git a/working-with-attachments/apply-custom-template-to-pdf-portfolio.cs b/working-with-attachments/apply-custom-template-to-pdf-portfolio.cs new file mode 100644 index 00000000..17a61bd4 --- /dev/null +++ b/working-with-attachments/apply-custom-template-to-pdf-portfolio.cs @@ -0,0 +1,63 @@ +using System; +using System.IO; +using Aspose.Pdf; +using Aspose.Pdf.Drawing; +using Aspose.Pdf.Text; + +class Program +{ + static void Main() + { + const string inputPath = "portfolio.pdf"; + const string outputPath = "portfolio_with_template.pdf"; + + if (!File.Exists(inputPath)) + { + Console.Error.WriteLine($"File not found: {inputPath}"); + return; + } + + // Load the existing PDF Portfolio + using (Document doc = new Document(inputPath)) + { + // Apply a visual template to each page + foreach (Page page in doc.Pages) + { + // Create a Graph that covers the whole page (width/height are double as required) + Graph graph = new Graph(page.PageInfo.Width, page.PageInfo.Height); + + // Add a semi‑transparent background rectangle + var rect = new Aspose.Pdf.Drawing.Rectangle( + 0f, // left (float) + 0f, // bottom (float) + (float)page.PageInfo.Width, // width (float) + (float)page.PageInfo.Height); // height (float) + + // Use Color.FromArgb to embed transparency (alpha 77 ≈ 30% opacity) + rect.GraphInfo = new GraphInfo + { + FillColor = Color.FromArgb(77, (int)(0.9 * 255), (int)(0.9 * 255), (int)(0.95 * 255)), + Color = Color.Gray, // border color + LineWidth = 0.5f + }; + graph.Shapes.Add(rect); + + // Add the Graph (background) to the page + page.Paragraphs.Add(graph); + + // Add a header text as part of the template + TextFragment header = new TextFragment("Custom Template Header"); + header.Position = new Position(50, page.PageInfo.Height - 50); // top‑left position + header.TextState.FontSize = 24; + header.TextState.Font = FontRepository.FindFont("Helvetica"); + header.TextState.ForegroundColor = Color.DarkBlue; + page.Paragraphs.Add(header); + } + + // Save the modified PDF Portfolio + doc.Save(outputPath); + } + + Console.WriteLine($"Portfolio saved with template to '{outputPath}'."); + } +} diff --git a/working-with-attachments/attach-remote-file-to-pdf.cs b/working-with-attachments/attach-remote-file-to-pdf.cs new file mode 100644 index 00000000..8ce94200 --- /dev/null +++ b/working-with-attachments/attach-remote-file-to-pdf.cs @@ -0,0 +1,74 @@ +using System; +using System.IO; +using System.Net.Http; +using System.Threading.Tasks; +using Aspose.Pdf; +using Aspose.Pdf.Annotations; + +class Program +{ + static async Task Main() + { + const string inputPdf = "input.pdf"; + const string outputPdf = "output_with_attachment.pdf"; + const string fileUrl = "https://example.com/file.pdf"; + + if (!File.Exists(inputPdf)) + { + Console.Error.WriteLine($"Input PDF not found: {inputPdf}"); + return; + } + + // Download the remote file into a byte array + byte[] fileBytes; + string fileName; + using (HttpClient http = new HttpClient()) + { + try + { + fileBytes = await http.GetByteArrayAsync(fileUrl); + fileName = Path.GetFileName(new Uri(fileUrl).LocalPath); + if (string.IsNullOrEmpty(fileName)) + fileName = "attachment.bin"; + } + catch (Exception ex) + { + Console.Error.WriteLine($"Failed to download file: {ex.Message}"); + return; + } + } + + // Open the PDF, add a file attachment annotation, and save + using (Document doc = new Document(inputPdf)) + { + // Use the first page (Aspose.Pdf uses 1‑based indexing) + Page page = doc.Pages[1]; + + // Define the rectangle that will contain the annotation icon + Aspose.Pdf.Rectangle rect = new Aspose.Pdf.Rectangle(100, 500, 120, 520); + + // Create a FileSpecification from the in‑memory stream + using (MemoryStream ms = new MemoryStream(fileBytes)) + { + FileSpecification fileSpec = new FileSpecification(ms, fileName); + + // Create the file attachment annotation + FileAttachmentAnnotation attachment = new FileAttachmentAnnotation(page, rect, fileSpec) + { + // The Icon enum must be qualified with its declaring type (FileIcon) + Icon = FileIcon.Paperclip, + Title = "Remote File", + Contents = $"Attached file from {fileUrl}" + }; + + // Add the annotation to the page + page.Annotations.Add(attachment); + } + + // Save the modified PDF + doc.Save(outputPdf); + } + + Console.WriteLine($"PDF saved with attachment: {outputPdf}"); + } +} diff --git a/working-with-attachments/batch-add-attachment-to-pdfs.cs b/working-with-attachments/batch-add-attachment-to-pdfs.cs new file mode 100644 index 00000000..04655a19 --- /dev/null +++ b/working-with-attachments/batch-add-attachment-to-pdfs.cs @@ -0,0 +1,57 @@ +using System; +using System.IO; +using Aspose.Pdf; + +class BatchAttachmentProcessor +{ + static void Main() + { + // Folder containing source PDFs + const string inputFolder = @"C:\PdfInput"; + // Folder where processed PDFs will be saved + const string outputFolder = @"C:\PdfOutput"; + // Path to the file that will be attached to each PDF + const string attachmentPath = @"C:\Attachment\attachment.pdf"; + + // Ensure the output directory exists + Directory.CreateDirectory(outputFolder); + + // Verify the attachment file exists + if (!File.Exists(attachmentPath)) + { + Console.Error.WriteLine($"Attachment not found: {attachmentPath}"); + return; + } + + // Process each PDF file in the input folder + foreach (string pdfFile in Directory.GetFiles(inputFolder, "*.pdf")) + { + string fileName = Path.GetFileName(pdfFile); + string outputPath = Path.Combine(outputFolder, fileName); + + try + { + // Load the PDF document inside a using block for deterministic disposal + using (Document doc = new Document(pdfFile)) + { + // Create a file specification for the attachment using the constructor + FileSpecification attachmentSpec = new FileSpecification(attachmentPath, "Embedded attachment"); + + // Add the attachment to the document's EmbeddedFiles collection + doc.EmbeddedFiles.Add(attachmentSpec); + + // Save the modified PDF to the output folder + doc.Save(outputPath); + } + + Console.WriteLine($"Processed: {fileName}"); + } + catch (Exception ex) + { + Console.Error.WriteLine($"Error processing '{fileName}': {ex.Message}"); + } + } + + Console.WriteLine("Batch attachment processing completed."); + } +} diff --git a/working-with-attachments/batch-add-watermark-to-pdf-pages.cs b/working-with-attachments/batch-add-watermark-to-pdf-pages.cs new file mode 100644 index 00000000..cf2cfa55 --- /dev/null +++ b/working-with-attachments/batch-add-watermark-to-pdf-pages.cs @@ -0,0 +1,78 @@ +using System; +using System.IO; +using Aspose.Pdf; +using Aspose.Pdf.Annotations; +using Aspose.Pdf.Text; + +class Program +{ + static void Main() + { + const string inputPdf = "input.pdf"; + const string outputPdf = "output_watermarked.pdf"; + const string watermarkText = "CONFIDENTIAL"; + + if (!File.Exists(inputPdf)) + { + Console.Error.WriteLine($"Input file not found: {inputPdf}"); + return; + } + + // Load the PDF document inside a using block to ensure proper disposal. + using (Document doc = new Document(inputPdf)) + { + // Iterate through all pages (1‑based indexing). + for (int i = 1; i <= doc.Pages.Count; i++) + { + Page page = doc.Pages[i]; + + // OPTIONAL: Detect pages that contain file‑attachment annotations. + // If you only want to watermark pages that actually have attachments, + // uncomment the block below. + /* + bool hasAttachment = false; + foreach (Annotation ann in page.Annotations) + { + if (ann is FileAttachmentAnnotation) + { + hasAttachment = true; + break; + } + } + if (!hasAttachment) continue; // skip pages without attachments + */ + + // Create a text watermark artifact. + // WatermarkArtifact is part of the core Aspose.Pdf API. + WatermarkArtifact watermark = new WatermarkArtifact(); + + // Set the watermark text and its visual style. + // The TextState defines font, size and color. + watermark.SetTextAndState( + watermarkText, + new TextState + { + FontSize = 72, + Font = FontRepository.FindFont("Helvetica"), + ForegroundColor = Color.Red, + FontStyle = FontStyles.Bold + }); + + // Position the watermark in the centre of the page. + watermark.ArtifactHorizontalAlignment = HorizontalAlignment.Center; + watermark.ArtifactVerticalAlignment = VerticalAlignment.Center; + + // Make the watermark appear behind the page content. + watermark.IsBackground = true; + + // Add the artifact to the current page. + page.Artifacts.Add(watermark); + } + + // Save the modified document. + doc.Save(outputPdf); + } + + Console.WriteLine($"Watermarked PDF saved to '{outputPdf}'."); + } +} \ No newline at end of file diff --git a/working-with-attachments/batch-extract-pdf-attachments-zip.cs b/working-with-attachments/batch-extract-pdf-attachments-zip.cs new file mode 100644 index 00000000..248b6b1f --- /dev/null +++ b/working-with-attachments/batch-extract-pdf-attachments-zip.cs @@ -0,0 +1,92 @@ +using System; +using System.IO; +using System.IO.Compression; +using Aspose.Pdf; +using Aspose.Pdf.Annotations; + +class BatchAttachmentExtractor +{ + static void Main() + { + // Folder containing the source PDF files. Use a path that works on any OS. + // If the absolute Windows style path does not exist, fall back to a relative folder. + const string rawInputFolder = @"C:\InputPdfs"; // original intent + string inputFolder = Directory.Exists(rawInputFolder) ? rawInputFolder : Path.GetFullPath("InputPdfs"); + + // Path for the consolidated ZIP archive (also made OS‑agnostic) + const string rawOutputZipPath = @"C:\Output\attachments.zip"; + string outputZipPath = Path.GetFullPath( + Directory.Exists(Path.GetDirectoryName(rawOutputZipPath) ?? string.Empty) + ? rawOutputZipPath + : Path.Combine("Output", "attachments.zip")); + + // Ensure the output directory exists + string? outputDir = Path.GetDirectoryName(outputZipPath); + if (!string.IsNullOrEmpty(outputDir)) + { + Directory.CreateDirectory(outputDir); + } + + // Collect all PDF files in the input folder + if (!Directory.Exists(inputFolder)) + { + Console.WriteLine($"Input folder does not exist: {inputFolder}"); + return; + } + string[] pdfFiles = Directory.GetFiles(inputFolder, "*.pdf", SearchOption.TopDirectoryOnly); + if (pdfFiles.Length == 0) + { + Console.WriteLine("No PDF files found in the specified folder."); + return; + } + + // Create the ZIP archive (overwrite if it already exists) + using (FileStream zipStream = new FileStream(outputZipPath, FileMode.Create)) + using (ZipArchive zip = new ZipArchive(zipStream, ZipArchiveMode.Create)) + { + foreach (string pdfPath in pdfFiles) + { + // Open each PDF inside a using block for deterministic disposal + using (Document doc = new Document(pdfPath)) + { + // Iterate through all pages (Aspose.Pdf uses 1‑based indexing) + for (int pageIndex = 1; pageIndex <= doc.Pages.Count; pageIndex++) + { + Page page = doc.Pages[pageIndex]; + + // Iterate through all annotations on the page + for (int annIndex = 1; annIndex <= page.Annotations.Count; annIndex++) + { + Annotation ann = page.Annotations[annIndex]; + + // We're interested only in file attachment annotations + if (ann is FileAttachmentAnnotation fileAnn && fileAnn.File != null) + { + FileSpecification fileSpec = fileAnn.File; + + // Determine a unique entry name inside the ZIP: + // / + string pdfBaseName = Path.GetFileNameWithoutExtension(pdfPath); + string attachmentName = !string.IsNullOrEmpty(fileSpec.Name) + ? fileSpec.Name + : $"attachment_{Guid.NewGuid()}"; + string entryName = $"{pdfBaseName}/{attachmentName}"; + + // Create a new entry in the ZIP archive + ZipArchiveEntry entry = zip.CreateEntry(entryName, CompressionLevel.Optimal); + using (Stream entryStream = entry.Open()) + using (Stream contentStream = fileSpec.Contents) + { + // Copy the embedded file data directly into the ZIP entry + contentStream.CopyTo(entryStream); + } + } + } + } + } + } + } + + Console.WriteLine($"All attachments have been extracted to: {outputZipPath}"); + } +} diff --git a/working-with-attachments/compress-pdf-portfolio-using-optimization-options.cs b/working-with-attachments/compress-pdf-portfolio-using-optimization-options.cs new file mode 100644 index 00000000..147b9ee2 --- /dev/null +++ b/working-with-attachments/compress-pdf-portfolio-using-optimization-options.cs @@ -0,0 +1,35 @@ +using System; +using System.IO; +using Aspose.Pdf; +using Aspose.Pdf.Optimization; + +class Program +{ + static void Main() + { + const string inputPath = "portfolio.pdf"; + const string outputPath = "portfolio_compressed.pdf"; + + if (!File.Exists(inputPath)) + { + Console.Error.WriteLine($"Input file not found: {inputPath}"); + return; + } + + // Load the existing PDF Portfolio + using (Document doc = new Document(inputPath)) + { + // Enable compression of PDF objects to reduce file size + OptimizationOptions opt = new OptimizationOptions + { + CompressObjects = true + }; + doc.OptimizeResources(opt); + + // Save the compressed PDF Portfolio + doc.Save(outputPath); + } + + Console.WriteLine($"Compressed PDF Portfolio saved to '{outputPath}'."); + } +} \ No newline at end of file diff --git a/working-with-attachments/create-pdf-portfolio-embed-files.cs b/working-with-attachments/create-pdf-portfolio-embed-files.cs new file mode 100644 index 00000000..b6ff1ea6 --- /dev/null +++ b/working-with-attachments/create-pdf-portfolio-embed-files.cs @@ -0,0 +1,63 @@ +using System; +using System.IO; +using Aspose.Pdf; // Core PDF API + +class Program +{ + static void Main() + { + // Input regular PDF + const string inputPdf = "regular.pdf"; + + // Output PDF that will become a portfolio + const string outputPdf = "portfolio.pdf"; + + // Files to embed into the portfolio + string[] filesToEmbed = new string[] + { + "document1.docx", + "image1.png", + "spreadsheet1.xlsx" + }; + + // Verify that the source PDF and all files exist + if (!File.Exists(inputPdf)) + { + Console.Error.WriteLine($"Input PDF not found: {inputPdf}"); + return; + } + + foreach (var f in filesToEmbed) + { + if (!File.Exists(f)) + { + Console.Error.WriteLine($"File to embed not found: {f}"); + return; + } + } + + try + { + // Load the regular PDF inside a using block for deterministic disposal + using (Document doc = new Document(inputPdf)) + { + // Add each file as an embedded file (PDF Portfolio) + foreach (var filePath in filesToEmbed) + { + // Use FileSpecification (core API) to embed files. + // Adding it to the document's EmbeddedFiles collection creates a portfolio entry. + doc.EmbeddedFiles.Add(new FileSpecification(filePath)); + } + + // Save the modified document; it now contains the embedded files as a PDF Portfolio + doc.Save(outputPdf); + } + + Console.WriteLine($"Portfolio created successfully: {outputPdf}"); + } + catch (Exception ex) + { + Console.Error.WriteLine($"Error: {ex.Message}"); + } + } +} diff --git a/working-with-attachments/create-pdf-portfolio-with-attachments.cs b/working-with-attachments/create-pdf-portfolio-with-attachments.cs new file mode 100644 index 00000000..a0e7dd23 --- /dev/null +++ b/working-with-attachments/create-pdf-portfolio-with-attachments.cs @@ -0,0 +1,38 @@ +using System; +using System.IO; +using Aspose.Pdf; + +class Program +{ + static void Main() + { + const string outputPath = "portfolio.pdf"; + + // Create an empty PDF document + using (Document doc = new Document()) + { + // Initialize the collection that holds portfolio files + if (doc.Collection == null) + doc.Collection = new Collection(); + + // Files to add to the portfolio (ensure the files exist) + string[] filesToAdd = { "file1.txt", "image.png" }; + foreach (string filePath in filesToAdd) + { + if (File.Exists(filePath)) + { + // Create a file specification for the attachment + var fileSpec = new FileSpecification(filePath, Path.GetFileName(filePath)); + fileSpec.Contents = new MemoryStream(File.ReadAllBytes(filePath)); + // Add the specification to the document's collection + doc.Collection.Add(fileSpec); + } + } + + // Save the portfolio PDF + doc.Save(outputPath); + } + + Console.WriteLine($"Portfolio PDF saved to '{outputPath}'."); + } +} diff --git a/working-with-attachments/delete-pdf-attachment-by-filename.cs b/working-with-attachments/delete-pdf-attachment-by-filename.cs new file mode 100644 index 00000000..fb6de9b5 --- /dev/null +++ b/working-with-attachments/delete-pdf-attachment-by-filename.cs @@ -0,0 +1,41 @@ +using System; +using System.IO; +using Aspose.Pdf; + +class Program +{ + static void Main() + { + const string inputPath = "input.pdf"; + const string outputPath = "output.pdf"; + const string attachmentName = "myfile.txt"; + + if (!File.Exists(inputPath)) + { + Console.Error.WriteLine($"File not found: {inputPath}"); + return; + } + + // Load the PDF document + using (Document doc = new Document(inputPath)) + { + // Verify the attachment exists + var fileSpec = doc.EmbeddedFiles.FindByName(attachmentName); + if (fileSpec != null) + { + // Delete the attachment by its filename + doc.EmbeddedFiles.Delete(attachmentName); + Console.WriteLine($"Attachment '{attachmentName}' deleted."); + } + else + { + Console.WriteLine($"Attachment '{attachmentName}' not found."); + } + + // Save the updated PDF + doc.Save(outputPath); + } + + Console.WriteLine($"Updated PDF saved to '{outputPath}'."); + } +} \ No newline at end of file diff --git a/working-with-attachments/delete-pdf-outline-item-by-description.cs b/working-with-attachments/delete-pdf-outline-item-by-description.cs new file mode 100644 index 00000000..9af2e0ce --- /dev/null +++ b/working-with-attachments/delete-pdf-outline-item-by-description.cs @@ -0,0 +1,35 @@ +using System; +using System.IO; +using Aspose.Pdf; + +class Program +{ + static void Main() + { + // Input PDF containing portfolio outline items + const string inputPath = "portfolio.pdf"; + // Output PDF after removal + const string outputPath = "portfolio_cleaned.pdf"; + // Description (title) of the outline item to delete + const string descriptionToDelete = "Quarterly Report"; + + if (!File.Exists(inputPath)) + { + Console.Error.WriteLine($"File not found: {inputPath}"); + return; + } + + // Load the PDF document (wrapped in using for deterministic disposal) + using (Document doc = new Document(inputPath)) + { + // Delete outline items whose title matches the given description. + // OutlineCollection.Delete(string) removes the item with the specified title. + doc.Outlines.Delete(descriptionToDelete); + + // Save the modified document + doc.Save(outputPath); + } + + Console.WriteLine($"Deleted outline item \"{descriptionToDelete}\" and saved to '{outputPath}'."); + } +} \ No newline at end of file diff --git a/working-with-attachments/embed-attachment-with-metadata-into-pdf.cs b/working-with-attachments/embed-attachment-with-metadata-into-pdf.cs new file mode 100644 index 00000000..effb6da8 --- /dev/null +++ b/working-with-attachments/embed-attachment-with-metadata-into-pdf.cs @@ -0,0 +1,52 @@ +using System; +using System.IO; +using Aspose.Pdf; + +class Program +{ + static void Main() + { + const string inputPdfPath = "input.pdf"; + const string attachmentFilePath = "attachment.txt"; + const string outputPdfPath = "output.pdf"; + + // Verify that both the source PDF and the attachment exist. + if (!File.Exists(inputPdfPath)) + { + Console.Error.WriteLine($"Source PDF not found: {inputPdfPath}"); + return; + } + + if (!File.Exists(attachmentFilePath)) + { + Console.Error.WriteLine($"Attachment file not found: {attachmentFilePath}"); + return; + } + + // Load the existing PDF document. + using (Document pdfDoc = new Document(inputPdfPath)) + { + // ------------------------------------------------------------ + // Embed the attachment into the PDF using a FileSpecification. + // ------------------------------------------------------------ + var fileSpec = new FileSpecification(attachmentFilePath, "Attachment added via Aspose.Pdf"); + // Load the file contents into a memory stream – this avoids keeping the file handle open. + fileSpec.Contents = new MemoryStream(File.ReadAllBytes(attachmentFilePath)); + pdfDoc.EmbeddedFiles.Add(fileSpec); + + // Add custom metadata about the attachment to the document information dictionary. + pdfDoc.Info.Add("AttachmentFileName", Path.GetFileName(attachmentFilePath)); + pdfDoc.Info.Add("AttachmentDescription", "Sample attachment added via Aspose.Pdf"); + + // Configure PDF save options – the default behavior already embeds attached files. + PdfSaveOptions saveOptions = new PdfSaveOptions(); + // No explicit EmbedAttachments property exists in the current Aspose.Pdf version. + // The embedded files added to the document are automatically written when saving. + + // Save the modified PDF with the embedded file and metadata. + pdfDoc.Save(outputPdfPath, saveOptions); + } + + Console.WriteLine($"PDF saved with attachment and metadata to '{outputPdfPath}'."); + } +} diff --git a/working-with-attachments/embed-file-attachment-from-byte-array.cs b/working-with-attachments/embed-file-attachment-from-byte-array.cs new file mode 100644 index 00000000..acca646d --- /dev/null +++ b/working-with-attachments/embed-file-attachment-from-byte-array.cs @@ -0,0 +1,51 @@ +using System; +using System.IO; +using Aspose.Pdf; +using Aspose.Pdf.Annotations; + +class Program +{ + static void Main() + { + // Sample byte array representing the file content to embed + byte[] fileData = System.Text.Encoding.UTF8.GetBytes("Hello, this is the embedded file content."); + + // Path to the output PDF + const string outputPdf = "output_with_attachment.pdf"; + + // Create a new PDF document inside a using block for deterministic disposal + using (Document doc = new Document()) + { + // Add a blank page (pages are 1‑based) + Page page = doc.Pages.Add(); + + // Define the rectangle for the attachment annotation (left, bottom, right, top) + Aspose.Pdf.Rectangle rect = new Aspose.Pdf.Rectangle(100, 500, 200, 550); + + // Create a FileSpecification from the byte array using a MemoryStream + using (MemoryStream ms = new MemoryStream(fileData)) + { + // The second argument is the name that will appear in the attachment list + FileSpecification fileSpec = new FileSpecification(ms, "EmbeddedFile.txt"); + + // Create the file attachment annotation with the page, rectangle, and file spec + FileAttachmentAnnotation attachment = new FileAttachmentAnnotation(page, rect, fileSpec) + { + // Optional: set a tooltip and icon for the annotation + Contents = "Embedded text file", + // Correct enum usage for the icon (FileIcon enum, not a nested type) + Icon = FileIcon.Paperclip, + Title = "Sample Attachment" + }; + + // Add the annotation to the page's annotation collection + page.Annotations.Add(attachment); + } + + // Save the PDF document + doc.Save(outputPdf); + } + + Console.WriteLine($"PDF with embedded file saved to '{outputPdf}'."); + } +} diff --git a/working-with-attachments/embed-hidden-xml-metadata-pdf-attachment.cs b/working-with-attachments/embed-hidden-xml-metadata-pdf-attachment.cs new file mode 100644 index 00000000..dc1fbd5a --- /dev/null +++ b/working-with-attachments/embed-hidden-xml-metadata-pdf-attachment.cs @@ -0,0 +1,60 @@ +using System; +using System.IO; +using System.Xml.Linq; +using Aspose.Pdf; +using Aspose.Pdf.Annotations; + +class Program +{ + static void Main() + { + const string outputPdf = "output_with_metadata.pdf"; + + // Build simple XML metadata + XDocument xmlMeta = new XDocument( + new XElement("Metadata", + new XElement("Author", "John Doe"), + new XElement("Created", DateTime.UtcNow.ToString("o")) + ) + ); + + // Serialize XML to a memory stream (no file on disk) + using (MemoryStream xmlStream = new MemoryStream()) + { + xmlMeta.Save(xmlStream); + xmlStream.Position = 0; // reset for reading + + // Create a new PDF document (self‑contained, no external input file required) + using (Document pdfDoc = new Document()) + { + // Add a blank page so we have a page to host the annotation + Page page = pdfDoc.Pages.Add(); + + // Create a FileSpecification for the XML stream + // Use the constructor that accepts a file name, then assign the stream to Contents + FileSpecification fileSpec = new FileSpecification("metadata.xml"); + fileSpec.Contents = xmlStream; + + // Define a zero‑size rectangle; the annotation will be invisible + Aspose.Pdf.Rectangle rect = new Aspose.Pdf.Rectangle(0, 0, 0, 0); + + // Create the file attachment annotation + FileAttachmentAnnotation attachment = new FileAttachmentAnnotation(page, rect, fileSpec) + { + // Mark the annotation as hidden so it does not appear in viewers + Flags = AnnotationFlags.Hidden, + // Optional: give it a name for programmatic access + Name = "DocumentMetadata" + }; + + // Add the annotation to the page + page.Annotations.Add(attachment); + + // Save the modified PDF + pdfDoc.Save(outputPdf); + } + } + + Console.WriteLine($"PDF saved with hidden XML metadata: {outputPdf}"); + } +} diff --git a/working-with-attachments/embed-image-into-pdf-portfolio.cs b/working-with-attachments/embed-image-into-pdf-portfolio.cs new file mode 100644 index 00000000..7331e6b7 --- /dev/null +++ b/working-with-attachments/embed-image-into-pdf-portfolio.cs @@ -0,0 +1,40 @@ +using System; +using System.IO; +using Aspose.Pdf; + +class Program +{ + static void Main() + { + const string outputPdf = "portfolio.pdf"; + const string imageFile = "image.png"; + + if (!File.Exists(imageFile)) + { + Console.Error.WriteLine($"Image file not found: {imageFile}"); + return; + } + + // Create a new PDF document (will become a portfolio when files are embedded) + using (Document doc = new Document()) + { + // Add a blank page (required for a valid PDF) + doc.Pages.Add(); + + // Create a file specification for the image to embed using the constructor + // The first argument is the file path, the second is a description. + FileSpecification fileSpec = new FileSpecification(imageFile, "Embedded image file"); + // Set the display name that will appear in the portfolio + fileSpec.Name = "SampleImage.png"; + + // Add the file specification to the embedded files collection. + // The first argument is a unique key for the embedded file. + doc.EmbeddedFiles.Add("image1", fileSpec); + + // Save the PDF portfolio + doc.Save(outputPdf); + } + + Console.WriteLine($"PDF portfolio created: {outputPdf}"); + } +} diff --git a/working-with-attachments/extract-attachments-from-encrypted-pdf.cs b/working-with-attachments/extract-attachments-from-encrypted-pdf.cs new file mode 100644 index 00000000..31a5aef6 --- /dev/null +++ b/working-with-attachments/extract-attachments-from-encrypted-pdf.cs @@ -0,0 +1,57 @@ +using System; +using System.IO; +using Aspose.Pdf; + +class ExtractAttachments +{ + static void Main() + { + // Input encrypted PDF path and password + const string inputPdfPath = "encrypted.pdf"; + const string password = "userPassword"; + + // Directory where extracted attachments will be saved + const string outputDir = "ExtractedAttachments"; + + if (!File.Exists(inputPdfPath)) + { + Console.Error.WriteLine($"Input file not found: {inputPdfPath}"); + return; + } + + // Ensure output directory exists + Directory.CreateDirectory(outputDir); + + // Open the encrypted PDF using the password + using (Aspose.Pdf.Document doc = new Aspose.Pdf.Document(inputPdfPath, password)) + { + // The EmbeddedFiles collection holds all file attachments + Aspose.Pdf.EmbeddedFileCollection attachments = doc.EmbeddedFiles; + + if (attachments == null || attachments.Count == 0) + { + Console.WriteLine("No attachments found in the PDF."); + return; + } + + // Iterate over each attachment and save it to the output directory + foreach (Aspose.Pdf.FileSpecification fileSpec in attachments) + { + // Determine a safe file name (use the original name if available) + string fileName = string.IsNullOrEmpty(fileSpec.Name) ? "attachment.bin" : fileSpec.Name; + string outputPath = Path.Combine(outputDir, fileName); + + // The Contents property provides a stream with the attachment data + using (Stream contentStream = fileSpec.Contents) + using (FileStream fileStream = new FileStream(outputPath, FileMode.Create, FileAccess.Write)) + { + contentStream.CopyTo(fileStream); + } + + Console.WriteLine($"Extracted: {fileName} -> {outputPath}"); + } + } + + Console.WriteLine("Attachment extraction completed."); + } +} \ No newline at end of file diff --git a/working-with-attachments/extract-embedded-attachment-from-pdf.cs b/working-with-attachments/extract-embedded-attachment-from-pdf.cs new file mode 100644 index 00000000..7cdbd68e --- /dev/null +++ b/working-with-attachments/extract-embedded-attachment-from-pdf.cs @@ -0,0 +1,52 @@ +using System; +using System.IO; +using Aspose.Pdf; + +class Program +{ + static void Main() + { + const string pdfPath = "input.pdf"; // source PDF + const string attachmentName = "myfile.txt"; // name of the embedded file to extract + const string outputDirectory = "ExtractedAttachments"; + + if (!File.Exists(pdfPath)) + { + Console.Error.WriteLine($"PDF not found: {pdfPath}"); + return; + } + + // Ensure the target directory exists + Directory.CreateDirectory(outputDirectory); + + try + { + // Load the PDF document (lifecycle: using → dispose) + using (Document doc = new Document(pdfPath)) + { + // Locate the embedded file by its name + FileSpecification fileSpec = doc.EmbeddedFiles.FindByName(attachmentName); + if (fileSpec == null || fileSpec.Contents == null) + { + Console.WriteLine($"Attachment '{attachmentName}' not found in the document."); + return; + } + + // Build the full path for the extracted file + string outputPath = Path.Combine(outputDirectory, attachmentName); + + // Save the embedded file to the target directory using the Contents stream + using (FileStream outStream = File.Create(outputPath)) + { + fileSpec.Contents.CopyTo(outStream); + } + + Console.WriteLine($"Attachment saved to: {outputPath}"); + } + } + catch (Exception ex) + { + Console.Error.WriteLine($"Error: {ex.Message}"); + } + } +} diff --git a/working-with-attachments/extract-embedded-file-by-index.cs b/working-with-attachments/extract-embedded-file-by-index.cs new file mode 100644 index 00000000..23111f94 --- /dev/null +++ b/working-with-attachments/extract-embedded-file-by-index.cs @@ -0,0 +1,54 @@ +using System; +using System.IO; +using Aspose.Pdf; + +class Program +{ + static void Main() + { + const string pdfPath = "portfolio.pdf"; // input PDF containing embedded files + const int index = 1; // 1‑based index of the desired embedded file + + if (!File.Exists(pdfPath)) + { + Console.Error.WriteLine($"File not found: {pdfPath}"); + return; + } + + // Load the PDF document inside a using block for deterministic disposal + using (Document doc = new Document(pdfPath)) + { + // Verify that the requested index exists (EmbeddedFileCollection is 1‑based) + if (doc.EmbeddedFiles == null || doc.EmbeddedFiles.Count < index) + { + Console.Error.WriteLine($"No embedded file at index {index}."); + return; + } + + // Retrieve the embedded file specification + FileSpecification fileSpec = doc.EmbeddedFiles[index]; + + // Preserve the original file name (includes its extension) + string outputFileName = !string.IsNullOrEmpty(fileSpec.Name) + ? fileSpec.Name + : $"embedded_{index}"; + + // Build the full output path (current directory) + string outputPath = Path.Combine(Environment.CurrentDirectory, outputFileName); + + // Extract the embedded file using the Contents stream (EmbeddedFile property does not exist) + if (fileSpec.Contents != null) + { + using (FileStream outStream = File.Create(outputPath)) + { + fileSpec.Contents.CopyTo(outStream); + } + Console.WriteLine($"Embedded file saved to '{outputPath}'."); + } + else + { + Console.Error.WriteLine("Embedded file data is missing."); + } + } + } +} diff --git a/working-with-attachments/extract-files-from-pdf-portfolio.cs b/working-with-attachments/extract-files-from-pdf-portfolio.cs new file mode 100644 index 00000000..7ed0f27c --- /dev/null +++ b/working-with-attachments/extract-files-from-pdf-portfolio.cs @@ -0,0 +1,85 @@ +using System; +using System.IO; +using Aspose.Pdf; + +class PortfolioExtractor +{ + static void Main() + { + // Input PDF portfolio file + const string inputPdf = "portfolio.pdf"; + + // Output root directory where extracted files will be placed + const string outputRoot = "ExtractedFiles"; + + if (!File.Exists(inputPdf)) + { + Console.Error.WriteLine($"Input file not found: {inputPdf}"); + return; + } + + // Ensure the output root directory exists + Directory.CreateDirectory(outputRoot); + + // Load the PDF document (portfolio) inside a using block for deterministic disposal + using (Document doc = new Document(inputPdf)) + { + // The EmbeddedFiles collection holds all files stored in the portfolio. + // Use reflection to avoid a direct compile‑time dependency on the EmbeddedFile type. + foreach (var embedded in doc.EmbeddedFiles) + { + // Retrieve the file name (may contain relative path information) + var nameProp = embedded.GetType().GetProperty("Name"); + string relativePath = nameProp?.GetValue(embedded) as string ?? string.Empty; + if (string.IsNullOrEmpty(relativePath)) + { + // Skip entries without a valid name + continue; + } + + // Convert any forward slashes to the platform's directory separator + relativePath = relativePath.Replace('/', Path.DirectorySeparatorChar); + + // Combine with the output root to get the full destination path + string destinationPath = Path.Combine(outputRoot, relativePath); + + // Ensure the target directory exists before saving the file + string destinationDir = Path.GetDirectoryName(destinationPath); + if (!string.IsNullOrEmpty(destinationDir)) + { + Directory.CreateDirectory(destinationDir); + } + + // Invoke the Save(string) method via reflection + var saveMethod = embedded.GetType().GetMethod("Save", new[] { typeof(string) }); + if (saveMethod != null) + { + saveMethod.Invoke(embedded, new object[] { destinationPath }); + Console.WriteLine($"Extracted: {destinationPath}"); + } + else + { + // Fallback: try to read the file specification stream directly + var fileSpecProp = embedded.GetType().GetProperty("FileSpecification"); + var fileSpec = fileSpecProp?.GetValue(embedded); + var contentsProp = fileSpec?.GetType().GetProperty("Contents"); + var contents = contentsProp?.GetValue(fileSpec) as Stream; + if (contents != null) + { + using (var outStream = File.Create(destinationPath)) + { + contents.CopyTo(outStream); + } + Console.WriteLine($"Extracted (stream fallback): {destinationPath}"); + } + else + { + Console.Error.WriteLine($"Unable to extract embedded file: {relativePath}"); + } + } + } + } + + Console.WriteLine("Portfolio extraction completed."); + } +} diff --git a/working-with-attachments/extract-pdf-attachment-metadata.cs b/working-with-attachments/extract-pdf-attachment-metadata.cs new file mode 100644 index 00000000..12c1af63 --- /dev/null +++ b/working-with-attachments/extract-pdf-attachment-metadata.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text.Json; +using Aspose.Pdf; +using Aspose.Pdf.Annotations; + +class Program +{ + // Simple DTO for JSON serialization + public class AttachmentInfo + { + // File name may be null if the annotation does not contain a file specification + public string? FileName { get; set; } + public DateTime? CreationDate { get; set; } + } + + static void Main() + { + const string inputPdfPath = "input.pdf"; + const string outputJsonPath = "attachments.json"; + + if (!File.Exists(inputPdfPath)) + { + Console.Error.WriteLine($"File not found: {inputPdfPath}"); + return; + } + + var attachments = new List(); + + // Load the PDF document (lifecycle rule: wrap in using) + using (Document doc = new Document(inputPdfPath)) + { + // Iterate through all pages (1‑based indexing) + for (int i = 1; i <= doc.Pages.Count; i++) + { + Page page = doc.Pages[i]; + + // Iterate through annotations on the page + for (int j = 1; j <= page.Annotations.Count; j++) + { + Annotation ann = page.Annotations[j]; + + // We're interested only in file attachment annotations + if (ann is FileAttachmentAnnotation fileAnn) + { + AttachmentInfo info = new AttachmentInfo + { + // Use FileSpecification.Name to get the original file name + FileName = fileAnn.File?.Name, + CreationDate = fileAnn.CreationDate + }; + attachments.Add(info); + } + } + } + } + + // Serialize the list to JSON (pretty printed) + JsonSerializerOptions jsonOptions = new JsonSerializerOptions { WriteIndented = true }; + string json = JsonSerializer.Serialize(attachments, jsonOptions); + + // Write JSON to the output file + File.WriteAllText(outputJsonPath, json); + + Console.WriteLine($"Attachment metadata written to '{outputJsonPath}'."); + } +} diff --git a/working-with-attachments/extract-pdf-attachments-in-parallel.cs b/working-with-attachments/extract-pdf-attachments-in-parallel.cs new file mode 100644 index 00000000..090073c8 --- /dev/null +++ b/working-with-attachments/extract-pdf-attachments-in-parallel.cs @@ -0,0 +1,74 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using Aspose.Pdf; + +class Program +{ + static void Main() + { + // Directory containing the source PDFs + string inputDirectory = "InputPdfs"; + // Directory where extracted attachments will be saved + string outputDirectory = "ExtractedAttachments"; + + // Ensure the output folder exists + Directory.CreateDirectory(outputDirectory); + + // Verify the input directory exists; if not, inform the user and exit gracefully + if (!Directory.Exists(inputDirectory)) + { + Console.Error.WriteLine($"Input directory '{inputDirectory}' does not exist. No PDFs to process."); + return; + } + + // Get all PDF files in the input directory + string[] pdfFiles = Directory.GetFiles(inputDirectory, "*.pdf"); + + if (pdfFiles.Length == 0) + { + Console.WriteLine("No PDF files found in the input directory."); + return; + } + + // Process each PDF in parallel + Parallel.ForEach(pdfFiles, pdfPath => + { + try + { + // Load the PDF document (lifecycle rule: wrap in using) + using (Document doc = new Document(pdfPath)) + { + // If the document has no embedded files, skip it + if (doc.EmbeddedFiles == null || doc.EmbeddedFiles.Count == 0) + return; + + // Iterate over each embedded file using dynamic to avoid compile‑time dependency on the concrete type + foreach (dynamic attachment in doc.EmbeddedFiles) + { + // Build a safe attachment name (fallback to "attachment" if name is missing) + string safeAttachmentName = string.IsNullOrWhiteSpace((string)attachment.Name) + ? "attachment" + : (string)attachment.Name; + + // Build a unique file name to avoid collisions: _ + string pdfBaseName = Path.GetFileNameWithoutExtension(pdfPath); + string outputPath = Path.Combine( + outputDirectory, + $"{pdfBaseName}_{safeAttachmentName}"); + + // Save the attachment to disk + attachment.Save(outputPath); + } + } + } + catch (Exception ex) + { + // Log any errors but continue processing other files + Console.Error.WriteLine($"Error processing '{pdfPath}': {ex.Message}"); + } + }); + + Console.WriteLine("Attachment extraction completed."); + } +} diff --git a/working-with-attachments/extract-pdf-attachments-sha256.cs b/working-with-attachments/extract-pdf-attachments-sha256.cs new file mode 100644 index 00000000..757c8d23 --- /dev/null +++ b/working-with-attachments/extract-pdf-attachments-sha256.cs @@ -0,0 +1,60 @@ +using System; +using System.IO; +using System.Security.Cryptography; +using Aspose.Pdf; + +class Program +{ + static void Main() + { + const string inputPdf = "input.pdf"; + + if (!File.Exists(inputPdf)) + { + Console.Error.WriteLine($"File not found: {inputPdf}"); + return; + } + + // Load the PDF document (wrapped in using for deterministic disposal) + using (Document doc = new Document(inputPdf)) + { + // Check if the document contains any embedded files (attachments) + if (doc.EmbeddedFiles == null || doc.EmbeddedFiles.Count == 0) + { + Console.WriteLine("No attachments found in the PDF."); + return; + } + + Console.WriteLine($"Found {doc.EmbeddedFiles.Count} attachment(s):"); + + // Iterate over each embedded file using reflection (avoids direct dependency on EmbeddedFile type) + foreach (var attachment in doc.EmbeddedFiles) + { + // Retrieve the attachment name via reflection + var nameProp = attachment.GetType().GetProperty("Name"); + string name = nameProp?.GetValue(attachment) as string ?? ""; + + // Obtain a stream for the embedded file content via reflection + var getStreamMethod = attachment.GetType().GetMethod("GetFileStream"); + if (getStreamMethod == null) + { + Console.WriteLine($"- Name: {name}\n Unable to read file stream (method not found)."); + continue; + } + + using (Stream fileStream = (Stream)getStreamMethod.Invoke(attachment, null)) + { + // Compute SHA-256 hash + using (SHA256 sha256 = SHA256.Create()) + { + byte[] hashBytes = sha256.ComputeHash(fileStream); + string hashHex = BitConverter.ToString(hashBytes).Replace("-", string.Empty).ToLowerInvariant(); + + Console.WriteLine($"- Name: {name}"); + Console.WriteLine($" SHA-256: {hashHex}"); + } + } + } + } + } +} diff --git a/working-with-attachments/extract-pdf-attachments-to-subfolders.cs b/working-with-attachments/extract-pdf-attachments-to-subfolders.cs new file mode 100644 index 00000000..383aa78c --- /dev/null +++ b/working-with-attachments/extract-pdf-attachments-to-subfolders.cs @@ -0,0 +1,97 @@ +using System; +using System.IO; +using System.Reflection; +using Aspose.Pdf; + +class Program +{ + static void Main() + { + const string inputPdfPath = "input.pdf"; // PDF containing attachments + const string outputRoot = "ExtractedAttachments"; // Base folder for extracted files + + if (!File.Exists(inputPdfPath)) + { + Console.Error.WriteLine($"Input file not found: {inputPdfPath}"); + return; + } + + // Ensure the root output directory exists + Directory.CreateDirectory(outputRoot); + + // Load the PDF inside a using block for deterministic disposal + using (Document pdfDoc = new Document(inputPdfPath)) + { + // The EmbeddedFiles collection holds all file attachments in the PDF + var attachments = pdfDoc.EmbeddedFiles; + + if (attachments == null || attachments.Count == 0) + { + Console.WriteLine("No attachments found in the PDF."); + return; + } + + int index = 1; + foreach (var attachment in attachments) + { + // Create a subfolder for each attachment (e.g., Attachment_1, Attachment_2, ...) + string subFolder = Path.Combine(outputRoot, $"Attachment_{index}"); + Directory.CreateDirectory(subFolder); + + // Retrieve the attachment name via reflection (avoids compile‑time dependency on EmbeddedFile type) + string fileName = GetAttachmentName(attachment) ?? $"attachment_{index}"; + + // Combine subfolder path with the file name + string outputFilePath = Path.Combine(subFolder, fileName); + + // Save the attachment to disk using reflection (calls Save(string)) + SaveAttachment(attachment, outputFilePath); + + Console.WriteLine($"Saved attachment #{index} to: {outputFilePath}"); + index++; + } + } + + Console.WriteLine("All attachments have been extracted."); + } + + // Helper: obtains the Name property of an embedded file via reflection + private static string GetAttachmentName(object attachment) + { + var nameProp = attachment.GetType().GetProperty("Name", BindingFlags.Public | BindingFlags.Instance); + if (nameProp != null) + { + var value = nameProp.GetValue(attachment) as string; + if (!string.IsNullOrWhiteSpace(value)) + return value; + } + return null; + } + + // Helper: invokes the Save(string) method via reflection; falls back to stream copy if needed + private static void SaveAttachment(object attachment, string path) + { + var saveMethod = attachment.GetType().GetMethod("Save", new[] { typeof(string) }); + if (saveMethod != null) + { + saveMethod.Invoke(attachment, new object[] { path }); + return; + } + + // Fallback – extract the raw stream from FileSpecification if Save(string) is unavailable + var fileSpecProp = attachment.GetType().GetProperty("FileSpecification"); + var fileSpec = fileSpecProp?.GetValue(attachment); + var contentsProp = fileSpec?.GetType().GetProperty("Contents"); + var stream = contentsProp?.GetValue(fileSpec) as Stream; + if (stream != null) + { + using (var outStream = File.Create(path)) + { + stream.CopyTo(outStream); + } + return; + } + + throw new InvalidOperationException("Unable to save attachment – no suitable Save method or stream found."); + } +} \ No newline at end of file diff --git a/working-with-attachments/flatten-pdf-portfolio.cs b/working-with-attachments/flatten-pdf-portfolio.cs new file mode 100644 index 00000000..58a9ef76 --- /dev/null +++ b/working-with-attachments/flatten-pdf-portfolio.cs @@ -0,0 +1,30 @@ +using System; +using System.IO; +using Aspose.Pdf; + +class Program +{ + static void Main() + { + const string inputPath = "portfolio.pdf"; // PDF Portfolio file + const string outputPath = "flattened.pdf"; // Resulting standard PDF + + if (!File.Exists(inputPath)) + { + Console.Error.WriteLine($"Input file not found: {inputPath}"); + return; + } + + // Load the PDF Portfolio, flatten it, and save as a regular PDF. + using (Document doc = new Document(inputPath)) + { + // Remove interactive form fields and other interactive elements. + doc.Flatten(); + + // Save the flattened document. + doc.Save(outputPath); + } + + Console.WriteLine($"Flattened PDF saved to '{outputPath}'."); + } +} \ No newline at end of file diff --git a/working-with-attachments/generate-csv-report-pdf-attachments.cs b/working-with-attachments/generate-csv-report-pdf-attachments.cs new file mode 100644 index 00000000..157655d6 --- /dev/null +++ b/working-with-attachments/generate-csv-report-pdf-attachments.cs @@ -0,0 +1,64 @@ +using System; +using System.IO; +using Aspose.Pdf; + +class PdfAttachmentReport +{ + // Escapes a CSV field by doubling quotes and surrounding with quotes if needed. + static string EscapeCsv(string field) + { + if (field == null) return ""; + bool mustQuote = field.Contains(",") || field.Contains("\"") || field.Contains("\n") || field.Contains("\r"); + string escaped = field.Replace("\"", "\"\""); + return mustQuote ? $"\"{escaped}\"" : escaped; + } + + static void Main() + { + const string pdfPath = "input.pdf"; // Path to the source PDF + const string csvPath = "attachments_report.csv"; // Output CSV file + + if (!File.Exists(pdfPath)) + { + Console.Error.WriteLine($"PDF file not found: {pdfPath}"); + return; + } + + // Load the PDF document (lifecycle rule: wrap in using for deterministic disposal) + using (Document doc = new Document(pdfPath)) + { + // Open a StreamWriter for the CSV output + using (StreamWriter writer = new StreamWriter(csvPath, false, System.Text.Encoding.UTF8)) + { + // Write CSV header + writer.WriteLine("Name,Size,Description"); + + // Iterate over all embedded file attachments + foreach (FileSpecification attachment in doc.EmbeddedFiles) + { + // Attachment name (may be null) + string name = attachment.Name ?? string.Empty; + + // Description (may be null) + string description = attachment.Description ?? string.Empty; + + // Determine size in bytes. + long size = 0; + if (attachment.Params != null && attachment.Params.Size > 0) + { + size = attachment.Params.Size; + } + else if (attachment.Contents != null && attachment.Contents.CanSeek) + { + size = attachment.Contents.Length; + } + + // Write a CSV line with proper escaping + writer.WriteLine($"{EscapeCsv(name)},{size},{EscapeCsv(description)}"); + } + } + } + + Console.WriteLine($"Attachment report generated: {csvPath}"); + } +} diff --git a/working-with-attachments/generate-pdf-page-thumbnails.cs b/working-with-attachments/generate-pdf-page-thumbnails.cs new file mode 100644 index 00000000..a2897f7d --- /dev/null +++ b/working-with-attachments/generate-pdf-page-thumbnails.cs @@ -0,0 +1,55 @@ +using System; +using System.IO; +using Aspose.Pdf; +using Aspose.Pdf.Devices; // ThumbnailDevice resides here + +class Program +{ + static void Main() + { + const string inputPath = "portfolio.pdf"; // source PDF containing portfolio items + const string outputPath = "portfolio_with_thumbs.pdf"; + + if (!File.Exists(inputPath)) + { + Console.Error.WriteLine($"Input file not found: {inputPath}"); + return; + } + + // Load the source PDF + using (Document srcDoc = new Document(inputPath)) + { + // Create a new PDF that will hold the thumbnails + Document thumbDoc = new Document(); + + // Iterate over each page (portfolio item) in the source PDF + foreach (Page srcPage in srcDoc.Pages) + { + // Create a thumbnail device with desired size (e.g., 150x150 pixels) + ThumbnailDevice thumbDevice = new ThumbnailDevice(150, 150); + + // Render the page to a PNG image stored in a memory stream + using (MemoryStream thumbStream = new MemoryStream()) + { + thumbDevice.Process(srcPage, thumbStream); + thumbStream.Position = 0; // reset stream for reading + + // Add a new page to the thumbnail document + Page thumbPage = thumbDoc.Pages.Add(); + + // Define the rectangle where the thumbnail image will be placed + // (lower‑left X, lower‑left Y, upper‑right X, upper‑right Y) + Aspose.Pdf.Rectangle rect = new Aspose.Pdf.Rectangle(0, 0, 150, 150); + + // Add the thumbnail image to the new page + thumbPage.AddImage(thumbStream, rect); + } + } + + // Save the document containing all thumbnails + thumbDoc.Save(outputPath); + } + + Console.WriteLine($"Thumbnails generated and saved to '{outputPath}'."); + } +} \ No newline at end of file diff --git a/working-with-attachments/index.json b/working-with-attachments/index.json new file mode 100644 index 00000000..703351ba --- /dev/null +++ b/working-with-attachments/index.json @@ -0,0 +1,1114 @@ +{ + "category": "working-with-attachments", + "nuget_version": "26.4.0", + "last_updated": "2026-05-08T07:42:40Z", + "examples": { + "add-file-attachment-to-pdf": { + "title": "Add File Attachment to PDF", + "filename": "add-file-attachment-to-pdf.cs", + "description": "Demonstrates how to load an existing PDF, embed a file using FileSpecification, and create a file attachment annotation on a page.", + "tags": [ + "attachment", + "pdf", + "aspose.pdf", + "file-specification", + "annotation" + ], + "apis_used": [ + "Aspose.Pdf.Document", + "Aspose.Pdf.FileSpecification", + "Aspose.Pdf.Annotations.FileAttachmentAnnotation", + "Aspose.Pdf.Page", + "Aspose.Pdf.Rectangle" + ], + "difficulty": "intermediate", + "generated_at": "2026-05-08T07:42:40Z", + "status": "verified" + }, + "embed-file-attachment-from-byte-array": { + "title": "Embed a File Attachment in PDF from a Byte Array", + "filename": "embed-file-attachment-from-byte-array.cs", + "description": "Shows how to create a FileSpecification from a byte array using a MemoryStream and add it to a PDF as a file attachment annotation.", + "tags": [ + "pdf", + "attachment", + "file-specification", + "memorystream", + "aspose-pdf" + ], + "apis_used": [ + "Aspose.Pdf.Document", + "Aspose.Pdf.Page", + "Aspose.Pdf.Rectangle", + "Aspose.Pdf.FileSpecification", + "Aspose.Pdf.Annotations.FileAttachmentAnnotation", + "Aspose.Pdf.Annotations.FileIcon", + "Aspose.Pdf.Document.Save" + ], + "difficulty": "intermediate", + "generated_at": "2026-05-08T07:42:40Z", + "status": "verified" + }, + "add-multiple-attachments-to-pdf": { + "title": "Add Multiple Attachments to a PDF", + "filename": "add-multiple-attachments-to-pdf.cs", + "description": "Shows how to attach several files to an existing PDF by iterating a list of file paths and saving the updated document.", + "tags": [ + "attachments", + "pdf", + "file-specification", + "embedded-files", + "aspose-pdf" + ], + "apis_used": [ + "Aspose.Pdf.Document", + "Aspose.Pdf.FileSpecification", + "Aspose.Pdf.Document.EmbeddedFiles", + "Aspose.Pdf.Document.Save", + "Aspose.Pdf.EmbeddedFilesCollection.Add" + ], + "difficulty": "beginner", + "generated_at": "2026-05-08T07:42:40Z", + "status": "verified" + }, + "set-custom-description-mime-type-attachments": { + "title": "Set Custom Description and MIME Type for PDF Attachments", + "filename": "set-custom-description-mime-type-attachments.cs", + "description": "Shows how to modify the description and MIME type of embedded file attachments in a PDF using Aspose.Pdf.", + "tags": [ + "attachments", + "metadata", + "pdf", + "aspose-pdf", + "file-specification" + ], + "apis_used": [ + "Aspose.Pdf.Document", + "Aspose.Pdf.Document.Save", + "Aspose.Pdf.EmbeddedFileCollection", + "Aspose.Pdf.FileSpecification", + "Aspose.Pdf.FileSpecification.Description", + "Aspose.Pdf.FileSpecification.MIMEType" + ], + "difficulty": "beginner", + "generated_at": "2026-05-08T07:42:40Z", + "status": "verified" + }, + "add-attachments-to-pdf-save-output": { + "title": "Add Attachments to PDF and Save to Output Folder", + "filename": "add-attachments-to-pdf-save-output.cs", + "description": "Demonstrates how to embed files as attachments in a PDF document using Aspose.Pdf and save the modified PDF to a specified output directory.", + "tags": [ + "attachments", + "pdf", + "save", + "output-folder", + "aspose-pdf" + ], + "apis_used": [ + "Aspose.Pdf.Document", + "Aspose.Pdf.FileSpecification", + "Aspose.Pdf.EmbeddedFiles", + "Aspose.Pdf.Document.Save", + "Aspose.Pdf.EmbeddedFiles.Add" + ], + "difficulty": "beginner", + "generated_at": "2026-05-08T07:42:40Z", + "status": "verified" + }, + "list-pdf-attachments-names-sizes": { + "title": "List PDF Attachments with Names and Sizes", + "filename": "list-pdf-attachments-names-sizes.cs", + "description": "Shows how to load a PDF, access its embedded files collection, and iterate through each attachment to output its name and size in bytes.", + "tags": [ + "attachments", + "embedded-files", + "pdf", + "aspose-pdf", + "file-specification" + ], + "apis_used": [ + "Aspose.Pdf.Document", + "Aspose.Pdf.EmbeddedFileCollection", + "Aspose.Pdf.FileSpecification", + "Aspose.Pdf.EmbeddedFileCollection.FindByName", + "Aspose.Pdf.FileSpecification.Params" + ], + "difficulty": "beginner", + "generated_at": "2026-05-08T07:42:40Z", + "status": "verified" + }, + "extract-embedded-attachment-from-pdf": { + "title": "Extract Embedded Attachment from PDF", + "filename": "extract-embedded-attachment-from-pdf.cs", + "description": "Demonstrates how to locate an embedded file in a PDF by name and save it to a specified directory using Aspose.Pdf.", + "tags": [ + "pdf", + "attachment", + "extract", + "aspose", + "csharp" + ], + "apis_used": [ + "Aspose.Pdf.Document", + "Aspose.Pdf.EmbeddedFiles", + "Aspose.Pdf.FileSpecification", + "Aspose.Pdf.FileSpecification.Contents", + "Aspose.Pdf.EmbeddedFiles.FindByName" + ], + "difficulty": "beginner", + "generated_at": "2026-05-08T07:42:40Z", + "status": "verified" + }, + "extract-pdf-attachments-to-subfolders": { + "title": "Extract PDF Attachments to Separate Subfolders", + "filename": "extract-pdf-attachments-to-subfolders.cs", + "description": "Loads a PDF, iterates over its embedded file attachments, creates a dedicated subfolder for each, and saves the attachments to disk, using reflection to access the attachment name and save method.", + "tags": [ + "pdf", + "attachments", + "extraction", + "aspose-pdf", + "reflection" + ], + "apis_used": [ + "Aspose.Pdf.Document", + "Aspose.Pdf.EmbeddedFilesCollection", + "Aspose.Pdf.EmbeddedFile", + "Aspose.Pdf.EmbeddedFile.Save", + "Aspose.Pdf.FileSpecification" + ], + "difficulty": "intermediate", + "generated_at": "2026-05-08T07:42:40Z", + "status": "verified" + }, + "remove-pdf-attachment-by-index": { + "title": "Remove PDF Attachment by Index", + "filename": "remove-pdf-attachment-by-index.cs", + "description": "Shows how to delete an embedded file from a PDF using its zero‑based index with Aspose.Pdf.", + "tags": [ + "pdf", + "attachments", + "delete", + "aspose-pdf", + "csharp" + ], + "apis_used": [ + "Aspose.Pdf.Document", + "Aspose.Pdf.Document.EmbeddedFiles", + "Aspose.Pdf.EmbeddedFileCollection", + "Aspose.Pdf.EmbeddedFile", + "Aspose.Pdf.EmbeddedFileCollection.Delete" + ], + "difficulty": "beginner", + "generated_at": "2026-05-08T07:42:40Z", + "status": "verified" + }, + "delete-pdf-attachment-by-filename": { + "title": "Delete PDF Attachment by Filename", + "filename": "delete-pdf-attachment-by-filename.cs", + "description": "Demonstrates how to locate and remove an embedded file from a PDF using its filename with Aspose.Pdf.", + "tags": [ + "pdf", + "attachment", + "delete", + "aspose", + "csharp" + ], + "apis_used": [ + "Aspose.Pdf.Document", + "Aspose.Pdf.EmbeddedFiles", + "Aspose.Pdf.EmbeddedFiles.FindByName", + "Aspose.Pdf.EmbeddedFiles.Delete", + "Aspose.Pdf.Document.Save" + ], + "difficulty": "beginner", + "generated_at": "2026-05-08T07:42:40Z", + "status": "verified" + }, + "extract-pdf-attachment-metadata": { + "title": "Extract PDF Attachment Metadata to JSON", + "filename": "extract-pdf-attachment-metadata.cs", + "description": "Shows how to read file attachment annotations from a PDF, retrieve each attachment's file name and creation date, and serialize the collected metadata to a JSON file.", + "tags": [ + "pdf", + "attachments", + "metadata", + "json", + "aspose" + ], + "apis_used": [ + "Aspose.Pdf.Document", + "Aspose.Pdf.Page", + "Aspose.Pdf.Annotations.Annotation", + "Aspose.Pdf.Annotations.FileAttachmentAnnotation", + "Aspose.Pdf.Annotations.FileSpecification" + ], + "difficulty": "intermediate", + "generated_at": "2026-05-08T07:42:40Z", + "status": "verified" + }, + "update-attachment-description-pdf": { + "title": "Update File Attachment Description in PDF", + "filename": "update-attachment-description-pdf.cs", + "description": "Demonstrates how to locate file attachment annotations in a PDF, modify their description, and re‑save the document using Aspose.Pdf.", + "tags": [ + "attachment", + "pdf", + "description", + "annotations", + "Aspose.Pdf" + ], + "apis_used": [ + "Aspose.Pdf.Document", + "Aspose.Pdf.Page", + "Aspose.Pdf.Annotations.FileAttachmentAnnotation", + "Aspose.Pdf.Annotations.FileSpecification", + "Aspose.Pdf.Document.Save" + ], + "difficulty": "intermediate", + "generated_at": "2026-05-08T07:42:40Z", + "status": "verified" + }, + "add-attachment-to-protected-pdf": { + "title": "Add Attachment to Password-Protected PDF", + "filename": "add-attachment-to-protected-pdf.cs", + "description": "Shows how to open an encrypted PDF with a user password, embed a file attachment, and save the document while preserving its encryption.", + "tags": [ + "pdf", + "attachment", + "encryption", + "aspose-pdf" + ], + "apis_used": [ + "Aspose.Pdf.Document", + "Aspose.Pdf.FileSpecification", + "Aspose.Pdf.Document.EmbeddedFiles", + "Aspose.Pdf.Document.Save", + "Aspose.Pdf.EmbeddedFilesCollection.Add" + ], + "difficulty": "beginner", + "generated_at": "2026-05-08T07:42:40Z", + "status": "verified" + }, + "extract-attachments-from-encrypted-pdf": { + "title": "Extract Attachments from Encrypted PDF", + "filename": "extract-attachments-from-encrypted-pdf.cs", + "description": "Demonstrates how to open a password‑protected PDF with Aspose.Pdf, enumerate its embedded file attachments, and save each attachment to a local folder.", + "tags": [ + "pdf", + "attachments", + "encryption", + "extraction", + "aspose-pdf" + ], + "apis_used": [ + "Aspose.Pdf.Document", + "Aspose.Pdf.Document.EmbeddedFiles", + "Aspose.Pdf.EmbeddedFileCollection", + "Aspose.Pdf.FileSpecification", + "Aspose.Pdf.FileSpecification.Contents" + ], + "difficulty": "intermediate", + "generated_at": "2026-05-08T07:42:40Z", + "status": "verified" + }, + "validate-attachment-size-before-adding-to-pdf": { + "title": "Validate Attachment Size Before Adding to PDF", + "filename": "validate-attachment-size-before-adding-to-pdf.cs", + "description": "The example checks a file's size against a defined limit before embedding it as a file attachment annotation in a PDF using Aspose.Pdf.", + "tags": [ + "attachment", + "size-validation", + "pdf", + "aspose", + "annotation" + ], + "apis_used": [ + "Aspose.Pdf.Document", + "Aspose.Pdf.Page", + "Aspose.Pdf.Rectangle", + "Aspose.Pdf.FileSpecification", + "Aspose.Pdf.Annotations.FileAttachmentAnnotation" + ], + "difficulty": "intermediate", + "generated_at": "2026-05-08T07:42:40Z", + "status": "verified" + }, + "batch-add-attachment-to-pdfs": { + "title": "Batch Add Attachment to PDFs", + "filename": "batch-add-attachment-to-pdfs.cs", + "description": "Shows how to iterate over a folder of PDF files and embed the same attachment into each document using Aspose.Pdf.", + "tags": [ + "pdf", + "attachment", + "batch", + "aspose", + "csharp" + ], + "apis_used": [ + "Aspose.Pdf.Document", + "Aspose.Pdf.FileSpecification", + "Aspose.Pdf.EmbeddedFilesCollection", + "Aspose.Pdf.EmbeddedFilesCollection.Add", + "Aspose.Pdf.Document.Save" + ], + "difficulty": "intermediate", + "generated_at": "2026-05-08T07:42:40Z", + "status": "verified" + }, + "batch-extract-pdf-attachments-zip": { + "title": "Batch Extract PDF Attachments to ZIP Archive", + "filename": "batch-extract-pdf-attachments-zip.cs", + "description": "Demonstrates how to iterate through multiple PDF files, collect file‑attachment annotations, and store each embedded file in a consolidated ZIP archive.", + "tags": [ + "pdf", + "attachments", + "batch", + "zip", + "aspose" + ], + "apis_used": [ + "Aspose.Pdf.Document", + "Aspose.Pdf.Page", + "Aspose.Pdf.Annotations.FileAttachmentAnnotation", + "Aspose.Pdf.FileSpecification", + "Aspose.Pdf.Annotations.Annotation" + ], + "difficulty": "intermediate", + "generated_at": "2026-05-08T07:42:40Z", + "status": "verified" + }, + "create-pdf-portfolio-with-attachments": { + "title": "Create PDF Portfolio with Attachments", + "filename": "create-pdf-portfolio-with-attachments.cs", + "description": "Shows how to create an empty PDF document, initialize its collection, add files as attachments using FileSpecification, and save the result as a PDF portfolio.", + "tags": [ + "pdf-portfolio", + "attachments", + "file-specification", + "aspose-pdf", + "document" + ], + "apis_used": [ + "Aspose.Pdf.Document", + "Aspose.Pdf.Collection", + "Aspose.Pdf.FileSpecification", + "Aspose.Pdf.Document.Save" + ], + "difficulty": "intermediate", + "generated_at": "2026-05-08T07:42:40Z", + "status": "verified" + }, + "add-word-document-to-pdf-portfolio": { + "title": "Add Word Document to PDF Portfolio", + "filename": "add-word-document-to-pdf-portfolio.cs", + "description": "Shows how to embed a Word (.docx) file into a PDF portfolio by creating a FileSpecification and adding it to the Document.Collection using Aspose.Pdf.", + "tags": [ + "pdf-portfolio", + "attachment", + "word", + "aspose-pdf", + "csharp" + ], + "apis_used": [ + "Aspose.Pdf.Document", + "Aspose.Pdf.Collection", + "Aspose.Pdf.FileSpecification", + "Aspose.Pdf.Document.Save", + "Aspose.Pdf.Collection.Add" + ], + "difficulty": "beginner", + "generated_at": "2026-05-08T07:42:40Z", + "status": "verified" + }, + "insert-excel-into-pdf-portfolio": { + "title": "Insert Excel Workbook into PDF Portfolio with Description", + "filename": "insert-excel-into-pdf-portfolio.cs", + "description": "Shows how to embed an existing Excel workbook into a PDF portfolio and assign a custom description using Aspose.Pdf.", + "tags": [ + "pdf-portfolio", + "embed-attachment", + "excel", + "aspose-pdf", + "file-specification" + ], + "apis_used": [ + "Aspose.Pdf.Document", + "Aspose.Pdf.FileSpecification", + "Aspose.Pdf.Document.Pages.Add", + "Aspose.Pdf.Document.Save", + "Aspose.Pdf.Document.EmbeddedFiles.Add" + ], + "difficulty": "beginner", + "generated_at": "2026-05-08T07:42:40Z", + "status": "verified" + }, + "embed-image-into-pdf-portfolio": { + "title": "Embed Image into PDF Portfolio with Display Name", + "filename": "embed-image-into-pdf-portfolio.cs", + "description": "Shows how to create a PDF portfolio, embed an image file, and assign a custom display name to the embedded file using Aspose.Pdf.", + "tags": [ + "pdf-portfolio", + "embed-attachment", + "display-name", + "aspose-pdf", + "csharp" + ], + "apis_used": [ + "Aspose.Pdf.Document", + "Aspose.Pdf.FileSpecification", + "Aspose.Pdf.Document.Pages.Add", + "Aspose.Pdf.Document.EmbeddedFiles.Add", + "Aspose.Pdf.Document.Save" + ], + "difficulty": "beginner", + "generated_at": "2026-05-08T07:42:40Z", + "status": "verified" + }, + "add-nested-pdf-to-portfolio": { + "title": "Add Nested PDF to Existing PDF Portfolio", + "filename": "add-nested-pdf-to-portfolio.cs", + "description": "Demonstrates how to embed a PDF file as a nested item inside an existing PDF Portfolio using Aspose.Pdf by creating a FileSpecification and adding it to the portfolio's EmbeddedFiles collection.", + "tags": [ + "pdf-portfolio", + "embedded-files", + "aspose-pdf", + "csharp", + "file-specification" + ], + "apis_used": [ + "Aspose.Pdf.Document", + "Aspose.Pdf.FileSpecification", + "Aspose.Pdf.Document.EmbeddedFiles", + "Aspose.Pdf.EmbeddedFilesCollection.Add", + "Aspose.Pdf.Document.Save" + ], + "difficulty": "beginner", + "generated_at": "2026-05-08T07:42:40Z", + "status": "verified" + }, + "add-multiple-files-to-pdf-portfolio": { + "title": "Add Multiple Files to a PDF Portfolio", + "filename": "add-multiple-files-to-pdf-portfolio.cs", + "description": "Demonstrates embedding various file types into a single PDF portfolio using a loop with Aspose.Pdf.", + "tags": [ + "pdf", + "portfolio", + "attachments", + "embed", + "aspose" + ], + "apis_used": [ + "Aspose.Pdf.Document", + "Aspose.Pdf.FileSpecification", + "Aspose.Pdf.Document.EmbeddedFiles", + "Aspose.Pdf.EmbeddedFilesCollection.Add", + "Aspose.Pdf.Document.Save" + ], + "difficulty": "beginner", + "generated_at": "2026-05-08T07:42:40Z", + "status": "verified" + }, + "compress-pdf-portfolio-using-optimization-options": { + "title": "Compress PDF Portfolio Using Optimization Options", + "filename": "compress-pdf-portfolio-using-optimization-options.cs", + "description": "Loads an existing PDF portfolio, enables object compression via OptimizationOptions, and saves the compressed PDF.", + "tags": [ + "pdf", + "compression", + "portfolio", + "optimization", + "aspose-pdf" + ], + "apis_used": [ + "Aspose.Pdf.Document", + "Aspose.Pdf.Optimization.OptimizationOptions", + "Aspose.Pdf.Document.OptimizeResources", + "Aspose.Pdf.Document.Save" + ], + "difficulty": "beginner", + "generated_at": "2026-05-08T07:42:40Z", + "status": "verified" + }, + "extract-files-from-pdf-portfolio": { + "title": "Extract Files from PDF Portfolio", + "filename": "extract-files-from-pdf-portfolio.cs", + "description": "Demonstrates how to enumerate embedded files in a PDF portfolio using Aspose.Pdf, preserve their original folder structure, and write each file to a target directory. The example also shows a fallback method to extract file streams when the Save method is unavailable.", + "tags": [ + "pdf", + "portfolio", + "extraction", + "attachments", + "aspnet" + ], + "apis_used": [ + "Aspose.Pdf.Document", + "Aspose.Pdf.Document.EmbeddedFiles", + "Aspose.Pdf.EmbeddedFile.Save(string)", + "Aspose.Pdf.FileSpecification", + "Aspose.Pdf.FileSpecification.Contents" + ], + "difficulty": "intermediate", + "generated_at": "2026-05-08T07:42:40Z", + "status": "verified" + }, + "extract-embedded-file-by-index": { + "title": "Extract Embedded File from PDF by Index", + "filename": "extract-embedded-file-by-index.cs", + "description": "Loads a PDF document, retrieves an embedded file at a given 1‑based index, and saves it to disk preserving its original name and extension.", + "tags": [ + "pdf", + "embedded-files", + "extraction", + "aspose.pdf", + "c#" + ], + "apis_used": [ + "Aspose.Pdf.Document", + "Aspose.Pdf.FileSpecification", + "Aspose.Pdf.Document.EmbeddedFiles", + "Aspose.Pdf.FileSpecification.Contents", + "Aspose.Pdf.FileSpecification.Name" + ], + "difficulty": "intermediate", + "generated_at": "2026-05-08T07:42:40Z", + "status": "verified" + }, + "remove-embedded-file-from-pdf-portfolio-by-index": { + "title": "Remove Embedded File from PDF Portfolio by Index", + "filename": "remove-embedded-file-from-pdf-portfolio-by-index.cs", + "description": "Shows how to delete an embedded file from a PDF portfolio using its zero‑based collection index with Aspose.Pdf.", + "tags": [ + "pdf", + "portfolio", + "embedded-file", + "delete", + "aspnet" + ], + "apis_used": [ + "Aspose.Pdf.Document", + "Aspose.Pdf.Document.EmbeddedFiles", + "Aspose.Pdf.EmbeddedFileCollection", + "Aspose.Pdf.EmbeddedFileCollection.Delete", + "Aspose.Pdf.Document.Save" + ], + "difficulty": "beginner", + "generated_at": "2026-05-08T07:42:40Z", + "status": "verified" + }, + "delete-pdf-outline-item-by-description": { + "title": "Delete PDF Outline Item by Description", + "filename": "delete-pdf-outline-item-by-description.cs", + "description": "Shows how to remove a PDF outline (bookmark) whose title matches a given string using Aspose.Pdf.", + "tags": [ + "pdf", + "outline", + "bookmark", + "delete", + "aspose" + ], + "apis_used": [ + "Aspose.Pdf.Document", + "Aspose.Pdf.OutlineCollection", + "Aspose.Pdf.OutlineCollection.Delete", + "Aspose.Pdf.Document.Save" + ], + "difficulty": "beginner", + "generated_at": "2026-05-08T07:42:40Z", + "status": "verified" + }, + "reorder-pdf-bookmarks": { + "title": "Reorder PDF Bookmarks (Outline)", + "filename": "reorder-pdf-bookmarks.cs", + "description": "Shows how to change the order of PDF outline items (bookmarks) by extracting them, clearing the outline collection, and re‑adding them in a custom sequence using Aspose.Pdf.", + "tags": [ + "pdf", + "bookmarks", + "outline", + "reorder", + "aspose-pdf" + ], + "apis_used": [ + "Aspose.Pdf.Document", + "Aspose.Pdf.OutlineCollection", + "Aspose.Pdf.OutlineItemCollection", + "Aspose.Pdf.OutlineCollection.Clear", + "Aspose.Pdf.OutlineCollection.Add", + "Aspose.Pdf.Document.Save" + ], + "difficulty": "intermediate", + "generated_at": "2026-05-08T07:42:40Z", + "status": "verified" + }, + "apply-custom-template-to-pdf-portfolio": { + "title": "Apply Custom Visual Template to PDF Portfolio", + "filename": "apply-custom-template-to-pdf-portfolio.cs", + "description": "Loads an existing PDF portfolio, adds a semi‑transparent background rectangle and a header text to each page using Aspose.Pdf.Drawing classes, and saves the modified document.", + "tags": [ + "pdf-portfolio", + "custom-template", + "graph", + "rectangle", + "textfragment" + ], + "apis_used": [ + "Aspose.Pdf.Document", + "Aspose.Pdf.Page", + "Aspose.Pdf.Drawing.Graph", + "Aspose.Pdf.Drawing.Rectangle", + "Aspose.Pdf.Drawing.GraphInfo", + "Aspose.Pdf.Text.TextFragment", + "Aspose.Pdf.Document.Save" + ], + "difficulty": "intermediate", + "generated_at": "2026-05-08T07:42:40Z", + "status": "verified" + }, + "generate-pdf-page-thumbnails": { + "title": "Generate Thumbnails for PDF Portfolio Items", + "filename": "generate-pdf-page-thumbnails.cs", + "description": "The example loads a PDF, renders each page to a PNG thumbnail using the Drawing API, and assembles those thumbnails into a new PDF document.", + "tags": [ + "thumbnail", + "pdf", + "aspose-pdf", + "image", + "pages" + ], + "apis_used": [ + "Aspose.Pdf.Document", + "Aspose.Pdf.Page", + "Aspose.Pdf.Devices.ThumbnailDevice", + "Aspose.Pdf.Page.AddImage", + "Aspose.Pdf.Rectangle", + "Aspose.Pdf.Document.Save" + ], + "difficulty": "intermediate", + "generated_at": "2026-05-08T07:42:40Z", + "status": "verified" + }, + "merge-pdf-portfolios-preserving-metadata": { + "title": "Merge PDF Portfolios Preserving Metadata", + "filename": "merge-pdf-portfolios-preserving-metadata.cs", + "description": "Shows how to combine two PDF portfolio files into a single PDF while retaining all embedded files and their metadata using Aspose.Pdf.", + "tags": [ + "pdf", + "portfolio", + "merge", + "metadata", + "attachments" + ], + "apis_used": [ + "Aspose.Pdf.Document", + "Aspose.Pdf.Document.MergeDocuments", + "Aspose.Pdf.Document.Save" + ], + "difficulty": "beginner", + "generated_at": "2026-05-08T07:42:40Z", + "status": "verified" + }, + "create-pdf-portfolio-embed-files": { + "title": "Create PDF Portfolio by Embedding Files", + "filename": "create-pdf-portfolio-embed-files.cs", + "description": "Shows how to convert a regular PDF into a PDF Portfolio by adding multiple embedded files using Aspose.Pdf.", + "tags": [ + "pdf-portfolio", + "embedded-files", + "aspose-pdf", + "file-attachment", + "document" + ], + "apis_used": [ + "Aspose.Pdf.Document", + "Aspose.Pdf.FileSpecification", + "Aspose.Pdf.Document.EmbeddedFiles", + "Aspose.Pdf.EmbeddedFilesCollection.Add", + "Aspose.Pdf.Document.Save" + ], + "difficulty": "intermediate", + "generated_at": "2026-05-08T07:42:40Z", + "status": "verified" + }, + "flatten-pdf-portfolio": { + "title": "Flatten PDF Portfolio to Standard PDF", + "filename": "flatten-pdf-portfolio.cs", + "description": "Shows how to load a PDF Portfolio, flatten it to remove interactive collection features, and save it as a regular PDF using Aspose.Pdf.", + "tags": [ + "pdf", + "portfolio", + "flatten", + "aspose.pdf", + "document" + ], + "apis_used": [ + "Aspose.Pdf.Document", + "Aspose.Pdf.Document.Flatten", + "Aspose.Pdf.Document.Save" + ], + "difficulty": "beginner", + "generated_at": "2026-05-08T07:42:40Z", + "status": "verified" + }, + "read-pdf-metadata-attachment-count": { + "title": "Read PDF Metadata and Count Attachments", + "filename": "read-pdf-metadata-attachment-count.cs", + "description": "Shows how to open a PDF with Aspose.Pdf, extract standard metadata fields and determine the number of embedded file attachments, then write a simple text report.", + "tags": [ + "metadata", + "attachments", + "report", + "aspdf", + "csharp" + ], + "apis_used": [ + "Aspose.Pdf.Document", + "Aspose.Pdf.DocumentInfo", + "Aspose.Pdf.EmbeddedFiles", + "Aspose.Pdf.EmbeddedFile", + "Aspose.Pdf.DocumentInfo.CreationDate" + ], + "difficulty": "beginner", + "generated_at": "2026-05-08T07:42:40Z", + "status": "verified" + }, + "generate-csv-report-pdf-attachments": { + "title": "Generate CSV Report of PDF Attachments", + "filename": "generate-csv-report-pdf-attachments.cs", + "description": "Loads a PDF document, enumerates its embedded file attachments, extracts each attachment's name, size, and description, and writes the information to a CSV file.", + "tags": [ + "pdf", + "attachments", + "csv", + "report", + "aspose.pdf" + ], + "apis_used": [ + "Aspose.Pdf.Document", + "Aspose.Pdf.Document.EmbeddedFiles", + "Aspose.Pdf.FileSpecification", + "Aspose.Pdf.FileSpecification.Params", + "Aspose.Pdf.FileSpecification.Contents" + ], + "difficulty": "intermediate", + "generated_at": "2026-05-08T07:42:40Z", + "status": "verified" + }, + "add-in-memory-attachment-to-pdf": { + "title": "Add In-Memory Attachment to PDF", + "filename": "add-in-memory-attachment-to-pdf.cs", + "description": "Demonstrates embedding a file into an existing PDF using a MemoryStream, avoiding intermediate disk files, and saving the updated document.", + "tags": [ + "pdf", + "attachment", + "memory-stream", + "aspose-pdf", + "csharp" + ], + "apis_used": [ + "Aspose.Pdf.Document", + "Aspose.Pdf.FileSpecification", + "Aspose.Pdf.EmbeddedFilesCollection.Add", + "Aspose.Pdf.Document.Save" + ], + "difficulty": "intermediate", + "generated_at": "2026-05-08T07:42:40Z", + "status": "verified" + }, + "attach-remote-file-to-pdf": { + "title": "Attach Remote File to PDF as a File Annotation", + "filename": "attach-remote-file-to-pdf.cs", + "description": "Downloads a file from a remote URL into memory and embeds it in a PDF as a file‑attachment annotation using Aspose.Pdf.", + "tags": [ + "attachment", + "pdf", + "http", + "annotation", + "aspose" + ], + "apis_used": [ + "Aspose.Pdf.Document", + "Aspose.Pdf.Page", + "Aspose.Pdf.Rectangle", + "Aspose.Pdf.FileSpecification", + "Aspose.Pdf.Annotations.FileAttachmentAnnotation", + "Aspose.Pdf.Annotations.FileIcon", + "Aspose.Pdf.Document.Save" + ], + "difficulty": "intermediate", + "generated_at": "2026-05-08T07:42:40Z", + "status": "verified" + }, + "add-file-attachment-to-pdf__v2": { + "title": "Add File Attachment to PDF with Error Handling", + "filename": "add-file-attachment-to-pdf__v2.cs", + "description": "Demonstrates loading a PDF, checking for missing files, creating a file attachment annotation, and saving the updated document using Aspose.Pdf.", + "tags": [ + "attachment", + "pdf", + "error-handling", + "annotation", + "aspose" + ], + "apis_used": [ + "Aspose.Pdf.Document", + "Aspose.Pdf.Page", + "Aspose.Pdf.Rectangle", + "Aspose.Pdf.FileSpecification", + "Aspose.Pdf.Annotations.FileAttachmentAnnotation", + "Aspose.Pdf.Annotations.FileIcon" + ], + "difficulty": "intermediate", + "generated_at": "2026-05-08T07:42:40Z", + "status": "verified" + }, + "validate-mime-type-add-attachment": { + "title": "Validate MIME Type and Add File Attachment to PDF", + "filename": "validate-mime-type-add-attachment.cs", + "description": "The example verifies that an attachment's MIME type matches its file extension, then embeds the file into a PDF as a file attachment annotation using Aspose.Pdf.", + "tags": [ + "mime-type", + "attachment", + "pdf", + "validation", + "aspose-pdf" + ], + "apis_used": [ + "Aspose.Pdf.Document", + "Aspose.Pdf.FileSpecification", + "Aspose.Pdf.Annotations.FileAttachmentAnnotation", + "Aspose.Pdf.Page", + "Aspose.Pdf.Rectangle", + "Aspose.Pdf.Image.GetMimeType" + ], + "difficulty": "intermediate", + "generated_at": "2026-05-08T07:42:40Z", + "status": "verified" + }, + "measure-pdf-attachment-performance": { + "title": "Measure Performance of Adding, Extracting, and Removing PDF Attachments", + "filename": "measure-pdf-attachment-performance.cs", + "description": "Demonstrates how to add a file attachment annotation, extract attached files, and remove attachments from a PDF while logging the time taken for each operation using Aspose.Pdf.", + "tags": [ + "attachments", + "performance", + "file-attachment", + "annotation", + "pdf" + ], + "apis_used": [ + "Aspose.Pdf.Document", + "Aspose.Pdf.FileSpecification", + "Aspose.Pdf.Annotations.FileAttachmentAnnotation", + "Aspose.Pdf.Page.Annotations.Add", + "Aspose.Pdf.FileAttachmentAnnotation.File.Contents", + "Aspose.Pdf.Page.Annotations.Delete", + "Aspose.Pdf.Document.Save" + ], + "difficulty": "intermediate", + "generated_at": "2026-05-08T07:42:40Z", + "status": "verified" + }, + "embed-attachment-with-metadata-into-pdf": { + "title": "Embed Attachment with Metadata into PDF", + "filename": "embed-attachment-with-metadata-into-pdf.cs", + "description": "Demonstrates how to embed a file into an existing PDF using Aspose.Pdf and add custom attachment metadata to the document information dictionary before saving.", + "tags": [ + "attachment", + "metadata", + "pdf", + "aspose-pdf", + "save-options" + ], + "apis_used": [ + "Aspose.Pdf.Document", + "Aspose.Pdf.FileSpecification", + "Aspose.Pdf.EmbeddedFiles.Add", + "Aspose.Pdf.DocumentInfo.Add", + "Aspose.Pdf.PdfSaveOptions", + "Aspose.Pdf.Document.Save" + ], + "difficulty": "intermediate", + "generated_at": "2026-05-08T07:42:40Z", + "status": "verified" + }, + "add-unicode-attachment-to-pdf": { + "title": "Add Unicode File Attachment to PDF", + "filename": "add-unicode-attachment-to-pdf.cs", + "description": "Demonstrates embedding a file with a Unicode filename as a file attachment annotation in a PDF using Aspose.Pdf and verifies the annotation count.", + "tags": [ + "attachment", + "unicode", + "annotation", + "pdf", + "aspose-pdf" + ], + "apis_used": [ + "Aspose.Pdf.Document", + "Aspose.Pdf.Page", + "Aspose.Pdf.Rectangle", + "Aspose.Pdf.FileSpecification", + "Aspose.Pdf.Annotations.FileAttachmentAnnotation", + "Aspose.Pdf.Annotations.FileIcon", + "Aspose.Pdf.Color" + ], + "difficulty": "intermediate", + "generated_at": "2026-05-08T07:42:40Z", + "status": "verified" + }, + "extract-pdf-attachments-sha256": { + "title": "Extract PDF Attachments and Compute SHA-256 Hashes", + "filename": "extract-pdf-attachments-sha256.cs", + "description": "Loads a PDF, enumerates its embedded files, reads each attachment stream, and calculates a SHA‑256 hash for integrity verification.", + "tags": [ + "pdf", + "attachments", + "sha256", + "hash", + "aspose" + ], + "apis_used": [ + "Aspose.Pdf.Document", + "Aspose.Pdf.Document.EmbeddedFiles", + "Aspose.Pdf.EmbeddedFileCollection", + "Aspose.Pdf.EmbeddedFile", + "Aspose.Pdf.EmbeddedFile.GetFileStream" + ], + "difficulty": "intermediate", + "generated_at": "2026-05-08T07:42:40Z", + "status": "verified" + }, + "batch-add-watermark-to-pdf-pages": { + "title": "Batch Add Watermark to PDF Pages", + "filename": "batch-add-watermark-to-pdf-pages.cs", + "description": "Loads a PDF, iterates through all pages, creates a centered red text watermark using WatermarkArtifact, adds it to each page (optionally only pages with file‑attachment annotations), and saves the result.", + "tags": [ + "watermark", + "pdf", + "attachments", + "Aspose.Pdf", + "pages" + ], + "apis_used": [ + "Aspose.Pdf.Document", + "Aspose.Pdf.Page", + "Aspose.Pdf.WatermarkArtifact", + "Aspose.Pdf.Text.TextState", + "Aspose.Pdf.FontRepository.FindFont", + "Aspose.Pdf.Page.Artifacts.Add", + "Aspose.Pdf.Document.Save" + ], + "difficulty": "intermediate", + "generated_at": "2026-05-08T07:42:40Z", + "status": "verified" + }, + "add-file-attachment-with-retry": { + "title": "Add File Attachment to PDF with Retry", + "filename": "add-file-attachment-with-retry.cs", + "description": "Demonstrates how to attach a file to the first page of a PDF stored on a network share and retry the operation on transient I/O or access errors using exponential back‑off.", + "tags": [ + "pdf", + "attachment", + "retry", + "network-share", + "aspose-pdf" + ], + "apis_used": [ + "Aspose.Pdf.Document", + "Aspose.Pdf.Page", + "Aspose.Pdf.FileSpecification", + "Aspose.Pdf.Annotations.FileAttachmentAnnotation", + "Aspose.Pdf.Rectangle", + "Aspose.Pdf.Document.Save" + ], + "difficulty": "intermediate", + "generated_at": "2026-05-08T07:42:40Z", + "status": "verified" + }, + "extract-pdf-attachments-in-parallel": { + "title": "Extract PDF Attachments in Parallel", + "filename": "extract-pdf-attachments-in-parallel.cs", + "description": "Demonstrates how to load multiple PDF files concurrently, enumerate their embedded files, and save each attachment to disk using Aspose.Pdf.", + "tags": [ + "pdf", + "attachments", + "parallel", + "extraction", + "aspnet" + ], + "apis_used": [ + "Aspose.Pdf.Document", + "Aspose.Pdf.Document.EmbeddedFiles", + "Aspose.Pdf.EmbeddedFile", + "Aspose.Pdf.EmbeddedFile.Save", + "Aspose.Pdf.EmbeddedFilesCollection" + ], + "difficulty": "intermediate", + "generated_at": "2026-05-08T07:42:40Z", + "status": "verified" + }, + "verify-pdf-size-after-attachment": { + "title": "Verify PDF Size Increases After Adding File Attachment", + "filename": "verify-pdf-size-after-attachment.cs", + "description": "The example creates a simple PDF, adds a file attachment annotation, saves the modified document, and asserts that the resulting file size is larger than the original.", + "tags": [ + "pdf", + "attachment", + "unit-test", + "nunit", + "aspose-pdf" + ], + "apis_used": [ + "Aspose.Pdf.Document", + "Aspose.Pdf.Page", + "Aspose.Pdf.Rectangle", + "Aspose.Pdf.FileSpecification", + "Aspose.Pdf.Annotations.FileAttachmentAnnotation", + "Aspose.Pdf.Document.Save", + "Aspose.Pdf.Page.Annotations.Add" + ], + "difficulty": "intermediate", + "generated_at": "2026-05-08T07:42:40Z", + "status": "verified" + }, + "test-attachment-removal-updates-collection-count": { + "title": "Test Attachment Removal Updates Collection Count", + "filename": "test-attachment-removal-updates-collection-count.cs", + "description": "Shows an integration test using xUnit that verifies removing an attachment from a ThreadMessageResponse correctly updates the Attachments collection count.", + "tags": [ + "unit-test", + "xunit", + "attachments", + "collection", + "csharp" + ], + "apis_used": [], + "difficulty": "beginner", + "generated_at": "2026-05-08T07:42:40Z", + "status": "verified" + }, + "embed-hidden-xml-metadata-pdf-attachment": { + "title": "Embed Hidden XML Metadata as PDF Attachment", + "filename": "embed-hidden-xml-metadata-pdf-attachment.cs", + "description": "Shows how to serialize XML metadata to a memory stream and embed it as a hidden file attachment annotation inside a PDF using Aspose.Pdf.", + "tags": [ + "pdf", + "metadata", + "attachment", + "xml", + "hidden-annotation" + ], + "apis_used": [ + "Aspose.Pdf.Document", + "Aspose.Pdf.Page", + "Aspose.Pdf.FileSpecification", + "Aspose.Pdf.Annotations.FileAttachmentAnnotation", + "Aspose.Pdf.Annotations.AnnotationFlags", + "Aspose.Pdf.Rectangle" + ], + "difficulty": "intermediate", + "generated_at": "2026-05-08T07:42:40Z", + "status": "verified" + } + } +} \ No newline at end of file diff --git a/working-with-attachments/insert-excel-into-pdf-portfolio.cs b/working-with-attachments/insert-excel-into-pdf-portfolio.cs new file mode 100644 index 00000000..507d3a89 --- /dev/null +++ b/working-with-attachments/insert-excel-into-pdf-portfolio.cs @@ -0,0 +1,40 @@ +using System; +using System.IO; +using Aspose.Pdf; + +class Program +{ + static void Main() + { + // Paths – adjust as necessary + const string excelPath = "Report.xlsx"; // Excel workbook to embed + const string outputPdf = "PortfolioWithExcel.pdf"; + + // Verify the Excel file exists + if (!File.Exists(excelPath)) + { + Console.Error.WriteLine($"Excel file not found: {excelPath}"); + return; + } + + // Create a new PDF document (portfolio) and add a blank page + Document pdfDoc = new Document(); + pdfDoc.Pages.Add(); // a page makes the PDF viewable + + // Create a FileSpecification for the Excel workbook + // First argument – file name as it will appear in the portfolio + // Second argument – custom description shown in the UI + var fileSpec = new FileSpecification("Report.xlsx", "Quarterly financial report – Excel workbook"); + + // Assign the file contents via a stream (required overload) + fileSpec.Contents = new MemoryStream(File.ReadAllBytes(excelPath)); + + // Add the specification to the document's embedded files collection + pdfDoc.EmbeddedFiles.Add(fileSpec); + + // Save the resulting PDF portfolio + pdfDoc.Save(outputPdf); + + Console.WriteLine($"PDF portfolio created: {outputPdf}"); + } +} diff --git a/working-with-attachments/list-pdf-attachments-names-sizes.cs b/working-with-attachments/list-pdf-attachments-names-sizes.cs new file mode 100644 index 00000000..0d25faf6 --- /dev/null +++ b/working-with-attachments/list-pdf-attachments-names-sizes.cs @@ -0,0 +1,45 @@ +using System; +using System.IO; +using Aspose.Pdf; + +class Program +{ + static void Main() + { + const string pdfPath = "input.pdf"; + + if (!File.Exists(pdfPath)) + { + Console.Error.WriteLine($"File not found: {pdfPath}"); + return; + } + + // Load the PDF document (using rule for disposal) + using (Document doc = new Document(pdfPath)) + { + // Access the embedded files collection (attachments) + EmbeddedFileCollection attachments = doc.EmbeddedFiles; + + if (attachments == null || attachments.Count == 0) + { + Console.WriteLine("No attachments found in the PDF."); + return; + } + + // Iterate over the attachment keys and display name and size + foreach (string key in attachments.Keys) + { + // Retrieve the file specification for the current key + FileSpecification fileSpec = attachments.FindByName(key); + + // Size in bytes – use Params.Size (Data property does not exist) + long size = fileSpec?.Params?.Size ?? 0; + + // Use the specification's Name if available, otherwise fall back to the key + string name = fileSpec?.Name ?? key; + + Console.WriteLine($"Attachment: {name}, Size: {size} bytes"); + } + } + } +} diff --git a/working-with-attachments/measure-pdf-attachment-performance.cs b/working-with-attachments/measure-pdf-attachment-performance.cs new file mode 100644 index 00000000..b5165c8e --- /dev/null +++ b/working-with-attachments/measure-pdf-attachment-performance.cs @@ -0,0 +1,107 @@ +using System; +using System.Diagnostics; +using System.IO; +using Aspose.Pdf; +using Aspose.Pdf.Annotations; + +class AttachmentPerformanceDemo +{ + static void Main() + { + const string inputPdf = "sample.pdf"; + const string outputPdf = "sample_with_attachments.pdf"; + const string attachmentFile = "example.txt"; + + if (!File.Exists(inputPdf)) + { + Console.Error.WriteLine($"Input PDF not found: {inputPdf}"); + return; + } + + if (!File.Exists(attachmentFile)) + { + Console.Error.WriteLine($"Attachment file not found: {attachmentFile}"); + return; + } + + // ---------- Add attachment ---------- + var swAdd = Stopwatch.StartNew(); + using (Document doc = new Document(inputPdf)) + { + // Create a file specification for the attachment + var fileSpec = new FileSpecification(attachmentFile); + + // Define the rectangle where the annotation will appear (coordinates in points) + var rect = new Aspose.Pdf.Rectangle(100, 500, 150, 550); + + // Create the file attachment annotation on the first page + var attachment = new FileAttachmentAnnotation(doc.Pages[1], rect, fileSpec) + { + // Use the FileIcon enum instead of a string + Icon = FileIcon.PushPin, + Contents = "Attached example.txt", + Color = Aspose.Pdf.Color.Blue + }; + + // Add the annotation to the page + doc.Pages[1].Annotations.Add(attachment); + + // Save the modified document + doc.Save(outputPdf); + } + swAdd.Stop(); + Console.WriteLine($"Add attachment time: {swAdd.ElapsedMilliseconds} ms"); + + // ---------- Extract attachment ---------- + var swExtract = Stopwatch.StartNew(); + using (Document doc = new Document(outputPdf)) + { + foreach (Page page in doc.Pages) + { + // Annotations collection uses 1‑based indexing + for (int i = 1; i <= page.Annotations.Count; i++) + { + Annotation ann = page.Annotations[i]; + if (ann is FileAttachmentAnnotation fileAnn) + { + // The FileSpecification contains the original file name + string attachedFileName = fileAnn.File.Name; + Console.WriteLine($"Found attachment: {attachedFileName}"); + + // Save the attached file to disk + using (Stream src = fileAnn.File.Contents) + using (FileStream dst = File.Create($"extracted_{attachedFileName}")) + { + src.CopyTo(dst); + } + } + } + } + } + swExtract.Stop(); + Console.WriteLine($"Extract attachment time: {swExtract.ElapsedMilliseconds} ms"); + + // ---------- Remove attachment ---------- + var swRemove = Stopwatch.StartNew(); + using (Document doc = new Document(outputPdf)) + { + foreach (Page page in doc.Pages) + { + // Iterate backwards when removing items to keep indexes valid + for (int i = page.Annotations.Count; i >= 1; i--) + { + Annotation ann = page.Annotations[i]; + if (ann is FileAttachmentAnnotation) + { + page.Annotations.Delete(i); + } + } + } + + // Save the document without the attachments + doc.Save("sample_without_attachments.pdf"); + } + swRemove.Stop(); + Console.WriteLine($"Remove attachment time: {swRemove.ElapsedMilliseconds} ms"); + } +} diff --git a/working-with-attachments/merge-pdf-portfolios-preserving-metadata.cs b/working-with-attachments/merge-pdf-portfolios-preserving-metadata.cs new file mode 100644 index 00000000..7302a509 --- /dev/null +++ b/working-with-attachments/merge-pdf-portfolios-preserving-metadata.cs @@ -0,0 +1,28 @@ +using System; +using System.IO; +using Aspose.Pdf; + +class Program +{ + static void Main() + { + const string portfolioPath1 = "portfolio1.pdf"; + const string portfolioPath2 = "portfolio2.pdf"; + const string outputPath = "merged_portfolio.pdf"; + + // Verify that both source files exist + if (!File.Exists(portfolioPath1) || !File.Exists(portfolioPath2)) + { + Console.Error.WriteLine("One or both input PDF portfolios were not found."); + return; + } + + // Merge the two PDF portfolios. The static MergeDocuments method + // combines the documents while preserving embedded files and their metadata. + Document mergedDocument = Document.MergeDocuments(portfolioPath1, portfolioPath2); + + // Save the merged result + mergedDocument.Save(outputPath); + Console.WriteLine($"Merged PDF portfolio saved to '{outputPath}'."); + } +} \ No newline at end of file diff --git a/working-with-attachments/read-pdf-metadata-attachment-count.cs b/working-with-attachments/read-pdf-metadata-attachment-count.cs new file mode 100644 index 00000000..24ab3e7e --- /dev/null +++ b/working-with-attachments/read-pdf-metadata-attachment-count.cs @@ -0,0 +1,58 @@ +using System; +using System.IO; +using Aspose.Pdf; + +class Program +{ + static void Main() + { + const string inputPath = "sample.pdf"; + const string reportPath = "report.txt"; + + if (!File.Exists(inputPath)) + { + Console.Error.WriteLine($"File not found: {inputPath}"); + return; + } + + // Use a using block for deterministic disposal (document-disposal-with-using rule) + using (Document doc = new Document(inputPath)) + { + // Access standard metadata via DocumentInfo (doc.Info) + DocumentInfo info = doc.Info; + string title = info.Title ?? "N/A"; + string author = info.Author ?? "N/A"; + string subject = info.Subject ?? "N/A"; + string keywords = info.Keywords ?? "N/A"; + string creator = info.Creator ?? "N/A"; + string producer = info.Producer ?? "N/A"; + // CreationDate and ModDate are non‑nullable DateTime values in Aspose.Pdf, so we format them directly. + string creationDate = info.CreationDate != DateTime.MinValue ? info.CreationDate.ToString("u") : "N/A"; + string modDate = info.ModDate != DateTime.MinValue ? info.ModDate.ToString("u") : "N/A"; + + // Get attachment (embedded file) count. The Attachments property does not exist; use EmbeddedFiles. + int attachmentCount = (doc.EmbeddedFiles != null) ? doc.EmbeddedFiles.Count : 0; + + // Build a simple text report + string report = $"PDF Metadata Report{Environment.NewLine}" + + $"File: {Path.GetFileName(inputPath)}{Environment.NewLine}" + + $"Title: {title}{Environment.NewLine}" + + $"Author: {author}{Environment.NewLine}" + + $"Subject: {subject}{Environment.NewLine}" + + $"Keywords: {keywords}{Environment.NewLine}" + + $"Creator: {creator}{Environment.NewLine}" + + $"Producer: {producer}{Environment.NewLine}" + + $"Creation Date: {creationDate}{Environment.NewLine}" + + $"Modification Date: {modDate}{Environment.NewLine}" + + $"Attachment Count: {attachmentCount}{Environment.NewLine}"; + + // Output to console + Console.WriteLine(report); + + // Save the report to a text file + File.WriteAllText(reportPath, report); + } + + Console.WriteLine($"Report saved to '{reportPath}'."); + } +} diff --git a/working-with-attachments/remove-embedded-file-from-pdf-portfolio-by-index.cs b/working-with-attachments/remove-embedded-file-from-pdf-portfolio-by-index.cs new file mode 100644 index 00000000..905c9ce0 --- /dev/null +++ b/working-with-attachments/remove-embedded-file-from-pdf-portfolio-by-index.cs @@ -0,0 +1,55 @@ +using System; +using System.IO; +using Aspose.Pdf; + +class PortfolioFileRemover +{ + /// + /// Removes an embedded file from a PDF portfolio by its zero‑based collection index. + /// + /// Path to the source PDF containing the portfolio. + /// Zero‑based index of the embedded file to delete. + /// Path where the modified PDF will be saved. + public static void RemoveEmbeddedFileByIndex(string inputPdf, int fileIndex, string outputPdf) + { + if (!File.Exists(inputPdf)) + { + Console.Error.WriteLine($"Input file not found: {inputPdf}"); + return; + } + + // Load the PDF document inside a using block for deterministic disposal. + using (Document doc = new Document(inputPdf)) + { + // Access the collection of embedded files (portfolio). + EmbeddedFileCollection embeddedFiles = doc.EmbeddedFiles; + + // Validate the requested index. + if (fileIndex < 0 || fileIndex >= embeddedFiles.Count) + { + Console.Error.WriteLine($"Invalid index {fileIndex}. Collection contains {embeddedFiles.Count} files."); + return; + } + + // The collection is keyed by the embedded file name. + // Retrieve the name at the specified index and delete by name. + string fileName = embeddedFiles.Keys[fileIndex]; + embeddedFiles.Delete(fileName); + + // Save the modified PDF. + doc.Save(outputPdf); + } + + Console.WriteLine($"Embedded file at index {fileIndex} removed. Saved to '{outputPdf}'."); + } + + // Example usage. + static void Main() + { + const string inputPath = "portfolio.pdf"; + const string outputPath = "portfolio_updated.pdf"; + const int removeIdx = 2; // remove the third file (zero‑based) + + RemoveEmbeddedFileByIndex(inputPath, removeIdx, outputPath); + } +} \ No newline at end of file diff --git a/working-with-attachments/remove-pdf-attachment-by-index.cs b/working-with-attachments/remove-pdf-attachment-by-index.cs new file mode 100644 index 00000000..b9611b1a --- /dev/null +++ b/working-with-attachments/remove-pdf-attachment-by-index.cs @@ -0,0 +1,41 @@ +using System; +using System.IO; +using Aspose.Pdf; + +class Program +{ + static void Main() + { + const string inputPath = "input.pdf"; + const string outputPath = "output.pdf"; + const int attachmentIndex = 0; // zero‑based index of the attachment to remove + + if (!File.Exists(inputPath)) + { + Console.Error.WriteLine($"File not found: {inputPath}"); + return; + } + + // Load the PDF inside a using block for proper disposal + using (Document doc = new Document(inputPath)) + { + // Validate the index against the collection count + if (attachmentIndex < 0 || attachmentIndex >= doc.EmbeddedFiles.Count) + { + Console.Error.WriteLine("Invalid attachment index."); + return; + } + + // Get the embedded file at the specified zero‑based index + var embeddedFile = doc.EmbeddedFiles[attachmentIndex]; + + // EmbeddedFileCollection only provides Delete(string name), so delete by name + doc.EmbeddedFiles.Delete(embeddedFile.Name); + + // Save the updated PDF + doc.Save(outputPath); + } + + Console.WriteLine($"Attachment at index {attachmentIndex} removed. Saved to '{outputPath}'."); + } +} \ No newline at end of file diff --git a/working-with-attachments/reorder-pdf-bookmarks.cs b/working-with-attachments/reorder-pdf-bookmarks.cs new file mode 100644 index 00000000..e21a90e4 --- /dev/null +++ b/working-with-attachments/reorder-pdf-bookmarks.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Aspose.Pdf; + +class Program +{ + static void Main() + { + const string inputPdf = "portfolio.pdf"; // source PDF with outline items + const string outputPdf = "reordered.pdf"; // result PDF + + if (!File.Exists(inputPdf)) + { + Console.Error.WriteLine($"File not found: {inputPdf}"); + return; + } + + // Desired order of outline (bookmark) titles. + // Adjust this array to reflect the sequence you need. + string[] desiredOrder = new[] + { + "Executive Summary", + "Financial Overview", + "Market Analysis", + "Risk Assessment", + "Appendix" + }; + + using (Document doc = new Document(inputPdf)) + { + // Get the document outline (bookmarks) collection. + OutlineCollection outlines = doc.Outlines; + + // Preserve existing items in a list for later lookup. + List existingItems = new List(); + foreach (OutlineItemCollection item in outlines) + existingItems.Add(item); + + // Clear all current outline items. + outlines.Clear(); + + // Re‑add items following the desired order. + foreach (string title in desiredOrder) + { + // Find the original item that matches the title. + OutlineItemCollection original = existingItems + .FirstOrDefault(i => string.Equals(i.Title, title, StringComparison.Ordinal)); + + if (original == null) + { + // Title not found – skip or handle as needed. + Console.WriteLine($"Warning: outline item \"{title}\" not found in source PDF."); + continue; + } + + // Create a new outline item attached to the current OutlineCollection. + OutlineItemCollection newItem = new OutlineItemCollection(outlines) + { + Title = original.Title, + Destination = original.Destination, + Action = original.Action, + Bold = original.Bold, + Italic = original.Italic, + Color = original.Color, + Open = original.Open + }; + + // Add the newly created item to the outline. + outlines.Add(newItem); + } + + // Save the reordered PDF. + doc.Save(outputPdf); + } + + Console.WriteLine($"Reordered PDF saved to \"{outputPdf}\"."); + } +} \ No newline at end of file diff --git a/working-with-attachments/set-custom-description-mime-type-attachments.cs b/working-with-attachments/set-custom-description-mime-type-attachments.cs new file mode 100644 index 00000000..65ab8308 --- /dev/null +++ b/working-with-attachments/set-custom-description-mime-type-attachments.cs @@ -0,0 +1,44 @@ +using System; +using System.IO; +using Aspose.Pdf; // Core Aspose.Pdf namespace + +class Program +{ + static void Main() + { + const string inputPdf = "input.pdf"; + const string outputPdf = "output_with_attachments.pdf"; + + if (!File.Exists(inputPdf)) + { + Console.Error.WriteLine($"File not found: {inputPdf}"); + return; + } + + // Load the PDF document + using (Document doc = new Document(inputPdf)) + { + // Access the collection of embedded files (attachments) + EmbeddedFileCollection attachments = doc.EmbeddedFiles; + + // If there are no attachments, nothing to modify + if (attachments != null && attachments.Count > 0) + { + // Iterate over each attachment and set custom description and MIME type + for (int i = 1; i <= attachments.Count; i++) // 1‑based indexing + { + FileSpecification fileSpec = attachments[i]; + + // Example custom values – replace with your own logic as needed + fileSpec.Description = $"Custom description for {fileSpec.Name}"; + fileSpec.MIMEType = "application/octet-stream"; // generic MIME type; adjust per file type + } + } + + // Save the modified PDF + doc.Save(outputPdf); + } + + Console.WriteLine($"PDF saved with updated attachment metadata: {outputPdf}"); + } +} \ No newline at end of file diff --git a/working-with-attachments/test-attachment-removal-updates-collection-count.cs b/working-with-attachments/test-attachment-removal-updates-collection-count.cs new file mode 100644 index 00000000..5edd353e --- /dev/null +++ b/working-with-attachments/test-attachment-removal-updates-collection-count.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using Xunit; + +namespace AsposePdfAiTests +{ + // --------------------------------------------------------------------------- + // Minimal domain model stubs required for the test. + // --------------------------------------------------------------------------- + public class ThreadMessageResponse + { + public List Attachments { get; set; } = new List(); + } + + public class Attachment + { + public string FileId { get; set; } + public List Tools { get; set; } = new List(); + } + + public class Tool + { + // No members needed for the current test scenario. + } + + // --------------------------------------------------------------------------- + // Integration test that verifies removal of an attachment updates the collection count. + // --------------------------------------------------------------------------- + public class AttachmentRemovalTests + { + [Fact] + public void RemovingAttachment_UpdatesCollectionCount() + { + // Arrange: create a response with an empty Attachments list + var response = new ThreadMessageResponse + { + Attachments = new List() + }; + + // Create two attachments. The Tools property expects a List, + // so we initialise it with an empty list (the actual tools are not relevant for this test). + var att1 = new Attachment + { + FileId = "file1", + Tools = new List() + }; + var att2 = new Attachment + { + FileId = "file2", + Tools = new List() + }; + + // Add the attachments to the collection + response.Attachments.Add(att1); + response.Attachments.Add(att2); + + // Verify the initial count is 2 + Assert.Equal(2, response.Attachments.Count); + + // Act: remove one attachment + response.Attachments.Remove(att1); + + // Verify the count is now 1 + Assert.Equal(1, response.Attachments.Count); + } + } + + // --------------------------------------------------------------------------- + // Provide a dummy entry point so the project compiles as a console application. + // --------------------------------------------------------------------------- + public class Program + { + public static void Main() + { + // No runtime logic required – the test runner will execute the tests. + } + } +} + +// --------------------------------------------------------------------------- +// Minimal stub for the Xunit framework when the real package is not referenced. +// This provides the Fact attribute and the Assert.Equal method used in the test. +// --------------------------------------------------------------------------- +namespace Xunit +{ + [AttributeUsage(AttributeTargets.Method)] + public sealed class FactAttribute : Attribute { } + + public static class Assert + { + public static void Equal(T expected, T actual) + { + if (!object.Equals(expected, actual)) + throw new Exception($"Assert.Equal failed. Expected:<{expected}>. Actual:<{actual}>."); + } + } +} diff --git a/working-with-attachments/update-attachment-description-pdf.cs b/working-with-attachments/update-attachment-description-pdf.cs new file mode 100644 index 00000000..62f5ab86 --- /dev/null +++ b/working-with-attachments/update-attachment-description-pdf.cs @@ -0,0 +1,59 @@ +using System; +using System.IO; +using Aspose.Pdf; +using Aspose.Pdf.Annotations; + +class Program +{ + static void Main() + { + const string inputPath = "input.pdf"; + const string outputPath = "output.pdf"; + const string newDescription = "Updated attachment description"; + + if (!File.Exists(inputPath)) + { + Console.Error.WriteLine($"File not found: {inputPath}"); + return; + } + + // Load the PDF document + using (Document doc = new Document(inputPath)) + { + bool updated = false; + + // Iterate through all pages (1‑based indexing) + for (int pageIndex = 1; pageIndex <= doc.Pages.Count; pageIndex++) + { + Page page = doc.Pages[pageIndex]; + + // Iterate through all annotations on the page (1‑based indexing) + for (int annIndex = 1; annIndex <= page.Annotations.Count; annIndex++) + { + Annotation ann = page.Annotations[annIndex]; + + // Check if the annotation is a file attachment + if (ann is FileAttachmentAnnotation fileAnn) + { + // Ensure the FileSpecification exists before updating + if (fileAnn.File != null) + { + fileAnn.File.Description = newDescription; + updated = true; + } + } + } + } + + if (!updated) + { + Console.WriteLine("No file attachment annotation found in the document."); + } + + // Re‑save the modified PDF + doc.Save(outputPath); + } + + Console.WriteLine($"PDF saved to '{outputPath}'."); + } +} \ No newline at end of file diff --git a/working-with-attachments/validate-attachment-size-before-adding-to-pdf.cs b/working-with-attachments/validate-attachment-size-before-adding-to-pdf.cs new file mode 100644 index 00000000..9c91e8bc --- /dev/null +++ b/working-with-attachments/validate-attachment-size-before-adding-to-pdf.cs @@ -0,0 +1,60 @@ +using System; +using System.IO; +using Aspose.Pdf; +using Aspose.Pdf.Annotations; + +class Program +{ + static void Main() + { + const string inputPdfPath = "input.pdf"; + const string outputPdfPath = "output.pdf"; + const string attachmentPath = "attachment.zip"; + + // Define maximum allowed attachment size (e.g., 5 MB) + const long maxAttachmentSizeBytes = 5L * 1024 * 1024; + + // Verify that the source files exist + if (!File.Exists(inputPdfPath)) + { + Console.Error.WriteLine($"Input PDF not found: {inputPdfPath}"); + return; + } + + if (!File.Exists(attachmentPath)) + { + Console.Error.WriteLine($"Attachment file not found: {attachmentPath}"); + return; + } + + // Check attachment size before adding it to the PDF + long attachmentSize = new FileInfo(attachmentPath).Length; + if (attachmentSize > maxAttachmentSizeBytes) + { + Console.Error.WriteLine($"Attachment exceeds size limit ({maxAttachmentSizeBytes} bytes). Size: {attachmentSize} bytes."); + return; + } + + // Load the PDF, add the attachment, and save + using (Document pdfDoc = new Document(inputPdfPath)) + { + // Use the first page for the annotation (adjust as needed) + Page page = pdfDoc.Pages[1]; + + // Define the rectangle area for the attachment annotation + Aspose.Pdf.Rectangle rect = new Aspose.Pdf.Rectangle(100, 500, 200, 600); + + // Create a FileSpecification for the attachment + FileSpecification fileSpec = new FileSpecification(attachmentPath); + + // Create the FileAttachment annotation and add it to the page + FileAttachmentAnnotation attachment = new FileAttachmentAnnotation(page, rect, fileSpec); + page.Annotations.Add(attachment); + + // Save the modified PDF + pdfDoc.Save(outputPdfPath); + } + + Console.WriteLine($"Attachment added successfully. Output saved to '{outputPdfPath}'."); + } +} \ No newline at end of file diff --git a/working-with-attachments/validate-mime-type-add-attachment.cs b/working-with-attachments/validate-mime-type-add-attachment.cs new file mode 100644 index 00000000..4a9fda37 --- /dev/null +++ b/working-with-attachments/validate-mime-type-add-attachment.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Drawing; +using Aspose.Pdf; +using Aspose.Pdf.Annotations; + +class Program +{ + static void Main() + { + const string inputPdfPath = "input.pdf"; + const string outputPdfPath = "output.pdf"; + const string attachmentPath = "sample.png"; // file to attach + + if (!File.Exists(inputPdfPath)) + { + Console.Error.WriteLine($"Input PDF not found: {inputPdfPath}"); + return; + } + + if (!File.Exists(attachmentPath)) + { + Console.Error.WriteLine($"Attachment file not found: {attachmentPath}"); + return; + } + + // Determine expected MIME type from file extension + string expectedMime = GetMimeTypeFromExtension(Path.GetExtension(attachmentPath)); + + // Determine actual MIME type (for images use Aspose.Pdf.Image.GetMimeType) + string actualMime = GetActualMimeType(attachmentPath); + + // Validate MIME type matches extension + if (!string.Equals(expectedMime, actualMime, StringComparison.OrdinalIgnoreCase)) + { + Console.Error.WriteLine($"MIME type mismatch: extension suggests '{expectedMime}' but detected '{actualMime}'."); + return; + } + + // Load PDF, embed file, and add attachment annotation + using (Document doc = new Document(inputPdfPath)) + { + // Create a FileSpecification for the attachment using a stream (required by Aspose.Pdf core API) + var fileSpec = new FileSpecification(attachmentPath, "Attachment"); + fileSpec.Contents = new MemoryStream(File.ReadAllBytes(attachmentPath)); + fileSpec.MIMEType = actualMime; + + // Add the file specification to the document's embedded files collection + doc.EmbeddedFiles.Add(fileSpec); + + // Create a rectangle for the annotation (float values are accepted by the constructor) + var rect = new Aspose.Pdf.Rectangle(100, 500, 200, 600); + + // Add the attachment annotation to the first page (1‑based indexing) + Page page = doc.Pages[1]; + var attachment = new FileAttachmentAnnotation(page, rect, fileSpec); + page.Annotations.Add(attachment); + + // Save the modified PDF + doc.Save(outputPdfPath); + } + + Console.WriteLine($"Attachment added successfully. Output saved to '{outputPdfPath}'."); + } + + // Maps common file extensions to MIME types + static string GetMimeTypeFromExtension(string extension) + { + if (string.IsNullOrEmpty(extension)) + return "application/octet-stream"; + + switch (extension.ToLowerInvariant()) + { + case ".png": return "image/png"; + case ".jpg": + case ".jpeg": return "image/jpeg"; + case ".gif": return "image/gif"; + case ".bmp": return "image/bmp"; + case ".pdf": return "application/pdf"; + case ".txt": return "text/plain"; + case ".doc": return "application/msword"; + case ".docx": return "application/vnd.openxmlformats-officedocument.wordprocessingml.document"; + case ".xls": return "application/vnd.ms-excel"; + case ".xlsx": return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; + default: return "application/octet-stream"; + } + } + + // Retrieves the actual MIME type of a file. + // For image files we use Aspose.Pdf.Image.GetMimeType; otherwise we fall back to the extension‑based MIME. + static string GetActualMimeType(string filePath) + { + string ext = Path.GetExtension(filePath).ToLowerInvariant(); + var imageExtensions = new HashSet { ".png", ".jpg", ".jpeg", ".gif", ".bmp" }; + + if (imageExtensions.Contains(ext)) + { + // Load the image using System.Drawing (Aspose.Pdf.Image.GetMimeType expects System.Drawing.Image) + using (System.Drawing.Image img = System.Drawing.Image.FromFile(filePath)) + { + return Aspose.Pdf.Image.GetMimeType(img); + } + } + + // Non‑image files: rely on the extension‑derived MIME type + return GetMimeTypeFromExtension(ext); + } +} diff --git a/working-with-attachments/verify-pdf-size-after-attachment.cs b/working-with-attachments/verify-pdf-size-after-attachment.cs new file mode 100644 index 00000000..b8e2b63c --- /dev/null +++ b/working-with-attachments/verify-pdf-size-after-attachment.cs @@ -0,0 +1,126 @@ +using System; +using System.IO; +using Aspose.Pdf; +using Aspose.Pdf.Annotations; +using NUnit.Framework; + +// Minimal NUnit stubs to allow compilation without the NUnit package +namespace NUnit.Framework +{ + [AttributeUsage(AttributeTargets.Class)] + public sealed class TestFixtureAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Method)] + public sealed class TestAttribute : Attribute { } + + public static class Assert + { + public static void Greater(T actual, T expected, string? message = null) where T : IComparable + { + if (actual.CompareTo(expected) <= 0) + throw new Exception(message ?? $"Assert.Greater failed. Expected > {expected}, but got {actual}."); + } + } +} + +namespace AsposePdfTests +{ + [TestFixture] + public class AttachmentSizeTests + { + private const string SampleTextFile = "sample.txt"; + + // Helper to create a simple text file used as attachment + private void CreateSampleFile() + { + File.WriteAllText(SampleTextFile, "This is a sample attachment file."); + } + + // Helper to delete temporary files safely + private void DeleteIfExists(string path) + { + try + { + if (File.Exists(path)) + File.Delete(path); + } + catch + { + // ignore cleanup errors + } + } + + [Test] + public void AddingFileAttachmentIncreasesPdfSize() + { + // Arrange: create a simple PDF with one blank page + string originalPdfPath = Path.GetTempFileName(); + string attachedPdfPath = Path.GetTempFileName(); + + CreateSampleFile(); + + try + { + // Create and save the original PDF + using (Document doc = new Document()) + { + // Add a blank page (Pages.Add() creates a new page) + doc.Pages.Add(); + doc.Save(originalPdfPath); // save without any attachments + } + + long originalSize = new FileInfo(originalPdfPath).Length; + + // Act: load the PDF, add a FileAttachmentAnnotation, and save to a new file + using (Document doc = new Document(originalPdfPath)) + { + // Get the first page (1‑based indexing) + Page page = doc.Pages[1]; + + // Define the rectangle where the attachment icon will appear + Aspose.Pdf.Rectangle rect = new Aspose.Pdf.Rectangle(100, 500, 200, 600); + + // Create a FileSpecification for the attachment file + using (FileStream fs = File.OpenRead(SampleTextFile)) + { + FileSpecification fileSpec = new FileSpecification(fs, SampleTextFile); + + // Create the FileAttachmentAnnotation + FileAttachmentAnnotation attachment = new FileAttachmentAnnotation(page, rect, fileSpec) + { + // Optional: add a description + Contents = "Sample attachment" + }; + + // Add the annotation to the page + page.Annotations.Add(attachment); + } + + // Save the modified PDF to a new file + doc.Save(attachedPdfPath); + } + + long attachedSize = new FileInfo(attachedPdfPath).Length; + + // Assert: the size after adding the attachment should be greater + Assert.Greater(attachedSize, originalSize, "PDF size did not increase after adding a file attachment."); + } + finally + { + // Cleanup temporary files + DeleteIfExists(originalPdfPath); + DeleteIfExists(attachedPdfPath); + DeleteIfExists(SampleTextFile); + } + } + } +} + +// Provide a minimal entry point to satisfy the compiler when building as an executable. +public class Program +{ + public static void Main(string[] args) + { + // No operation – tests are executed via the test runner. + } +} \ No newline at end of file