Introduced endpoints for embedding attachments in PDFs and converting between standard PDFs and PDF/A formats. Added `PdfAttachmentController` and `PdfConversionController` with multipart/form-data and Base64-based support. Implemented commands, handlers, and validators for these operations. Extended `IPdfProcessor` with methods for adding attachments and PDF/A conversion. Partially implemented functionality in `DevExpressPdfProcessor`, including `ConvertFromPdfAAsync`. Added `attachment.xml` and `withoutAttachment.pdf` as resources for testing. Marked endpoints as `[Obsolete]` to indicate incomplete implementation. Improved validation and error handling for commands.
91 lines
2.7 KiB
C#
91 lines
2.7 KiB
C#
using DocumentOperator.Application.Common.Interfaces;
|
|
using FluentValidation;
|
|
using MediatR;
|
|
|
|
namespace DocumentOperator.Application.AddAttachments;
|
|
|
|
/// <summary>
|
|
/// Command to add one or more attachments to a PDF document (supports PDF/A-3)
|
|
/// </summary>
|
|
public record AddAttachmentsCommand : IRequest<byte[]>
|
|
{
|
|
/// <summary>
|
|
/// PDF document stream. Must be positioned at the beginning (Position = 0).
|
|
/// </summary>
|
|
public required Stream PdfStream { get; init; }
|
|
|
|
/// <summary>
|
|
/// List of attachments to embed (filename, content, optional MIME type)
|
|
/// </summary>
|
|
public required IReadOnlyList<AttachmentFile> Attachments { get; init; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Represents a file to be attached to a PDF
|
|
/// </summary>
|
|
public record AttachmentFile
|
|
{
|
|
/// <summary>
|
|
/// File name (e.g., "invoice.xml", "document.pdf")
|
|
/// </summary>
|
|
public required string FileName { get; init; }
|
|
|
|
/// <summary>
|
|
/// File content as byte array
|
|
/// </summary>
|
|
public required byte[] Content { get; init; }
|
|
|
|
/// <summary>
|
|
/// MIME type (optional, e.g., "application/xml", "application/pdf")
|
|
/// If not provided, will be inferred from file extension
|
|
/// </summary>
|
|
public string? MimeType { get; init; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handler for AddAttachmentsCommand
|
|
/// </summary>
|
|
public class AddAttachmentsCommandHandler(IPdfProcessor pdfProcessor)
|
|
: IRequestHandler<AddAttachmentsCommand, byte[]>
|
|
{
|
|
public async Task<byte[]> Handle(AddAttachmentsCommand request, CancellationToken cancellationToken)
|
|
{
|
|
// Convert to tuple list for IPdfProcessor
|
|
var attachmentTuples = request.Attachments
|
|
.Select(a => (a.FileName, a.Content, a.MimeType))
|
|
.ToList();
|
|
|
|
return await pdfProcessor.AddAttachmentsAsync(request.PdfStream, attachmentTuples);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Validator for AddAttachmentsCommand
|
|
/// </summary>
|
|
public class AddAttachmentsCommandValidator : AbstractValidator<AddAttachmentsCommand>
|
|
{
|
|
public AddAttachmentsCommandValidator()
|
|
{
|
|
RuleFor(x => x.PdfStream)
|
|
.NotNull()
|
|
.WithMessage("PDF stream is required");
|
|
|
|
RuleFor(x => x.Attachments)
|
|
.NotNull()
|
|
.NotEmpty()
|
|
.WithMessage("At least one attachment is required");
|
|
|
|
RuleForEach(x => x.Attachments).ChildRules(attachment =>
|
|
{
|
|
attachment.RuleFor(a => a.FileName)
|
|
.NotEmpty()
|
|
.WithMessage("Attachment file name is required");
|
|
|
|
attachment.RuleFor(a => a.Content)
|
|
.NotNull()
|
|
.NotEmpty()
|
|
.WithMessage("Attachment content is required");
|
|
});
|
|
}
|
|
}
|