Add interfaces and DTOs for PDF operations support

Introduced multiple interfaces and DTOs to support a wide range of
PDF-related operations, including attachment handling, conversion,
validation, annotation, stamping, and metadata extraction.

Key changes:
- Added `DocumentServiceClientOptions` for HTTP client config.
- Introduced `IPdfAttachmentClient` for attachment operations.
- Added `IPdfConversionClient` for PDF/A conversion (marked obsolete).
- Added `IPdfOperationsClient` for merge, annotate, and stamp ops.
- Introduced `IPdfValidationClient` for PDF and PDF/A validation.
- Added `ISwissQrCodeClient` for Swiss QR Code extraction.
- Added `IZugferdClient` for ZUGFeRD detection and extraction.
- Created request DTOs for Base64-encoded operations.
- Added enums for annotation, stamp, and validation configurations.

These changes provide a flexible and extensible foundation for
interacting with PDF documents, supporting both multipart and
Base64-encoded inputs, and ensuring type safety with enums and records.
This commit is contained in:
2026-07-30 16:59:54 +02:00
parent d477eb5a28
commit b3a07f1348
20 changed files with 964 additions and 0 deletions

View File

@@ -0,0 +1,29 @@
namespace DocumentService.Client.Configuration;
/// <summary>
/// Configuration options for DocumentService HTTP client.
/// </summary>
public class DocumentServiceClientOptions
{
/// <summary>
/// Base URL of the DocumentService API.
/// </summary>
/// <example>https://api.example.com</example>
public string BaseUrl { get; set; } = "http://localhost:5000";
/// <summary>
/// HTTP request timeout duration.
/// </summary>
public TimeSpan Timeout { get; set; } = TimeSpan.FromMinutes(5);
/// <summary>
/// Maximum number of retry attempts for failed requests.
/// </summary>
public int MaxRetries { get; set; } = 3;
/// <summary>
/// Whether to throw exceptions on HTTP error responses (4xx, 5xx).
/// If false, returns null/default values instead.
/// </summary>
public bool ThrowOnError { get; set; } = true;
}

View File

@@ -0,0 +1,89 @@
using DocumentService.Client.Models.Requests;
namespace DocumentService.Client.Interfaces;
/// <summary>
/// Client for PDF attachment operations (check, extract, add).
/// </summary>
public interface IPdfAttachmentClient
{
/// <summary>
/// Checks if PDF contains attachments (multipart).
/// </summary>
/// <param name="pdfStream">PDF file stream</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>Attachment check result with metadata</returns>
Task<AttachmentCheckResult> CheckAttachmentsAsync(Stream pdfStream, CancellationToken cancellationToken = default);
/// <summary>
/// Checks if PDF contains attachments (Base64).
/// </summary>
/// <param name="pdfBytes">PDF file as byte array</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>Attachment check result with metadata</returns>
Task<AttachmentCheckResult> CheckAttachmentsAsync(byte[] pdfBytes, CancellationToken cancellationToken = default);
/// <summary>
/// Extracts all embedded attachments from a PDF (multipart).
/// The returned dictionary maps each file name to its decompressed content stream.
/// </summary>
/// <param name="pdfStream">PDF file stream</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>Dictionary of file name ? content stream for each extracted attachment</returns>
Task<Dictionary<string, Stream>> ExtractAttachmentsAsync(Stream pdfStream, CancellationToken cancellationToken = default);
/// <summary>
/// Extracts all embedded attachments from a PDF (Base64).
/// The returned dictionary maps each file name to its decompressed content stream.
/// </summary>
/// <param name="pdfBytes">PDF file as byte array</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>Dictionary of file name ? content stream for each extracted attachment</returns>
Task<Dictionary<string, Stream>> ExtractAttachmentsAsync(byte[] pdfBytes, CancellationToken cancellationToken = default);
/// <summary>
/// Adds attachments to PDF (multipart). ?? Not implemented yet in API.
/// </summary>
/// <param name="pdfStream">PDF file stream</param>
/// <param name="attachments">Attachments to add</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>Modified PDF as stream</returns>
[Obsolete("API endpoint not implemented yet")]
Task<Stream> AddAttachmentsAsync(Stream pdfStream, List<AttachmentRequestDto> attachments, CancellationToken cancellationToken = default);
/// <summary>
/// Adds attachments to PDF (Base64). ?? Not implemented yet in API.
/// </summary>
/// <param name="pdfBytes">PDF file as byte array</param>
/// <param name="attachments">Attachments to add</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>Modified PDF as stream</returns>
[Obsolete("API endpoint not implemented yet")]
Task<Stream> AddAttachmentsAsync(byte[] pdfBytes, List<AttachmentRequestDto> attachments, CancellationToken cancellationToken = default);
}
/// <summary>
/// DTO for attachment check result
/// </summary>
public record AttachmentCheckResult
{
/// <summary>Whether the document contains any embedded file attachments.</summary>
public bool HasAttachments { get; init; }
/// <summary>Number of embedded file attachments found.</summary>
public int AttachmentCount { get; init; }
/// <summary>Metadata for each attachment found in the document.</summary>
public List<AttachmentMetadata> Attachments { get; init; } = new();
}
/// <summary>
/// DTO for attachment metadata
/// </summary>
public record AttachmentMetadata
{
/// <summary>Name of the embedded file.</summary>
public string FileName { get; init; } = string.Empty;
/// <summary>MIME type of the embedded file (e.g. "application/xml"), or <c>null</c> if unknown.</summary>
public string? MimeType { get; init; }
/// <summary>Size of the embedded file in bytes.</summary>
public long Size { get; init; }
}

