Files
DocumentService/DocumentOperator.Application/AddAnnotation/AddAnnotationCommand.cs
TekH eed9d46e19 feat(annotation): Add Origin/Width/Height parameters to AddAnnotationCommand
- 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
2026-07-21 14:38:01 +02:00

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)");
}
}