Enabled multi-targeting in `DocumentService.Application` to support `net462`, `net480`, and `net8.0`. Updated dependencies with framework-specific versions and added conditional configurations for compatibility. Refactored `DocumentService.Client` by removing redundant enums and unused code. Adjusted namespaces and project references to streamline the codebase and improve maintainability. These changes modernize the project, enhance compatibility, and align with modular design principles.
DocumentService.Client
.NET client library for DocumentService API - supports .NET Framework 4.6.2, 4.8, and .NET 8.0.
Features
- Multi-target support: .NET Framework 4.6.2, 4.8, and .NET 8.0
- HttpClientFactory integration: Proper lifecycle management and connection pooling
- Separate clients per controller:
IPdfValidationClient,IPdfAttachmentClient,IPdfOperationsClient,ISwissQrCodeClient - Dual input support: Multipart (Stream) and Base64 (byte[]) for all endpoints
- Strongly-typed models: Shared request/response DTOs with XML documentation
Installation
dotnet add package DocumentService.Client
Configuration
ASP.NET Core / .NET 8.0
using DocumentService.Client.Extensions;
var builder = WebApplication.CreateBuilder(args);
// Register all DocumentService clients
builder.Services.AddDocumentServiceClients(options =>
{
options.BaseUrl = "https://documentservice.example.com";
options.Timeout = TimeSpan.FromMinutes(10);
options.MaxRetries = 3;
options.ThrowOnError = true;
});
var app = builder.Build();
.NET Framework 4.6.2 / 4.8
using DocumentService.Client.Extensions;
using Microsoft.Extensions.DependencyInjection;
var services = new ServiceCollection();
services.AddDocumentServiceClients(options =>
{
options.BaseUrl = "https://documentservice.example.com";
options.Timeout = TimeSpan.FromMinutes(10);
});
var serviceProvider = services.BuildServiceProvider();
Usage Examples
1. PDF Validation
using DocumentService.Client.Interfaces;
public class PdfService
{
private readonly IPdfValidationClient _validationClient;
public PdfService(IPdfValidationClient validationClient)
{
_validationClient = validationClient;
}
public async Task ValidatePdfAsync(Stream pdfStream)
{
// Option 1: From Stream (multipart)
var result = await _validationClient.ValidatePdfAsync(pdfStream);
Console.WriteLine($"Pages: {result.PageCount}");
Console.WriteLine($"Version: {result.PdfVersion}");
Console.WriteLine($"Encrypted: {result.IsEncrypted}");
}
public async Task ValidatePdfFromBytesAsync(byte[] pdfBytes)
{
// Option 2: From byte array (Base64 JSON)
var result = await _validationClient.ValidatePdfAsync(pdfBytes);
Console.WriteLine($"File Size: {result.FileSizeBytes} bytes");
}
public async Task ValidatePdfAAsync(string filePath)
{
using var stream = File.OpenRead(filePath);
var result = await _validationClient.ValidatePdfAAsync(stream);
Console.WriteLine($"Valid PDF/A: {result.IsValid}");
Console.WriteLine($"PDF/A Version: {result.PdfAVersion}");
if (result.Errors.Any())
{
Console.WriteLine("Errors:");
foreach (var error in result.Errors)
{
Console.WriteLine($" - {error}");
}
}
}
}
2. PDF Attachments
using DocumentService.Client.Interfaces;
using DocumentService.Client.Extensions; // For ToBase64StringAsync, ToBytesAsync
public class AttachmentService
{
private readonly IPdfAttachmentClient _attachmentClient;
public AttachmentService(IPdfAttachmentClient attachmentClient)
{
_attachmentClient = attachmentClient;
}
public async Task CheckAttachmentsAsync(byte[] pdfBytes)
{
var result = await _attachmentClient.CheckAttachmentsAsync(pdfBytes);
Console.WriteLine($"Has Attachments: {result.HasAttachments}");
Console.WriteLine($"Attachment Count: {result.AttachmentCount}");
foreach (var attachment in result.Attachments)
{
Console.WriteLine($" - {attachment.FileName} ({attachment.Size} bytes)");
}
}
public async Task ExtractAttachmentsAsync(Stream pdfStream, string outputPath)
{
// Returns ZIP file as Stream (memory efficient!)
using var zipStream = await _attachmentClient.ExtractAttachmentsAsync(pdfStream);
// Option 1: Save directly to file
using var fileStream = File.Create(outputPath);
await zipStream.CopyToAsync(fileStream);
Console.WriteLine($"Attachments extracted to: {outputPath}");
}
public async Task ExtractAttachmentsToBase64Async(byte[] pdfBytes)
{
// Returns ZIP as Stream
using var zipStream = await _attachmentClient.ExtractAttachmentsAsync(pdfBytes);
// Option 2: Convert to Base64 using extension method
string base64Zip = await zipStream.ToBase64StringAsync();
Console.WriteLine($"ZIP as Base64: {base64Zip.Substring(0, 50)}...");
}
public async Task ExtractAttachmentsToBytesAsync(Stream pdfStream)
{
// Returns ZIP as Stream
using var zipStream = await _attachmentClient.ExtractAttachmentsAsync(pdfStream);
// Option 3: Convert to byte array using extension method
byte[] zipBytes = await zipStream.ToBytesAsync();
Console.WriteLine($"ZIP size: {zipBytes.Length} bytes");
}
}
3. PDF Operations (Merge, Annotate, Stamp)
using DocumentService.Client.Interfaces;
using DocumentService.Client.Models.Requests;
using DocumentService.Client.Models.ValueObjects;
using DocumentService.Client.Extensions; // For Stream extensions
public class OperationsService
{
private readonly IPdfOperationsClient _operationsClient;
public OperationsService(IPdfOperationsClient operationsClient)
{
_operationsClient = operationsClient;
}
// MERGE
public async Task<Stream> MergePdfsAsync(List<string> pdfPaths)
{
var streams = pdfPaths.Select(File.OpenRead).ToList();
// Returns merged PDF as Stream
var mergedStream = await _operationsClient.MergeAsync(
streams,
pageRanges: new List<string?> { "1-2", null, "3,5" } // Optional
);
foreach (var stream in streams) stream.Dispose();
return mergedStream; // Caller responsible for disposing
}
public async Task MergePdfsToFileAsync(List<string> pdfPaths, string outputPath)
{
using var mergedStream = await MergePdfsAsync(pdfPaths);
// Save to file
using var fileStream = File.Create(outputPath);
await mergedStream.CopyToAsync(fileStream);
}
// ANNOTATE
public async Task<byte[]> AddHighlightAsync(byte[] pdfBytes)
{
var request = new AddAnnotationBase64Request
{
Base64Pdf = Convert.ToBase64String(pdfBytes),
AnnotationType = AnnotationType.TextMarkup,
PageNumber = 1,
X1 = 100,
Y1 = 200,
Width = 150,
Height = 20,
Color = "FFFF00", // Yellow
TextMarkupStyle = TextMarkupStyle.Highlight,
Origin = AnnotationOrigin.TopLeft
};
// Returns Stream - convert to bytes
using var annotatedStream = await _operationsClient.AnnotateAsync(pdfBytes, request);
return await annotatedStream.ToBytesAsync();
}
// STAMP
public async Task<Stream> AddStampAsync(Stream pdfStream)
{
var request = new AddStampBase64Request
{
Base64Pdf = string.Empty, // Will be filled by client
StampType = StampType.Text,
X = 300,
Y = 50,
Text = "CONFIDENTIAL",
FontName = "Arial",
FontSize = 24,
Color = "FF0000", // Red
Opacity = 0.5,
Rotation = 45,
Placement = StampPlacement.Foreground,
Origin = AnnotationOrigin.BottomLeft
};
// Returns stamped PDF as Stream
return await _operationsClient.StampAsync(pdfStream, request);
}
}
4. Swiss QR Code Extraction
using DocumentService.Client.Interfaces;
public class QrCodeService
{
private readonly ISwissQrCodeClient _qrCodeClient;
public QrCodeService(ISwissQrCodeClient qrCodeClient)
{
_qrCodeClient = qrCodeClient;
}
public async Task ExtractQrCodeAsync(byte[] pdfBytes)
{
// Get parsed Bill object
var result = await _qrCodeClient.ExtractSwissQrCodeAsync(pdfBytes, raw: false);
Console.WriteLine($"Bill: {result.Bill}");
}
public async Task ExtractRawQrCodeAsync(Stream pdfStream)
{
// Get raw QR text lines
var result = await _qrCodeClient.ExtractSwissQrCodeAsync(pdfStream, raw: true);
Console.WriteLine("Raw QR Lines:");
foreach (var line in result.RawLines)
{
Console.WriteLine($" {line}");
}
}
}
API Endpoints
| Client | Method | API Endpoint | Description |
|---|---|---|---|
| IPdfValidationClient | ValidatePdfAsync() |
POST /api/pdf/validation/validate |
Validates PDF and returns metadata |
| IPdfValidationClient | ValidatePdfAAsync() |
POST /api/pdf/validation/validate-pdfa |
Validates PDF/A conformance |
| IPdfAttachmentClient | CheckAttachmentsAsync() |
POST /api/pdf/attachments/check |
Checks for embedded attachments |
| IPdfAttachmentClient | ExtractAttachmentsAsync() |
POST /api/pdf/attachments/extract |
Extracts attachments as ZIP |
| IPdfOperationsClient | MergeAsync() |
POST /api/pdf/operations/merge |
Merges multiple PDFs |
| IPdfOperationsClient | AnnotateAsync() |
POST /api/pdf/operations/annotate |
Adds annotations (highlight, notes, etc.) |
| IPdfOperationsClient | StampAsync() |
POST /api/pdf/operations/stamp |
Adds text/image stamps |
| ISwissQrCodeClient | ExtractSwissQrCodeAsync() |
POST /api/pdf/qr-code/extract-swiss |
Extracts Swiss QR Code data |
Error Handling
try
{
var result = await validationClient.ValidatePdfAsync(pdfBytes);
}
catch (HttpRequestException ex)
{
Console.WriteLine($"HTTP error: {ex.Message}");
}
catch (InvalidOperationException ex)
{
Console.WriteLine($"API returned null: {ex.Message}");
}
License
Copyright © 2026 Digital Data GmbH. All rights reserved.