View File

@@ -0,0 +1,49 @@
namespace DocumentService.Client.Interfaces;
/// <summary>
/// Client for PDF conversion operations (PDF ? PDF/A).
/// </summary>
/// <remarks>
/// All methods on this interface are marked obsolete because the corresponding
/// API endpoints are not yet implemented.
/// </remarks>
public interface IPdfConversionClient
{
/// <summary>
/// Converts a standard PDF to PDF/A format (multipart).
/// </summary>
/// <param name="pdfStream">Source PDF stream</param>
/// <param name="pdfALevel">Target PDF/A level (e.g., "PDF/A-3b")</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>PDF/A document as stream</returns>
[Obsolete("API endpoint not implemented yet")]
Task<Stream> ConvertToPdfAAsync(Stream pdfStream, string pdfALevel = "PDF/A-3b", CancellationToken cancellationToken = default);
/// <summary>
/// Converts a standard PDF to PDF/A format (Base64).
/// </summary>
/// <param name="pdfBytes">Source PDF as byte array</param>
/// <param name="pdfALevel">Target PDF/A level (e.g., "PDF/A-3b")</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>PDF/A document as stream</returns>
[Obsolete("API endpoint not implemented yet")]
Task<Stream> ConvertToPdfAAsync(byte[] pdfBytes, string pdfALevel = "PDF/A-3b", CancellationToken cancellationToken = default);
/// <summary>
/// Converts a PDF/A document to standard PDF (multipart).
/// </summary>
/// <param name="pdfStream">Source PDF/A stream</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>Standard PDF as stream</returns>
[Obsolete("API endpoint not implemented yet")]
Task<Stream> ConvertFromPdfAAsync(Stream pdfStream, CancellationToken cancellationToken = default);
/// <summary>
/// Converts a PDF/A document to standard PDF (Base64).
/// </summary>
/// <param name="pdfBytes">Source PDF/A as byte array</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>Standard PDF as stream</returns>
[Obsolete("API endpoint not implemented yet")]
Task<Stream> ConvertFromPdfAAsync(byte[] pdfBytes, CancellationToken cancellationToken = default);
}

View File

