diff --git a/DocumentOperator.Infrastructure/Services/PdfProcessing/DevExpressPdfProcessor.cs b/DocumentOperator.Infrastructure/Services/PdfProcessing/DevExpressPdfProcessor.cs
index 6a6d380..ed4118a 100644
--- a/DocumentOperator.Infrastructure/Services/PdfProcessing/DevExpressPdfProcessor.cs
+++ b/DocumentOperator.Infrastructure/Services/PdfProcessing/DevExpressPdfProcessor.cs
@@ -234,6 +234,68 @@ public class DevExpressPdfProcessor : IPdfProcessor
);
}
+ ///
+ /// Extracts all embedded files from a PDF document and returns them as a ZIP archive.
+ /// Uses DevExpress PdfDocument.FileAttachments to retrieve attachment data.
+ ///
+ /// PDF content as stream (caller is responsible for disposal)
+ /// ZIP archive byte array containing all extracted attachments
+ /// Thrown when stream is empty
+ /// Thrown when PDF contains no attachments
+ public async Task ExtractAttachmentsAsync(Stream pdfStream)
+ {
+ // 1. Input Validation
+ ArgumentNullException.ThrowIfNull(pdfStream, nameof(pdfStream));
+
+ if (pdfStream.Length == 0)
+ {
+ throw new BadRequestException("PDF stream cannot be empty");
+ }
+
+ // Defensive validation: Seekable streams must be at Position = 0
+ if (pdfStream.CanSeek && pdfStream.Position != 0)
+ {
+ throw new BadRequestException("PDF stream must be positioned at the beginning (Position = 0).");
+ }
+
+ // 2. Load PDF with DevExpress Document API
+ using var processor = new PdfDocumentProcessor();
+ processor.LoadDocument(pdfStream);
+
+ var document = processor.Document;
+
+ // 3. Extract attachment data using DevExpress FileAttachments collection
+ var fileAttachments = document.FileAttachments;
+
+ // 4. No attachments case
+ if (fileAttachments == null || !fileAttachments.Any())
+ {
+ throw new NotFoundException("PDF does not contain any attachments");
+ }
+
+ // 5. Create ZIP archive in memory
+ using var zipStream = new MemoryStream();
+ using (var zipArchive = new System.IO.Compression.ZipArchive(zipStream, System.IO.Compression.ZipArchiveMode.Create, leaveOpen: true))
+ {
+ foreach (var attachment in fileAttachments)
+ {
+ // Get attachment metadata
+ string fileName = attachment.FileName ?? "unnamed";
+ byte[] fileData = attachment.Data;
+
+ // Create entry in ZIP
+ var entry = zipArchive.CreateEntry(fileName, System.IO.Compression.CompressionLevel.Optimal);
+
+ // Write attachment data to ZIP entry
+ using var entryStream = entry.Open();
+ await entryStream.WriteAsync(fileData, 0, fileData.Length);
+ }
+ }
+
+ // 6. Return ZIP byte array
+ return zipStream.ToArray();
+ }
+
#endregion
#region Private Helpers