- Add Origin parameter (default: BottomLeft) - Add Width/Height as alternatives to X2/Y2 - Validation: Either (X2+Y2) OR (Width+Height) required, not both - FluentValidation rules enforce mutual exclusivity
79 lines
2.7 KiB
C#
79 lines
2.7 KiB
C#
using DocumentOperator.Application.Common.Interfaces;
|
|
using DocumentOperator.Domain.Models.ValueObjects;
|
|
using FluentValidation;
|
|
using MediatR;
|
|
|
|
namespace DocumentOperator.Application.AddAnnotation;
|
|
|
|
/// <summary>
|
|
/// Command to add an annotation to a PDF document.
|
|
/// </summary>
|
|
public record AddAnnotationCommand : IRequest<byte[]>
|
|
{
|
|
public required Stream PdfStream { get; init; }
|
|
public required AnnotationType AnnotationType { get; init; }
|
|
public required int PageNumber { get; init; }
|
|
public required (double X1, double Y1, double X2, double Y2) Rectangle { get; init; }
|
|
public string? Content { get; init; }
|
|
public string? Author { get; init; }
|
|
public string? Color { get; init; }
|
|
public TextMarkupStyle? TextMarkupStyle { get; init; }
|
|
public AnnotationOrigin Origin { get; init; } = AnnotationOrigin.BottomLeft;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handler for AddAnnotationCommand.
|
|
/// </summary>
|
|
public class AddAnnotationHandler(IPdfProcessor pdfProcessor) : IRequestHandler<AddAnnotationCommand, byte[]>
|
|
{
|
|
public async Task<byte[]> Handle(AddAnnotationCommand request, CancellationToken cancellationToken)
|
|
{
|
|
return await pdfProcessor.AddAnnotationAsync(
|
|
request.PdfStream,
|
|
request.AnnotationType,
|
|
request.PageNumber,
|
|
request.Rectangle,
|
|
request.Content,
|
|
request.Author,
|
|
request.Color,
|
|
request.TextMarkupStyle,
|
|
request.Origin);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Validator for AddAnnotationCommand.
|
|
/// </summary>
|
|
public class AddAnnotationValidator : AbstractValidator<AddAnnotationCommand>
|
|
{
|
|
public AddAnnotationValidator()
|
|
{
|
|
RuleFor(x => x.PdfStream)
|
|
.NotNull()
|
|
.WithMessage("PDF stream is required");
|
|
|
|
RuleFor(x => x.PageNumber)
|
|
.GreaterThan(0)
|
|
.WithMessage("Page number must be greater than 0");
|
|
|
|
RuleFor(x => x.Content)
|
|
.NotEmpty()
|
|
.When(x => x.AnnotationType == AnnotationType.FreeText || x.AnnotationType == AnnotationType.StickyNote)
|
|
.WithMessage("Content is required for FreeText and StickyNote annotations");
|
|
|
|
RuleFor(x => x.TextMarkupStyle)
|
|
.NotNull()
|
|
.When(x => x.AnnotationType == AnnotationType.TextMarkup)
|
|
.WithMessage("TextMarkupStyle is required for TextMarkup annotations");
|
|
|
|
RuleFor(x => x.Color)
|
|
.Matches("^[0-9A-Fa-f]{6}$")
|
|
.When(x => !string.IsNullOrWhiteSpace(x.Color))
|
|
.WithMessage("Color must be a 6-digit hex value (e.g., 'FF0000' for red)");
|
|
|
|
RuleFor(x => x.Rectangle)
|
|
.Must(r => r.X2 > r.X1 && r.Y2 > r.Y1)
|
|
.WithMessage("Rectangle coordinates must define a valid area (X2 > X1 and Y2 > Y1)");
|
|
}
|
|
}
|