@@ -0,0 +1,69 @@
using DocumentService.Client.Models.Requests;
namespace DocumentService.Client.Interfaces;
/// <summary>
/// Client for PDF operations (merge, annotate, stamp).
/// </summary>
public interface IPdfOperationsClient
{
// ==================== MERGE OPERATIONS ====================
/// <summary>
/// Merges multiple PDFs (multipart).
/// </summary>
/// <param name="pdfStreams">PDF file streams to merge</param>
/// <param name="pageRanges">Optional page ranges per PDF (e.g., "1-3,5")</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>Merged PDF as stream</returns>
Task<Stream> MergeAsync(IEnumerable<Stream> pdfStreams, List<string?>? pageRanges = null, CancellationToken cancellationToken = default);
/// <summary>
/// Merges multiple PDFs (Base64).
/// </summary>
/// <param name="pdfByteArrays">PDF files as byte arrays</param>
/// <param name="pageRanges">Optional page ranges per PDF (e.g., "1-3,5")</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>Merged PDF as stream</returns>
Task<Stream> MergeAsync(IEnumerable<byte[]> pdfByteArrays, List<string?>? pageRanges = null, CancellationToken cancellationToken = default);
// ==================== ANNOTATION OPERATIONS ====================
/// <summary>
/// Adds annotation to PDF (multipart).
/// </summary>
/// <param name="pdfStream">PDF file stream</param>
/// <param name="request">Annotation request with coordinates and style</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>Annotated PDF as stream</returns>
Task<Stream> AnnotateAsync(Stream pdfStream, AddAnnotationBase64Request request, CancellationToken cancellationToken = default);
/// <summary>
/// Adds annotation to PDF (Base64).
/// </summary>
/// <param name="pdfBytes">PDF file as byte array</param>
/// <param name="request">Annotation request with coordinates and style</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>Annotated PDF as stream</returns>
Task<Stream> AnnotateAsync(byte[] pdfBytes, AddAnnotationBase64Request request, CancellationToken cancellationToken = default);
// ==================== STAMP OPERATIONS ====================
/// <summary>
/// Adds stamp to PDF (multipart).
/// </summary>
/// <param name="pdfStream">PDF file stream</param>
/// <param name="request">Stamp request with position and style</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>Stamped PDF as stream</returns>
Task<Stream> StampAsync(Stream pdfStream, AddStampBase64Request request, CancellationToken cancellationToken = default);
/// <summary>
/// Adds stamp to PDF (Base64).
/// </summary>
/// <param name="pdfBytes">PDF file as byte array</param>
/// <param name="request">Stamp request with position and style</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>Stamped PDF as stream</returns>
Task<Stream> StampAsync(byte[] pdfBytes, AddStampBase64Request request, CancellationToken cancellationToken = default);
}

View File

@@ -0,0 +1,75 @@
namespace DocumentService.Client.Interfaces;
/// <summary>
/// Client for PDF validation operations (validate, validate PDF/A).
/// </summary>
public interface IPdfValidationClient
{
/// <summary>
/// Validates a PDF from stream (multipart upload).
/// </summary>
/// <param name="pdfStream">PDF file stream</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>Validation result with metadata</returns>
Task<PdfValidationResult> ValidatePdfAsync(Stream pdfStream, CancellationToken cancellationToken = default);
/// <summary>
/// Validates a PDF from byte array (Base64 JSON).
/// </summary>
/// <param name="pdfBytes">PDF file as byte array</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>Validation result with metadata</returns>
Task<PdfValidationResult> ValidatePdfAsync(byte[] pdfBytes, CancellationToken cancellationToken = default);
/// <summary>
/// Validates a PDF/A from stream (multipart upload).
/// </summary>
/// <param name="pdfStream">PDF/A file stream</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>PDF/A validation result with conformance level and errors</returns>
Task<PdfAValidationResult> ValidatePdfAAsync(Stream pdfStream, CancellationToken cancellationToken = default);
/// <summary>
/// Validates a PDF/A from byte array (Base64 JSON).
/// </summary>
/// <param name="pdfBytes">PDF/A file as byte array</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>PDF/A validation result with conformance level and errors</returns>
Task<PdfAValidationResult> ValidatePdfAAsync(byte[] pdfBytes, CancellationToken cancellationToken = default);
}
/// <summary>
/// DTO for PDF validation result
/// </summary>
public record PdfValidationResult
{
/// <summary>Total number of pages in the document.</summary>
public int PageCount { get; init; }
/// <summary>File size in bytes.</summary>
public long FileSizeBytes { get; init; }
/// <summary>PDF specification version (e.g. "1.7").</summary>
public string PdfVersion { get; init; } = string.Empty;
/// <summary>Whether the document is password-protected.</summary>
public bool IsEncrypted { get; init; }
/// <summary>Whether the document contains embedded file attachments.</summary>
public bool HasAttachments { get; init; }
/// <summary>Number of embedded file attachments.</summary>
public int AttachmentCount { get; init; }
}
/// <summary>
/// DTO for PDF/A validation result
/// </summary>
public record PdfAValidationResult
{
/// <summary>Whether the document is fully PDF/A conformant.</summary>
public bool IsValid { get; init; }
/// <summary>Detected PDF/A conformance level (e.g. "PDF/A-3b"), or <c>null</c> if not a PDF/A document.</summary>
public string? PdfAVersion { get; init; }
/// <summary>Total number of pages in the document.</summary>
public int PageCount { get; init; }
/// <summary>List of conformance errors found during validation.</summary>
public List<string> Errors { get; init; } = new();
/// <summary>List of conformance warnings found during validation.</summary>
public List<string> Warnings { get; init; } = new();
}

