Compare commits
3 Commits
d03806e622
...
077eb1e017
| Author | SHA1 | Date | |
|---|---|---|---|
| 077eb1e017 | |||
| 8b154e7378 | |||
| a12d529d9e |
348
CONTROLLER_ENDPOINTS.md
Normal file
348
CONTROLLER_ENDPOINTS.md
Normal file
@@ -0,0 +1,348 @@
|
||||
# DocumentOperator - Controller & Endpoint Specification
|
||||
|
||||
**Project:** DocumentService (DOC)
|
||||
**Ticket:** DOC-1 - GDPicture and Nutrient Replacing
|
||||
**Owner:** Hakan Tek
|
||||
**Date:** July 3, 2026
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
This specification defines the controller structure and REST API endpoints for the DocumentOperator service.
|
||||
|
||||
---
|
||||
|
||||
## PdfValidationController
|
||||
|
||||
### Endpoint: PDF Validation
|
||||
**Route:** `POST /api/pdf/validation/validate`
|
||||
**Function:** Checks whether the file is a valid PDF, whether it is corrupted, and returns basic information
|
||||
|
||||
**Input:**
|
||||
- PDF file (multipart/form-data)
|
||||
|
||||
**Output:**
|
||||
```json
|
||||
{
|
||||
"isValid": bool,
|
||||
"pdfVersion": string,
|
||||
"pageCount": int,
|
||||
"fileSize": long,
|
||||
"encrypted": bool,
|
||||
"errors": string[]
|
||||
}
|
||||
```
|
||||
|
||||
**Usage:** All products - basic PDF input check
|
||||
|
||||
---
|
||||
|
||||
### Endpoint: PDF/A Validation
|
||||
**Route:** `POST /api/pdf/validation/validate-pdfa`
|
||||
**Function:** PDF/A conformance check (embedded fonts, encryption, JavaScript, etc.)
|
||||
|
||||
**Input:**
|
||||
- PDF file (multipart/form-data)
|
||||
|
||||
**Output:**
|
||||
```json
|
||||
{
|
||||
"isValid": bool,
|
||||
"pdfaVersion": string,
|
||||
"pageCount": int,
|
||||
"errors": string[],
|
||||
"warnings": string[]
|
||||
}
|
||||
```
|
||||
|
||||
**Usage:** taskFLOW, eParser - ensuring PDF/A conformance
|
||||
|
||||
---
|
||||
|
||||
## PdfAttachmentController
|
||||
|
||||
### Endpoint: Attachment Check
|
||||
**Route:** `POST /api/pdf/attachments/check`
|
||||
**Function:** Detects whether embedded files (e.g. ZUGFeRD XML) are present in the PDF
|
||||
|
||||
**Input:**
|
||||
- PDF file (multipart/form-data)
|
||||
|
||||
**Output:**
|
||||
```json
|
||||
{
|
||||
"hasAttachments": bool,
|
||||
"attachmentCount": int,
|
||||
"attachments": [
|
||||
{
|
||||
"fileName": string,
|
||||
"mimeType": string,
|
||||
"size": long
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Usage:** eParser (ZUGFeRD), ErgebnisberichtCreator
|
||||
|
||||
---
|
||||
|
||||
### Endpoint: Attachment Extraction
|
||||
**Route:** `POST /api/pdf/attachments/extract`
|
||||
**Function:** Extracts all embedded files from the PDF and saves them to the specified path
|
||||
|
||||
**Input:**
|
||||
```json
|
||||
{
|
||||
"file": "PDF (multipart/form-data)",
|
||||
"outputPath": string
|
||||
}
|
||||
```
|
||||
|
||||
**Output:**
|
||||
```json
|
||||
{
|
||||
"success": bool,
|
||||
"extractedFiles": [
|
||||
{
|
||||
"fileName": string,
|
||||
"savedPath": string,
|
||||
"size": long
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Usage:** eParser (ZUGFeRD XML extraction)
|
||||
|
||||
---
|
||||
|
||||
## PdfOperationsController
|
||||
|
||||
### Endpoint: PDF Merge
|
||||
**Route:** `POST /api/pdf/operations/merge`
|
||||
**Function:** Merges multiple PDFs into a single file
|
||||
|
||||
**Input:**
|
||||
```json
|
||||
{
|
||||
"sourceFiles": string[],
|
||||
"outputPath": string
|
||||
}
|
||||
```
|
||||
|
||||
**Output:**
|
||||
```json
|
||||
{
|
||||
"success": bool,
|
||||
"outputPath": string,
|
||||
"pageCount": int,
|
||||
"fileSize": long
|
||||
}
|
||||
```
|
||||
|
||||
**Usage:** signFLOW (Envelope Generator), ErgebnisberichtCreator, ResultHandler (windream)
|
||||
|
||||
---
|
||||
|
||||
### Endpoint: PDF Stamp
|
||||
**Route:** `POST /api/pdf/operations/stamp`
|
||||
**Function:** Adds stamps to PDF pages (APPROVED, CONFIDENTIAL, etc.)
|
||||
|
||||
**Input:**
|
||||
```json
|
||||
{
|
||||
"file": "PDF (multipart/form-data)",
|
||||
"stamp": {
|
||||
"text": string,
|
||||
"position": string,
|
||||
"pages": string,
|
||||
"color": string,
|
||||
"opacity": float
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Output:**
|
||||
```json
|
||||
{
|
||||
"success": bool,
|
||||
"outputPath": string
|
||||
}
|
||||
```
|
||||
|
||||
**Usage:** ErgebnisberichtCreator
|
||||
|
||||
---
|
||||
|
||||
### Endpoint: PDF Annotate
|
||||
**Route:** `POST /api/pdf/operations/annotate`
|
||||
**Function:** Adds comments, highlights, and markings to the PDF
|
||||
|
||||
**Input:**
|
||||
```json
|
||||
{
|
||||
"file": "PDF (multipart/form-data)",
|
||||
"annotations": [
|
||||
{
|
||||
"type": string,
|
||||
"page": int,
|
||||
"position": object,
|
||||
"text": string
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Output:**
|
||||
```json
|
||||
{
|
||||
"success": bool,
|
||||
"outputPath": string
|
||||
}
|
||||
```
|
||||
|
||||
**Usage:** signFLOW (Envelope Generator)
|
||||
|
||||
---
|
||||
|
||||
## PdfRenderController
|
||||
|
||||
### Endpoint: PDF Preview
|
||||
**Route:** `POST /api/pdf/render/preview`
|
||||
**Function:** Renders PDF pages as PNG/JPEG for preview
|
||||
|
||||
**Input:**
|
||||
```json
|
||||
{
|
||||
"file": "PDF (multipart/form-data)",
|
||||
"page": int,
|
||||
"format": string,
|
||||
"dpi": int
|
||||
}
|
||||
```
|
||||
|
||||
**Output:**
|
||||
```json
|
||||
{
|
||||
"images": [
|
||||
{
|
||||
"page": int,
|
||||
"base64": string,
|
||||
"width": int,
|
||||
"height": int
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Usage:** taskFLOW, fileFLOW, easyFLOW, orgFLOW - PDF preview
|
||||
|
||||
---
|
||||
|
||||
## PdfConversionController
|
||||
|
||||
### Endpoint: Convert PDF to PDF/A
|
||||
**Route:** `POST /api/pdf/conversion/to-pdfa`
|
||||
**Function:** Converts a standard PDF to PDF/A
|
||||
|
||||
**Input:**
|
||||
```json
|
||||
{
|
||||
"file": "PDF (multipart/form-data)",
|
||||
"pdfaLevel": string
|
||||
}
|
||||
```
|
||||
|
||||
**Output:**
|
||||
```json
|
||||
{
|
||||
"success": bool,
|
||||
"outputPath": string,
|
||||
"pdfaVersion": string
|
||||
}
|
||||
```
|
||||
|
||||
**Usage:** taskFLOW (optional conversion)
|
||||
|
||||
---
|
||||
|
||||
### Endpoint: Convert PDF/A to PDF
|
||||
**Route:** `POST /api/pdf/conversion/from-pdfa`
|
||||
**Function:** Converts PDF/A to a standard PDF
|
||||
|
||||
**Input:**
|
||||
```json
|
||||
{
|
||||
"file": "PDF (multipart/form-data)"
|
||||
}
|
||||
```
|
||||
|
||||
**Output:**
|
||||
```json
|
||||
{
|
||||
"success": bool,
|
||||
"outputPath": string
|
||||
}
|
||||
```
|
||||
|
||||
**Usage:** taskFLOW (optional conversion)
|
||||
|
||||
---
|
||||
|
||||
## Technical Specifications
|
||||
|
||||
### Framework Support
|
||||
- ✓ .NET Core (3.1+, 6.0+, 8.0+)
|
||||
- ✓ .NET Framework (4.7.2+, 4.8+)
|
||||
|
||||
### Client Usage
|
||||
The service can be used on the client side **without manual HTTP response handling**:
|
||||
- Provide REST client wrapper
|
||||
- SDK for C# clients
|
||||
- Automatic serialization/deserialization
|
||||
- Abstracted error handling
|
||||
|
||||
**Example Client SDK:**
|
||||
```csharp
|
||||
var client = new DocumentOperatorClient("https://api.example.com");
|
||||
var result = await client.Pdf.Validation.ValidateAsync(pdfFile);
|
||||
if (result.IsValid) { ... }
|
||||
```
|
||||
|
||||
### Response Format
|
||||
- Default: JSON
|
||||
- Errors: HTTP Status Codes (400, 404, 500) + JSON error object
|
||||
- Success: HTTP 200 + JSON response
|
||||
|
||||
### Authentication
|
||||
- API Key (Header: `X-API-Key`)
|
||||
- Optional: OAuth2/JWT for advanced scenarios
|
||||
|
||||
### Swagger/OpenAPI
|
||||
- Complete API documentation
|
||||
- Interactive test UI
|
||||
- Code generation for clients
|
||||
|
||||
---
|
||||
|
||||
## Prioritization
|
||||
|
||||
### Phase 1 (Priority)
|
||||
1. PdfValidationController - both endpoints
|
||||
2. PdfAttachmentController - both endpoints
|
||||
3. PdfOperationsController - Merge endpoint
|
||||
|
||||
### Phase 2
|
||||
4. PdfOperationsController - Stamp & Annotate
|
||||
5. PdfRenderController - Preview
|
||||
|
||||
### Phase 3
|
||||
6. PdfConversionController - both endpoints
|
||||
|
||||
---
|
||||
|
||||
**Last Updated:** July 3, 2026
|
||||
**Author:** Hakan Tek
|
||||
**Status:** Draft - Awaiting Feedback
|
||||
@@ -1,5 +1,8 @@
|
||||
namespace DocumentOperator.API.Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// Placeholder class for Serilog configuration extensions.
|
||||
/// </summary>
|
||||
public class SerilogConfiguration
|
||||
{
|
||||
}
|
||||
|
||||
@@ -3,8 +3,16 @@ using System.Reflection;
|
||||
|
||||
namespace DocumentOperator.API.Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides extension methods for configuring Swagger/OpenAPI documentation.
|
||||
/// </summary>
|
||||
public static class SwaggerConfiguration
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds Swagger documentation generation to the service collection.
|
||||
/// </summary>
|
||||
/// <param name="services">The service collection to add Swagger to.</param>
|
||||
/// <returns>The modified service collection.</returns>
|
||||
public static IServiceCollection AddSwaggerDocumentation(this IServiceCollection services)
|
||||
{
|
||||
services.AddSwaggerGen(options =>
|
||||
|
||||
@@ -16,12 +16,21 @@ public class ExceptionHandlingMiddleware
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly ILogger<ExceptionHandlingMiddleware> _logger;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ExceptionHandlingMiddleware"/> class.
|
||||
/// </summary>
|
||||
/// <param name="next">The next middleware in the pipeline.</param>
|
||||
/// <param name="logger">The logger instance for exception logging.</param>
|
||||
public ExceptionHandlingMiddleware(RequestDelegate next, ILogger<ExceptionHandlingMiddleware> logger)
|
||||
{
|
||||
_next = next;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the middleware to handle incoming HTTP requests and catch exceptions.
|
||||
/// </summary>
|
||||
/// <param name="context">The HTTP context for the current request.</param>
|
||||
public async Task InvokeAsync(HttpContext context)
|
||||
{
|
||||
try
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
namespace DocumentOperator.API.Middleware
|
||||
{
|
||||
/// <summary>
|
||||
/// Placeholder middleware for HTTP request/response logging.
|
||||
/// </summary>
|
||||
public class RequestLoggingMiddleware
|
||||
{
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
namespace DocumentOperator.API.Middleware
|
||||
{
|
||||
/// <summary>
|
||||
/// Placeholder middleware for multi-tenancy resolution via X-API-Key header.
|
||||
/// </summary>
|
||||
public class TenantResolutionMiddleware
|
||||
{
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ using DocumentOperator.Infrastructure.Configuration;
|
||||
using DocumentOperator.Application;
|
||||
using DocumentOperator.Infrastructure;
|
||||
using DocumentOperator.API.Middleware;
|
||||
using DocumentOperator.API.Endpoints.v1;
|
||||
using DocumentOperator.API.Configuration;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
@@ -41,6 +40,7 @@ try
|
||||
builder.Services.AddApplication(); // Application Layer (MediatR, FluentValidation, Behaviors)
|
||||
builder.Services.AddInfrastructure(); // Infrastructure Layer (DevExpress, Services)
|
||||
|
||||
builder.Services.AddControllers(); // Controllers (Controller-based API)
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddSwaggerDocumentation();
|
||||
|
||||
@@ -67,9 +67,9 @@ try
|
||||
app.UseHttpsRedirection();
|
||||
|
||||
// ========================================
|
||||
// 6. Endpoints (Minimal API)
|
||||
// 6. Endpoints (Controller-based API)
|
||||
// ========================================
|
||||
app.MapDocumentEndpoints(); // POST /api/v1/documents/validate
|
||||
app.MapControllers(); // Maps all [ApiController] controllers
|
||||
|
||||
Log.Information("DocumentOperator API started successfully");
|
||||
|
||||
@@ -86,4 +86,8 @@ finally
|
||||
}
|
||||
|
||||
// Make Program class accessible for Integration Tests
|
||||
/// <summary>
|
||||
/// Entry point class for the DocumentOperator API.
|
||||
/// Made partial and public for integration test access.
|
||||
/// </summary>
|
||||
public partial class Program { }
|
||||
@@ -1,9 +1,11 @@
|
||||
using Codecrete.SwissQRBill.Generator;
|
||||
using DevExpress.Drawing;
|
||||
using DevExpress.Pdf;
|
||||
using DocumentOperator.Application.Common.Interfaces;
|
||||
using DocumentOperator.Domain.Exceptions;
|
||||
using DocumentOperator.Domain.ValueObjects;
|
||||
using System.Drawing;
|
||||
using System.Runtime.Versioning;
|
||||
using ZXing;
|
||||
|
||||
namespace DocumentOperator.Infrastructure.Services.QrCodeProcessing;
|
||||
@@ -17,6 +19,7 @@ public sealed class DevExpressSwissQrCodeProcessor : ISwissQrCodeProcessor
|
||||
private const int QrCodeSearchDpi = 300; // High DPI for better QR code recognition
|
||||
|
||||
/// <inheritdoc />
|
||||
[SupportedOSPlatform("windows")]
|
||||
public async Task<SwissQrCodeData> ExtractSwissQrCodeAsync(byte[] pdfBytes, CancellationToken cancellationToken = default)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(pdfBytes);
|
||||
@@ -64,30 +67,39 @@ public sealed class DevExpressSwissQrCodeProcessor : ISwissQrCodeProcessor
|
||||
/// <summary>
|
||||
/// Renders a PDF page to a high-resolution bitmap for QR code detection
|
||||
/// </summary>
|
||||
private static Bitmap RenderPageToImage(PdfDocumentProcessor processor, int pageIndex)
|
||||
private static DXBitmap RenderPageToImage(PdfDocumentProcessor processor, int pageIndex)
|
||||
{
|
||||
// Render page at high DPI for better QR code recognition
|
||||
var pageImage = processor.CreateBitmap(pageIndex + 1, QrCodeSearchDpi);
|
||||
var pageImage = processor.CreateDXBitmap(pageIndex + 1, QrCodeSearchDpi);
|
||||
return pageImage;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decodes QR code from an image using ZXing library
|
||||
/// Decodes QR code from a DXBitmap image using ZXing library
|
||||
/// </summary>
|
||||
private static string? DecodeQrCodeFromImage(Bitmap image)
|
||||
[SupportedOSPlatform("windows")]
|
||||
private static string? DecodeQrCodeFromImage(DXBitmap dxImage)
|
||||
{
|
||||
// Convert DXBitmap to System.Drawing.Bitmap via MemoryStream
|
||||
using var ms = new MemoryStream();
|
||||
dxImage.Save(ms, DXImageFormat.Png);
|
||||
ms.Position = 0;
|
||||
|
||||
using var gdiImage = Image.FromStream(ms);
|
||||
using var gdiBitmap = new Bitmap(gdiImage);
|
||||
|
||||
var reader = new ZXing.Windows.Compatibility.BarcodeReader
|
||||
{
|
||||
AutoRotate = true,
|
||||
TryInverted = true,
|
||||
Options = new ZXing.Common.DecodingOptions
|
||||
{
|
||||
PossibleFormats = new[] { BarcodeFormat.QR_CODE },
|
||||
TryHarder = true
|
||||
PossibleFormats = [BarcodeFormat.QR_CODE],
|
||||
TryHarder = true,
|
||||
TryInverted = true
|
||||
}
|
||||
};
|
||||
|
||||
var result = reader.Decode(image);
|
||||
var result = reader.Decode(gdiBitmap);
|
||||
return result?.Text;
|
||||
}
|
||||
|
||||
@@ -155,7 +167,13 @@ public sealed class DevExpressSwissQrCodeProcessor : ISwissQrCodeProcessor
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps Codecrete Address to our AddressData value object
|
||||
/// Maps Codecrete Address to our AddressData value object.
|
||||
///
|
||||
/// NOTE: AddressLine1 and AddressLine2 (Combined Address / K-Type) are deprecated
|
||||
/// as of Swiss Payment Standards 2025 (effective 21 Nov 2025).
|
||||
/// The Swiss QR Bill now mandates Structured Address (S-Type) format.
|
||||
/// These fields are retained for backward compatibility with legacy QR codes
|
||||
/// generated before the deprecation date.
|
||||
/// </summary>
|
||||
private static AddressData MapAddress(Codecrete.SwissQRBill.Generator.Address address)
|
||||
{
|
||||
@@ -165,8 +183,10 @@ public sealed class DevExpressSwissQrCodeProcessor : ISwissQrCodeProcessor
|
||||
Name = address.Name ?? string.Empty,
|
||||
Street = address.Street,
|
||||
BuildingNumber = address.HouseNo,
|
||||
#pragma warning disable CS0618 // AddressLine1/AddressLine2 obsolete but required for backward compatibility
|
||||
AddressLine1 = address.AddressLine1,
|
||||
AddressLine2 = address.AddressLine2,
|
||||
#pragma warning restore CS0618
|
||||
PostalCode = address.PostalCode ?? string.Empty,
|
||||
City = address.Town ?? string.Empty,
|
||||
Country = address.CountryCode ?? string.Empty
|
||||
|
||||
@@ -13,6 +13,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DocumentOperator.Domain", "
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DocumentOperator.Tests", "DocumentOperator.Tests\DocumentOperator.Tests.csproj", "{32D2E997-3DA7-4061-8A50-DBB34BBC3E5A}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{3F9E8D8E-1234-4567-89AB-CDEF01234567}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
CONTROLLER_ENDPOINTS.md = CONTROLLER_ENDPOINTS.md
|
||||
REQUIRED_FEATURES.md = REQUIRED_FEATURES.md
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
||||
557
REQUIRED_FEATURES.md
Normal file
557
REQUIRED_FEATURES.md
Normal file
@@ -0,0 +1,557 @@
|
||||
# DocumentOperator - Required Functions and Features
|
||||
|
||||
**Project:** DocumentService (DOC)
|
||||
**Ticket:** DOC-1 - GDPicture and Nutrient Replacing
|
||||
**Owner:** Hakan Tek
|
||||
**Last Updated:** 03.07.2026
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
This document describes in detail all PDF processing functions that the DocumentOperator service must implement. These functions will be implemented using the DevExpress Office File API as a replacement for the GDPicture and Nutrient libraries.
|
||||
|
||||
---
|
||||
|
||||
## 1. Input Processing
|
||||
|
||||
### 1.1 PDF/A Validation
|
||||
|
||||
**Purpose:** Check whether a PDF file conforms to the PDF/A standard.
|
||||
|
||||
**What is PDF/A?**
|
||||
- ISO standard PDF format for long-term archiving
|
||||
- Versions: PDF/A-1, PDF/A-2, PDF/A-3
|
||||
- Has restrictions to guarantee future accessibility
|
||||
|
||||
**Criteria to Check:**
|
||||
- ✓ Are all fonts embedded? (required in PDF/A)
|
||||
- ✓ Is encryption present? (prohibited in PDF/A)
|
||||
- ✓ Are there external content links? (prohibited in PDF/A)
|
||||
- ✓ Does it contain JavaScript? (prohibited in PDF/A)
|
||||
- ✓ Is metadata correctly defined?
|
||||
- ✓ Are color profiles defined?
|
||||
- ✓ Is transparency usage compliant with the standard?
|
||||
|
||||
**Suggested API Endpoint:**
|
||||
```
|
||||
POST /api/pdf/validate-pdfa
|
||||
Body: { "filePath": "string" } or multipart file upload
|
||||
Response: {
|
||||
"isValid": true/false,
|
||||
"pdfaVersion": "PDF/A-2b",
|
||||
"errors": [],
|
||||
"warnings": []
|
||||
}
|
||||
```
|
||||
|
||||
**Example Usage Scenario:**
|
||||
```
|
||||
Input: invoice_2024.pdf
|
||||
Output: {
|
||||
"isValid": true,
|
||||
"pdfaVersion": "PDF/A-3b",
|
||||
"pageCount": 5,
|
||||
"errors": [],
|
||||
"warnings": ["Document contains optional content"]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 1.2 Attachment Check
|
||||
|
||||
**Purpose:** Detect whether embedded files (attachments) exist inside a PDF file.
|
||||
|
||||
**What is an Attachment?**
|
||||
- External files embedded inside a PDF
|
||||
- Examples: XML, Excel, images, other PDFs
|
||||
- Special use: XML data in ZUGFeRD/XRechnung e-invoices
|
||||
|
||||
**PDF/A Versions and Attachments:**
|
||||
- PDF/A-1: NO attachment support
|
||||
- PDF/A-2: NO attachment support
|
||||
- PDF/A-3: Attachment support AVAILABLE (most common use)
|
||||
|
||||
**Suggested API Endpoint:**
|
||||
```
|
||||
POST /api/pdf/has-attachments
|
||||
Response: {
|
||||
"hasAttachments": true/false,
|
||||
"attachmentCount": 2,
|
||||
"attachments": [
|
||||
{
|
||||
"fileName": "factur-x.xml",
|
||||
"mimeType": "application/xml",
|
||||
"size": 12345,
|
||||
"description": "ZUGFeRD Invoice Data"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 1.3 Extract Attachments
|
||||
|
||||
**Purpose:** Extract all embedded files from a PDF and save them to the specified location.
|
||||
|
||||
**Usage Scenarios:**
|
||||
- Extracting XML from ZUGFeRD e-invoices
|
||||
- Retrieving attached documents from PDF/A-3 files
|
||||
- Extracting embedded data for automated processing
|
||||
|
||||
**Suggested API Endpoint:**
|
||||
```
|
||||
POST /api/pdf/extract-attachments
|
||||
Body: {
|
||||
"filePath": "string",
|
||||
"outputPath": "string",
|
||||
"fileNamePattern": "{originalName}" // or "{index}_{originalName}"
|
||||
}
|
||||
Response: {
|
||||
"success": true,
|
||||
"extractedFiles": [
|
||||
{
|
||||
"fileName": "factur-x.xml",
|
||||
"savedPath": "C:\\Temp\\Attachments\\factur-x.xml",
|
||||
"size": 12345
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**DevExpress Implementation Example:**
|
||||
```csharp
|
||||
using DevExpress.Pdf;
|
||||
|
||||
using (PdfDocumentProcessor processor = new PdfDocumentProcessor())
|
||||
{
|
||||
processor.LoadDocument("invoice.pdf");
|
||||
|
||||
foreach (PdfEmbeddedFile embeddedFile in processor.Document.EmbeddedFiles)
|
||||
{
|
||||
byte[] fileData = embeddedFile.GetData();
|
||||
string outputPath = Path.Combine(targetFolder, embeddedFile.FileName);
|
||||
File.WriteAllBytes(outputPath, fileData);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 1.4 PDF File Validity Check
|
||||
|
||||
**Purpose:** Check whether a file is a genuine PDF and verify its structural integrity.
|
||||
|
||||
**Items to Check:**
|
||||
- ✓ Is the file actually a PDF? (Magic bytes: %PDF-)
|
||||
- ✓ Is the PDF header valid?
|
||||
- ✓ Is the PDF structure not broken/corrupt?
|
||||
- ✓ Can the file be opened?
|
||||
- ✓ What is the PDF version? (1.4, 1.7, 2.0, etc.)
|
||||
- ✓ Page count
|
||||
- ✓ Can basic metadata be read?
|
||||
|
||||
**Suggested API Endpoint:**
|
||||
```
|
||||
POST /api/pdf/validate
|
||||
Response: {
|
||||
"isValid": true,
|
||||
"pdfVersion": "1.7",
|
||||
"pageCount": 5,
|
||||
"fileSize": 524288,
|
||||
"encrypted": false,
|
||||
"errors": [],
|
||||
"metadata": {
|
||||
"title": "Invoice 2024",
|
||||
"author": "Company XYZ",
|
||||
"creationDate": "2024-01-15T10:30:00"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Error Scenarios:**
|
||||
```
|
||||
Scenario 1 - Corrupt file:
|
||||
{
|
||||
"isValid": false,
|
||||
"errors": ["PDF header is missing or corrupted"]
|
||||
}
|
||||
|
||||
Scenario 2 - Wrong format:
|
||||
{
|
||||
"isValid": false,
|
||||
"errors": ["File is not a PDF (detected: JPEG image)"]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. PDF Processing Operations
|
||||
|
||||
### 2.1 PDF Merge (Concatenate/Merge)
|
||||
|
||||
**Purpose:** Merge multiple PDF files into a single PDF.
|
||||
|
||||
**Usage Areas:**
|
||||
- **signFLOW (Envelope Generator):** Merging multiple documents into a single envelope
|
||||
- **ErgebnisberichtCreator:** Merging report sections
|
||||
- **ResultHandler (windream):** Document merging
|
||||
|
||||
**Suggested API Endpoint:**
|
||||
```
|
||||
POST /api/pdf/merge
|
||||
Body: {
|
||||
"sourceFiles": ["file1.pdf", "file2.pdf", "file3.pdf"],
|
||||
"outputPath": "merged.pdf",
|
||||
"options": {
|
||||
"addBookmarks": true,
|
||||
"preserveMetadata": true,
|
||||
"compressionLevel": "medium"
|
||||
}
|
||||
}
|
||||
Response: {
|
||||
"success": true,
|
||||
"outputPath": "merged.pdf",
|
||||
"pageCount": 15,
|
||||
"fileSize": 1048576
|
||||
}
|
||||
```
|
||||
|
||||
**Features:**
|
||||
- Preserve page order
|
||||
- Add bookmarks (for each source file)
|
||||
- Merge metadata
|
||||
- Preserve PDF/A compliance
|
||||
|
||||
---
|
||||
|
||||
### 2.2 Stamping
|
||||
|
||||
**Purpose:** Add a stamp to PDF pages - e.g. "APPROVED", "CONFIDENTIAL", "DRAFT"
|
||||
|
||||
**Usage Area:**
|
||||
- **ErgebnisberichtCreator:** Approval/status stamps on reports
|
||||
|
||||
**Suggested API Endpoint:**
|
||||
```
|
||||
POST /api/pdf/stamp
|
||||
Body: {
|
||||
"filePath": "document.pdf",
|
||||
"stamp": {
|
||||
"text": "APPROVED",
|
||||
"position": "TopRight", // TopLeft, TopRight, BottomLeft, BottomRight, Center
|
||||
"pages": "all", // or "1,3,5" or "1-5"
|
||||
"color": "#FF0000",
|
||||
"opacity": 0.5,
|
||||
"rotation": 45,
|
||||
"fontSize": 48
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Stamp Types:**
|
||||
- Text stamp
|
||||
- Image stamp (logo, signature)
|
||||
- QR code stamp
|
||||
- Date/time stamp
|
||||
|
||||
---
|
||||
|
||||
### 2.3 Annotation
|
||||
|
||||
**Purpose:** Add comments, highlights, notes, and drawings to a PDF.
|
||||
|
||||
**Usage Area:**
|
||||
- **signFLOW:** Signature fields, comments, highlights
|
||||
|
||||
**Annotation Types:**
|
||||
- Text annotations (comments)
|
||||
- Highlight
|
||||
- Underline
|
||||
- Strikeout
|
||||
- Shapes (rectangle, circle, arrow)
|
||||
- Stamps (predefined stamps)
|
||||
|
||||
**Suggested API Endpoint:**
|
||||
```
|
||||
POST /api/pdf/annotate
|
||||
Body: {
|
||||
"filePath": "document.pdf",
|
||||
"annotations": [
|
||||
{
|
||||
"type": "highlight",
|
||||
"page": 1,
|
||||
"rect": {"x": 100, "y": 200, "width": 200, "height": 20},
|
||||
"color": "#FFFF00"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"page": 1,
|
||||
"position": {"x": 100, "y": 250},
|
||||
"text": "This section is important!",
|
||||
"author": "Hakan Tek"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2.4 PDF/A Conversion
|
||||
|
||||
**Purpose:** PDF ↔ PDF/A format conversion.
|
||||
|
||||
**Usage Area:**
|
||||
- **taskFLOW:** Converting PDF/A to standard PDF (optional)
|
||||
|
||||
**Two-Way Operation:**
|
||||
|
||||
#### A) PDF → PDF/A (For archiving)
|
||||
```
|
||||
POST /api/pdf/convert-to-pdfa
|
||||
Body: {
|
||||
"filePath": "document.pdf",
|
||||
"pdfaLevel": "PDF/A-2b", // or PDF/A-1b, PDF/A-3b
|
||||
"embedFonts": true,
|
||||
"colorProfile": "sRGB"
|
||||
}
|
||||
```
|
||||
|
||||
#### B) PDF/A → PDF (Remove restrictions)
|
||||
```
|
||||
POST /api/pdf/convert-from-pdfa
|
||||
Body: {
|
||||
"filePath": "document-pdfa.pdf",
|
||||
"removeRestrictions": true
|
||||
}
|
||||
```
|
||||
|
||||
**During Conversion:**
|
||||
- Embed all fonts
|
||||
- Remove JavaScript
|
||||
- Resolve external references
|
||||
- Add color profiles
|
||||
- Metadata standardization
|
||||
|
||||
---
|
||||
|
||||
## 3. PDF Rendering (Preview/Rendering)
|
||||
|
||||
### 3.1 PDF Preview
|
||||
|
||||
**Purpose:** Render PDF pages and return them as images (PNG/JPEG).
|
||||
|
||||
**Usage Areas:**
|
||||
- **taskFLOW:** PDF preview
|
||||
- **fileFLOW:** PDF preview
|
||||
- **easyFLOW:** PDF preview
|
||||
- **orgFLOW:** PDF preview
|
||||
|
||||
**Suggested API Endpoint:**
|
||||
```
|
||||
POST /api/pdf/render
|
||||
Body: {
|
||||
"filePath": "document.pdf",
|
||||
"page": 1, // or "all"
|
||||
"format": "png", // or "jpeg"
|
||||
"dpi": 150,
|
||||
"width": 800, // optional, aspect ratio preserved
|
||||
"quality": 85 // for JPEG
|
||||
}
|
||||
Response: {
|
||||
"images": [
|
||||
{
|
||||
"page": 1,
|
||||
"base64": "iVBORw0KGgoAAAANS...",
|
||||
"width": 800,
|
||||
"height": 1132
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Features:**
|
||||
- Page selection (single page or all)
|
||||
- Configurable DPI
|
||||
- Format selection (PNG, JPEG)
|
||||
- Thumbnail generation
|
||||
|
||||
---
|
||||
|
||||
## 4. Advanced Functions
|
||||
|
||||
### 4.1 Text Extraction
|
||||
|
||||
**Purpose:** Extract plain text from a PDF.
|
||||
|
||||
```
|
||||
POST /api/pdf/extract-text
|
||||
Response: {
|
||||
"text": "Extracted text content...",
|
||||
"pageTexts": [
|
||||
{ "page": 1, "text": "Page 1 content..." },
|
||||
{ "page": 2, "text": "Page 2 content..." }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4.2 Metadata Read/Write
|
||||
|
||||
**Purpose:** Read and update PDF metadata.
|
||||
|
||||
```
|
||||
GET /api/pdf/metadata?filePath=document.pdf
|
||||
Response: {
|
||||
"title": "Invoice 2024",
|
||||
"author": "Company XYZ",
|
||||
"subject": "Monthly Invoice",
|
||||
"keywords": ["invoice", "payment"],
|
||||
"creator": "Microsoft Word",
|
||||
"producer": "Adobe PDF Library",
|
||||
"creationDate": "2024-01-15T10:30:00",
|
||||
"modificationDate": "2024-01-15T14:45:00"
|
||||
}
|
||||
|
||||
POST /api/pdf/metadata
|
||||
Body: {
|
||||
"filePath": "document.pdf",
|
||||
"metadata": {
|
||||
"title": "Updated Title",
|
||||
"author": "New Author"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4.3 Form Field Operations
|
||||
|
||||
**Purpose:** Read and fill fields in PDF forms.
|
||||
|
||||
```
|
||||
GET /api/pdf/form-fields
|
||||
Response: {
|
||||
"fields": [
|
||||
{ "name": "customerName", "type": "text", "value": "" },
|
||||
{ "name": "invoiceDate", "type": "text", "value": "" },
|
||||
{ "name": "approved", "type": "checkbox", "value": false }
|
||||
]
|
||||
}
|
||||
|
||||
POST /api/pdf/fill-form
|
||||
Body: {
|
||||
"filePath": "form.pdf",
|
||||
"fields": {
|
||||
"customerName": "ACME Corp",
|
||||
"invoiceDate": "2024-07-03",
|
||||
"approved": true
|
||||
},
|
||||
"flatten": true // Make fields non-editable
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Priority Order
|
||||
|
||||
### Phase 1 - Core Functions (PRIORITY)
|
||||
1. ✓ PDF Validation (1.4)
|
||||
2. ✓ PDF/A Validation (1.1)
|
||||
3. ✓ Attachment Check (1.2)
|
||||
4. ✓ Extract Attachments (1.3)
|
||||
5. ✓ PDF Merge (2.1)
|
||||
|
||||
### Phase 2 - Advanced Functions
|
||||
6. ✓ Stamping (2.2)
|
||||
7. ✓ Annotation (2.3)
|
||||
8. ✓ PDF Preview/Rendering (3.1)
|
||||
|
||||
### Phase 3 - Conversion and Extras
|
||||
9. ✓ PDF/A Conversion (2.4)
|
||||
10. ✓ Text Extraction (4.1)
|
||||
11. ✓ Metadata Operations (4.2)
|
||||
12. ✓ Form Field Operations (4.3)
|
||||
|
||||
---
|
||||
|
||||
## 6. Technical Requirements
|
||||
|
||||
### 6.1 Library to Use
|
||||
- **DevExpress Office File API (PDF Document API)**
|
||||
- NuGet: `DevExpress.Pdf`
|
||||
- License: Existing DevExpress license
|
||||
|
||||
### 6.2 REST API Requirements
|
||||
- HTTP REST endpoints
|
||||
- JSON request/response
|
||||
- Swagger/OpenAPI documentation
|
||||
- File upload support (multipart/form-data)
|
||||
|
||||
### 6.3 Performance Goals
|
||||
- Asynchronous processing (async/await)
|
||||
- Large file support (streaming)
|
||||
- Memory optimization
|
||||
- Parallel processing support
|
||||
|
||||
### 6.4 Error Handling
|
||||
- Detailed error messages
|
||||
- HTTP status codes (400, 404, 500, etc.)
|
||||
- Validation errors
|
||||
- Logging (Serilog recommended)
|
||||
|
||||
---
|
||||
|
||||
## 7. Test Strategy
|
||||
|
||||
### Unit Tests
|
||||
- Separate test for each function
|
||||
- Various PDF versions
|
||||
- Edge cases (empty file, corrupt PDF, etc.)
|
||||
|
||||
### Integration Tests
|
||||
- API endpoint tests
|
||||
- File upload/download tests
|
||||
- End-to-end scenarios
|
||||
|
||||
### Test Files
|
||||
- Valid PDF
|
||||
- Valid PDF/A (1b, 2b, 3b)
|
||||
- Corrupt PDF
|
||||
- PDF with attachments
|
||||
- PDF with forms
|
||||
- Encrypted PDF
|
||||
- Large files (>100MB)
|
||||
|
||||
---
|
||||
|
||||
## 8. Documentation Requirements
|
||||
|
||||
### Swagger/OpenAPI
|
||||
- Detailed description for all endpoints
|
||||
- Request/Response examples
|
||||
- Error codes description
|
||||
- Authentication (if applicable)
|
||||
|
||||
### Code Documentation
|
||||
- XML comments (for C#)
|
||||
- Interface documentation
|
||||
- Usage examples
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- **DevExpress PDF API Documentation:** https://docs.devexpress.com/OfficeFileAPI/
|
||||
- **PDF/A Standard:** ISO 19005
|
||||
- **ZUGFeRD Standard:** https://www.ferd-net.de/
|
||||
- **Ticket:** DOC-1 (M:\Austausch\DocumentOperator)
|
||||
|
||||
---
|
||||
|
||||
**Last Updated:** 03.07.2026
|
||||
**Prepared by:** Hakan Tek
|
||||
**Status:** Initial Draft
|
||||
Reference in New Issue
Block a user