View File

@@ -0,0 +1,36 @@
namespace DocumentService.Client.Interfaces;
/// <summary>
/// Client for Swiss QR Code extraction operations.
/// </summary>
public interface ISwissQrCodeClient
{
/// <summary>
/// Extracts Swiss QR Code from PDF (multipart).
/// </summary>
/// <param name="pdfStream">PDF file stream</param>
/// <param name="raw">If true, returns raw QR text lines instead of parsed Bill object</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>Swiss QR Code extraction result</returns>
Task<SwissQrCodeExtractionResult> ExtractSwissQrCodeAsync(Stream pdfStream, bool raw = false, CancellationToken cancellationToken = default);
/// <summary>
/// Extracts Swiss QR Code from PDF (Base64).
/// </summary>
/// <param name="pdfBytes">PDF file as byte array</param>
/// <param name="raw">If true, returns raw QR text lines instead of parsed Bill object</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>Swiss QR Code extraction result</returns>
Task<SwissQrCodeExtractionResult> ExtractSwissQrCodeAsync(byte[] pdfBytes, bool raw = false, CancellationToken cancellationToken = default);
}
/// <summary>
/// DTO for Swiss QR Code extraction result
/// </summary>
public record SwissQrCodeExtractionResult
{
/// <summary>Parsed Swiss QR bill object. <c>null</c> when <c>raw=true</c> was requested.</summary>
public object? Bill { get; init; }
/// <summary>Raw QR code text lines. Populated when <c>raw=true</c> was requested.</summary>
public List<string> RawLines { get; init; } = new();
}

View File

@@ -0,0 +1,79 @@
namespace DocumentService.Client.Interfaces;
/// <summary>
/// Client for ZUGFeRD operations (detection, extraction).
/// </summary>
public interface IZugferdClient
{
/// <summary>
/// Checks whether a PDF contains a ZUGFeRD XML attachment (multipart).
/// </summary>
/// <param name="pdfStream">PDF file stream</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>ZUGFeRD check result</returns>
Task<ZugferdCheckResult> HasZugferdAsync(Stream pdfStream, CancellationToken cancellationToken = default);
/// <summary>
/// Checks whether a PDF contains a ZUGFeRD XML attachment (Base64).
/// </summary>
/// <param name="pdfBytes">PDF file as byte array</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>ZUGFeRD check result</returns>
Task<ZugferdCheckResult> HasZugferdAsync(byte[] pdfBytes, CancellationToken cancellationToken = default);
/// <summary>
/// Extracts the ZUGFeRD XML from a PDF and returns the raw XML stream (multipart).
/// </summary>
/// <param name="pdfStream">PDF file stream</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>ZUGFeRD XML content as stream</returns>
Task<Stream> ExtractZugferdAsync(Stream pdfStream, CancellationToken cancellationToken = default);
/// <summary>
/// Extracts the ZUGFeRD XML from a PDF and returns the raw XML stream (Base64).
/// </summary>
/// <param name="pdfBytes">PDF file as byte array</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>ZUGFeRD XML content as stream</returns>
Task<Stream> ExtractZugferdAsync(byte[] pdfBytes, CancellationToken cancellationToken = default);
/// <summary>
/// Extracts ZUGFeRD metadata and XML content as a structured result (multipart).
/// </summary>
/// <param name="pdfStream">PDF file stream</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>Structured ZUGFeRD extraction result</returns>
Task<ZugferdExtractionResult> ExtractZugferdAsResultAsync(Stream pdfStream, CancellationToken cancellationToken = default);
/// <summary>
/// Extracts ZUGFeRD metadata and XML content as a structured result (Base64).
/// </summary>
/// <param name="pdfBytes">PDF file as byte array</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>Structured ZUGFeRD extraction result</returns>
Task<ZugferdExtractionResult> ExtractZugferdAsResultAsync(byte[] pdfBytes, CancellationToken cancellationToken = default);
}
/// <summary>DTO for ZUGFeRD detection result</summary>
public record ZugferdCheckResult
{
/// <summary>Whether the document contains a ZUGFeRD XML attachment.</summary>
public bool HasZugferd { get; init; }
/// <summary>Detected ZUGFeRD version (e.g. "2.1"), or <c>null</c> if not present.</summary>
public string? Version { get; init; }
/// <summary>ZUGFeRD profile name (e.g. "EN 16931"), or <c>null</c> if not present.</summary>
public string? Profile { get; init; }
}
/// <summary>DTO for ZUGFeRD extraction result</summary>
public record ZugferdExtractionResult
{
/// <summary>File name of the extracted XML attachment (e.g. "factur-x.xml").</summary>
public string FileName { get; init; } = string.Empty;
/// <summary>Full XML content of the ZUGFeRD attachment.</summary>
public string XmlContent { get; init; } = string.Empty;
/// <summary>ZUGFeRD version (e.g. "2.1"), or <c>null</c> if not detected.</summary>
public string? Version { get; init; }
/// <summary>ZUGFeRD profile name (e.g. "EN 16931"), or <c>null</c> if not detected.</summary>
public string? Profile { get; init; }
}

View File

@@ -0,0 +1,66 @@
namespace DocumentService.Client.Models.Requests;
/// <summary>
/// Request DTO for Base64-encoded PDF attachment check
/// </summary>
public record CheckPdfAttachmentsRequest
{
/// <summary>
/// PDF document encoded as Base64 string
/// </summary>
/// <example>JVBERi0xLjQKJeLjz9MKMSAwIG9iago8PC9UeXBlL0NhdGFsb2c...</example>
public required string Base64Pdf { get; init; }
}
/// <summary>
/// Request DTO for Base64-encoded PDF attachment extraction
/// </summary>
public record ExtractPdfAttachmentsRequest
{
/// <summary>
/// PDF document encoded as Base64 string
/// </summary>
/// <example>JVBERi0xLjQKJeLjz9MKMSAwIG9iago8PC9UeXBlL0NhdGFsb2c...</example>
public required string Base64Pdf { get; init; }
}
/// <summary>
/// Request DTO for Base64-encoded PDF with attachments to add
/// </summary>
public record AddAttachmentsRequest
{
/// <summary>
/// PDF document encoded as Base64 string
/// </summary>
/// <example>JVBERi0xLjQKJeLjz9MKMSAwIG9iago8PC9UeXBlL0NhdGFsb2c...</example>
public required string Base64Pdf { get; init; }
/// <summary>
/// List of attachments to embed
/// </summary>
public required List<AttachmentRequestDto> Attachments { get; init; }
}
/// <summary>
/// DTO for attachment file in request
/// </summary>
public record AttachmentRequestDto
{
/// <summary>
/// File name (e.g., "invoice.xml", "document.pdf")
/// </summary>
/// <example>factur-x.xml</example>
public required string FileName { get; init; }
/// <summary>
/// File content encoded as Base64 string
/// </summary>
/// <example>PD94bWwgdmVyc2lvbj0iMS4wIj8+...</example>
public required string Base64Content { get; init; }
/// <summary>
/// MIME type (optional, e.g., "application/xml")
/// </summary>
/// <example>application/xml</example>
public string? MimeType { get; init; }
}

View File

@@ -0,0 +1,31 @@
namespace DocumentService.Client.Models.Requests;
/// <summary>
/// Request DTO for converting a standard PDF to PDF/A format (Base64 JSON)
/// </summary>
public record ConvertToPdfARequest
{
/// <summary>
/// PDF document encoded as Base64 string
/// </summary>
/// <example>JVBERi0xLjQKJeLjz9MKMSAwIG9iago8PC9UeXBlL0NhdGFsb2c...</example>
public required string Base64Pdf { get; init; }
/// <summary>
/// Target PDF/A conformance level. Defaults to "PDF/A-3b".
/// </summary>
/// <example>PDF/A-3b</example>
public string? PdfALevel { get; init; }
}
/// <summary>
/// Request DTO for converting a PDF/A document back to standard PDF (Base64 JSON)
/// </summary>
public record ConvertFromPdfARequest
{
/// <summary>
/// PDF/A document encoded as Base64 string
/// </summary>
/// <example>JVBERi0xLjQKJeLjz9MKMSAwIG9iago8PC9UeXBlL0NhdGFsb2c...</example>
public required string Base64Pdf { get; init; }
}

View File

@@ -0,0 +1,221 @@
using DocumentService.Client.Models.ValueObjects;
namespace DocumentService.Client.Models.Requests;
/// <summary>
/// Request DTO for Base64-encoded PDF merge operation
/// </summary>
public record MergePdfsBase64Request
{
/// <summary>
/// Array of Base64-encoded PDF files (minimum 2 required)
/// </summary>
/// <example>["JVBERi0xLjQK...", "JVBERi0xLjQK..."]</example>
public required List<string> Base64Pdfs { get; init; }
/// <summary>
/// Optional page ranges per PDF (null = all pages).
/// Format: "1-3,5" means pages 1, 2, 3, and 5.
/// If provided, array length must match Base64Pdfs length.
/// </summary>
/// <example>["1-2", "1,3,5", null]</example>
public List<string?>? PageRanges { get; init; }
}
/// <summary>
/// Request DTO for Base64-encoded PDF annotation
/// </summary>
public record AddAnnotationBase64Request
{
/// <summary>
/// Base64-encoded PDF file
/// </summary>
/// <example>"JVBERi0xLjQK..."</example>
public required string Base64Pdf { get; init; }
/// <summary>
/// Type of annotation to add
/// </summary>
/// <example>TextMarkup</example>
public required AnnotationType AnnotationType { get; init; }
/// <summary>
/// Target page number (1-indexed)
/// </summary>
/// <example>1</example>
public required int PageNumber { get; init; }
/// <summary>
/// Rectangle X1 coordinate (left)
/// </summary>
/// <example>100.0</example>
public required double X1 { get; init; }
/// <summary>
/// Rectangle Y1 coordinate (top or bottom depending on Origin)
/// </summary>
/// <example>100.0</example>
public required double Y1 { get; init; }
/// <summary>
/// Rectangle X2 coordinate (right). Optional if Width is provided.
/// </summary>
/// <example>200.0</example>
public double? X2 { get; init; }
/// <summary>
/// Rectangle Y2 coordinate (bottom or top depending on Origin). Optional if Height is provided.
/// </summary>
/// <example>120.0</example>
public double? Y2 { get; init; }
/// <summary>
/// Rectangle width. Alternative to X2 (X2 = X1 + Width). Optional if X2 is provided.
/// </summary>
/// <example>100.0</example>
public double? Width { get; init; }
/// <summary>
/// Rectangle height. Alternative to Y2 (Y2 = Y1 + Height). Optional if Y2 is provided.
/// </summary>
/// <example>20.0</example>
public double? Height { get; init; }
/// <summary>
/// Annotation content (required for FreeText and StickyNote)
/// </summary>
/// <example>"Important text to highlight"</example>
public string? Content { get; init; }
/// <summary>
/// Author name (optional)
/// </summary>
/// <example>"John Doe"</example>
public string? Author { get; init; }
/// <summary>
/// Hex color (6 digits, e.g., "FF0000" for red). Optional - defaults vary by annotation type.
/// </summary>
/// <example>"FFFF00"</example>
public string? Color { get; init; }
/// <summary>
/// Text markup style (Highlight, Underline, or Strikeout). Required for TextMarkup annotations.
/// </summary>
/// <example>Highlight</example>
public TextMarkupStyle? TextMarkupStyle { get; init; }
/// <summary>
/// Coordinate origin (BottomLeft = PDF native, TopLeft = UI-friendly). Default: BottomLeft
/// </summary>
/// <example>BottomLeft</example>
public AnnotationOrigin Origin { get; init; } = AnnotationOrigin.BottomLeft;
}
/// <summary>
/// Request DTO for Base64-encoded PDF stamp operation
/// </summary>
public record AddStampBase64Request
{
/// <summary>
/// Base64-encoded PDF file
/// </summary>
/// <example>"JVBERi0xLjQK..."</example>
public required string Base64Pdf { get; init; }
/// <summary>
/// Type of stamp (Text, Image, or Predefined)
/// </summary>
/// <example>Text</example>
public required StampType StampType { get; init; }
/// <summary>
/// Target page numbers (1-indexed). Null or empty = all pages.
/// </summary>
/// <example>[1, 3, 5]</example>
public int[]? PageNumbers { get; init; }
/// <summary>
/// Stamp position X coordinate
/// </summary>
/// <example>100.0</example>
public required double X { get; init; }
/// <summary>
/// Stamp position Y coordinate
/// </summary>
/// <example>100.0</example>
public required double Y { get; init; }
/// <summary>
/// Stamp width (optional, auto-size for images if not specified)
/// </summary>
/// <example>200.0</example>
public double? Width { get; init; }
/// <summary>
/// Stamp height (optional, auto-size for images if not specified)
/// </summary>
/// <example>50.0</example>
public double? Height { get; init; }
/// <summary>
/// Coordinate origin (BottomLeft = PDF native, TopLeft = UI-friendly). Default: BottomLeft
/// </summary>
/// <example>BottomLeft</example>
public AnnotationOrigin Origin { get; init; } = AnnotationOrigin.BottomLeft;
/// <summary>
/// Text content (required for Text stamps)
/// </summary>
/// <example>"CONFIDENTIAL"</example>
public string? Text { get; init; }
/// <summary>
/// Font name (default: Arial)
/// </summary>
/// <example>"Arial"</example>
public string? FontName { get; init; }
/// <summary>
/// Font size in points (default: 12)
/// </summary>
/// <example>24.0</example>
public double? FontSize { get; init; }
/// <summary>
/// Hex color (6 digits, e.g., "FF0000" for red, default: "000000")
/// </summary>
/// <example>"FF0000"</example>
public string? Color { get; init; }
/// <summary>
/// Opacity (0.0 = transparent, 1.0 = opaque, default: 0.5)
/// </summary>
/// <example>0.5</example>
public double? Opacity { get; init; }
/// <summary>
/// Rotation angle in degrees (0-360, default: 0)
/// </summary>
/// <example>45.0</example>
public double? Rotation { get; init; }
/// <summary>
/// Stamp placement (Foreground = on top, Background = watermark effect, default: Foreground)
/// </summary>
/// <example>Foreground</example>
public StampPlacement Placement { get; init; } = StampPlacement.Foreground;
/// <summary>
/// Base64-encoded image (required for Image stamps, PNG/JPEG)
/// </summary>
/// <example>"iVBORw0KGgoAAAANSUhEUgAA..."</example>
public string? Base64Image { get; init; }
/// <summary>
/// Predefined stamp type (required for Predefined stamps)
/// </summary>
/// <example>Confidential</example>
public PredefinedStampType? PredefinedType { get; init; }
}

View File

@@ -0,0 +1,27 @@
using DocumentService.Client.Models.ValueObjects;
namespace DocumentService.Client.Models.Requests;
/// <summary>
/// Request DTO for Base64-encoded PDF validation
/// </summary>
public record ValidatePdfBase64Request
{
/// <summary>
/// PDF document encoded as Base64 string
/// </summary>
/// <example>JVBERi0xLjQKJeLjz9MKMSAwIG9iago8PC9UeXBlL0NhdGFsb2c...</example>
public required string Base64Pdf { get; init; }
}
/// <summary>
/// Request DTO for Base64-encoded PDF/A validation
/// </summary>
public record ValidatePdfABase64Request
{
/// <summary>
/// PDF document encoded as Base64 string
/// </summary>
/// <example>JVBERi0xLjQKJeLjz9MKMSAwIG9iago8PC9UeXBlL0NhdGFsb2c...</example>
public required string Base64Pdf { get; init; }
}

View File

@@ -0,0 +1,13 @@
namespace DocumentService.Client.Models.Requests;
/// <summary>
/// Request DTO for Base64-encoded Swiss QR Code extraction
/// </summary>
public record ExtractSwissQrCodeBase64Request
{
/// <summary>
/// PDF document encoded as Base64 string
/// </summary>
/// <example>JVBERi0xLjQKJeLjz9MKMSAwIG9iago8PC9UeXBlL0NhdGFsb2c...</example>
public required string Base64Pdf { get; init; }
}

View File

@@ -0,0 +1,25 @@
namespace DocumentService.Client.Models.Requests;
/// <summary>
/// Request DTO for Base64-encoded PDF ZUGFeRD check
/// </summary>
public record HasZugferdRequest
{
/// <summary>
/// PDF document encoded as Base64 string
/// </summary>
/// <example>JVBERi0xLjQKJeLjz9MKMSAwIG9iago8PC9UeXBlL0NhdGFsb2c...</example>
public required string Base64Pdf { get; init; }
}
/// <summary>
/// Request DTO for Base64-encoded PDF ZUGFeRD extraction
/// </summary>
public record ExtractZugferdRequest
{
/// <summary>
/// PDF document encoded as Base64 string
/// </summary>
/// <example>JVBERi0xLjQKJeLjz9MKMSAwIG9iago8PC9UeXBlL0NhdGFsb2c...</example>
public required string Base64Pdf { get; init; }
}

View File

@@ -0,0 +1,17 @@
namespace DocumentService.Client.Models.ValueObjects;
/// <summary>
/// Coordinate origin point for PDF annotations.
/// </summary>
public enum AnnotationOrigin
{
/// <summary>
/// Bottom-left corner (PDF native coordinate system, default)
/// </summary>
BottomLeft,
/// <summary>
/// Top-left corner (common in UI frameworks)
/// </summary>
TopLeft
}

View File

@@ -0,0 +1,32 @@
namespace DocumentService.Client.Models.ValueObjects;
/// <summary>
/// Supported PDF annotation types
/// </summary>
public enum AnnotationType
{
/// <summary>
/// Text markup annotation (highlight, underline, strikeout)
/// </summary>
TextMarkup,
/// <summary>
/// Free text annotation (text box with visible text)
/// </summary>
FreeText,
/// <summary>
/// Sticky note annotation (popup comment icon)
/// </summary>
StickyNote,
/// <summary>
/// Circle shape annotation
/// </summary>
Circle,
/// <summary>
/// Square shape annotation
/// </summary>
Square
}

View File

@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DocumentService.Client.Models.ValueObjects;
public enum OnReconfigure
{
ThrowException = 0,
Ignore = 1,
}

View File

@@ -0,0 +1,32 @@
namespace DocumentService.Client.Models.ValueObjects;
/// <summary>
/// Predefined stamp types with standard text and styling.
/// </summary>
public enum PredefinedStampType
{
/// <summary>
/// CONFIDENTIAL stamp (red, bold).
/// </summary>
Confidential,
/// <summary>
/// APPROVED stamp (green, bold).
/// </summary>
Approved,
/// <summary>
/// DRAFT stamp (gray, italic).
/// </summary>
Draft,
/// <summary>
/// VOID stamp (red, strikethrough effect).
/// </summary>
Void,
/// <summary>
/// FOR REVIEW stamp (orange, bold).
/// </summary>
ForReview
}

View File

@@ -0,0 +1,17 @@
namespace DocumentService.Client.Models.ValueObjects;
/// <summary>
/// Specifies whether the stamp should appear in the foreground or background.
/// </summary>
public enum StampPlacement
{
/// <summary>
/// Stamp appears on top of existing page content.
/// </summary>
Foreground,
/// <summary>
/// Stamp appears behind existing page content (watermark effect).
/// </summary>
Background
}

View File

@@ -0,0 +1,22 @@
namespace DocumentService.Client.Models.ValueObjects;
/// <summary>
/// Specifies the type of stamp to add to a PDF document.
/// </summary>
public enum StampType
{
/// <summary>
/// Text-based stamp with custom text, font, and color.
/// </summary>
Text,
/// <summary>
/// Image-based stamp (PNG/JPEG overlay).
/// </summary>
Image,
/// <summary>
/// Predefined stamp with standard text (e.g., CONFIDENTIAL, APPROVED).
/// </summary>
Predefined
}

View File

@@ -0,0 +1,22 @@
namespace DocumentService.Client.Models.ValueObjects;
/// <summary>
/// Text markup annotation style (highlight, underline, strikeout)
/// </summary>
public enum TextMarkupStyle
{
/// <summary>
/// Highlight text with background color
/// </summary>
Highlight,
/// <summary>
/// Underline text
/// </summary>
Underline,
/// <summary>
/// Strikeout text (strikethrough)
/// </summary>
Strikeout
}