Compare commits

..

11 Commits

Author SHA1 Message Date
3a2fa77862 refactor(WebUI): configure HtpClient 2026-06-15 11:33:03 +02:00
cfa6dbd2de remove deprecated parameter 2026-06-15 11:18:34 +02:00
eb2603f389 remove wwwroot/app.css and add js and css dependencies 2026-06-15 11:04:35 +02:00
456178bee1 migrate shared components 2026-06-15 10:54:08 +02:00
2c41c74510 refactor(COPILOT_CONTEXT): update to be compatible with the migration from ReceiverUI to WebUI 2026-06-15 10:44:19 +02:00
bb73795d68 remove MIGRATION_CONTEXT.md
Migrate ReceiverUI to hybrid Blazor WebUI architecture

Migrated the `EnvelopeGenerator.ReceiverUI` project to a new
hybrid Blazor architecture (`EnvelopeGenerator.WebUI`) that
supports both Blazor Server and WebAssembly (WASM) modes.

- Added `WebUI` (server) and `WebUI.Client` (WASM) projects.
- Migrated client-side pages to `WebUI.Client` with
  `@rendermode InteractiveWebAssembly`.
- Migrated server-side pages to `WebUI` with
  `@rendermode InteractiveServer`.
- Added YARP reverse proxy (`yarp.json`) to `WebUI` for API
  and Swagger routing.
- Registered DevExpress server-side services in `WebUI` to
  enable backend rendering for `DxPdfViewer`.
- Migrated services, models, options, and data files to
  `WebUI.Client` with updated namespaces.
- Merged static files (JS, CSS, configuration) from
  `ReceiverUI/wwwroot` to `WebUI/wwwroot`.
- Retained `ReceiverUI` project for rollback safety.

This migration resolves the issue where the DevExpress
`DxPdfViewer` failed to render PDFs in a pure Blazor
WebAssembly environment due to missing server-side rendering
services.
2026-06-15 10:41:53 +02:00
207992d95a fix(WebUI.Client.Services): resolve doc comment icons 2026-06-15 10:27:51 +02:00
d6bafc64a6 Fix BOM issue in using directives in Report.cs and ReportsFactory.cs
The `using DevExpress.XtraReports.UI;` directive was modified in
both `Report.cs` and `ReportsFactory.cs` due to the addition of a
non-visible character (likely a Byte Order Mark or BOM) at the
beginning of the line. This change does not affect functionality
but may resolve potential issues with tools or version control
systems sensitive to such characters.
2026-06-15 10:18:31 +02:00
3090711892 resolve comment icons 2026-06-15 10:13:22 +02:00
9dbd8f7952 remove Home.razor 2026-06-15 09:59:42 +02:00
48a41f2987 refactor(wwwroot): move docs, fonts, fake-data, images and js files.
- update appsettings.json to fix
2026-06-15 09:59:18 +02:00
36 changed files with 837 additions and 1390 deletions

View File

@@ -1,72 +1,107 @@
# EnvelopeGenerator — AI Context Reference
## Purpose
Digital document signing system with **unified Blazor WASM frontend** for both Senders and Receivers. Senders create envelopes and place signature fields. Receivers view PDFs, sign documents, export stamped PDFs.
Digital document signing system with **unified Blazor Auto (Server+WASM hybrid) frontend** for both Senders and Receivers. Senders create envelopes and place signature fields. Receivers view PDFs, sign documents, export stamped PDFs.
**Primary Libraries:** DevExpress + PDF.js (PSPDFKit removed)
---
## Migration Notice
**EnvelopeGenerator.ReceiverUI ? EnvelopeGenerator.WebUI Migration**
The project has been migrated from pure Blazor WebAssembly (`ReceiverUI`) to **Blazor Auto (Server+WASM hybrid)** architecture (`WebUI`) to resolve DevExpress `DxPdfViewer` compatibility issues.
**Reason:** DevExpress `DxPdfViewer` requires backend server-side rendering services that are NOT available in pure WebAssembly projects.
**New Structure:**
- **WebUI** (Server project): Hosts server-side components, YARP proxy, DevExpress backend services
- **WebUI.Client** (WASM project): Client-side components, business logic, services
**Migration Details:** See `MIGRATION_CONTEXT.md`
---
## Deployment Architecture
**Two Presentation Projects (Both Required):**
1. **EnvelopeGenerator.API** (ASP.NET Core Web API)
- Runs independently (development & production)
- **YARP Reverse Proxy** configured via `yarp.json`
- Proxies requests to:
- `EnvelopeGenerator.ReceiverUI` (Blazor WASM)
- External Auth.API service
- Serves as single entry point for all requests
- Backend services for document management, authentication, signature endpoints
- Serves as API endpoint for WebUI
2. **EnvelopeGenerator.ReceiverUI** (Blazor WebAssembly)
- Runs on separate host/port
- Accessed **only through API proxy** (not directly)
- Serves static files (HTML, JS, CSS, WASM)
2. **EnvelopeGenerator.WebUI** (Blazor Auto - Server+WASM Hybrid)
- **Server Project (`EnvelopeGenerator.WebUI`):**
- **YARP Reverse Proxy** configured via `yarp.json`
- Proxies `/api/*` requests to `API:8088`
- Hosts server-side components (`@rendermode InteractiveServer`)
- DevExpress server-side services (DxPdfViewer backend)
- **Client Project (`EnvelopeGenerator.WebUI.Client`):**
- Client-side components (`@rendermode InteractiveWebAssembly`)
- Business logic services (AuthService, DocumentService, etc.)
- WASM runtime
**Request Flow:**
```
Client ? API:8088 (YARP Proxy) ? ReceiverUI:52936 (Blazor WASM)
? Auth.API:9090 (External Auth Service)
Client ? WebUI:XXXX (Blazor Auto)
?? Server-side Pages (DxPdfViewer)
?? Client-side Pages (WASM)
?? YARP Proxy: /api/* ? API:8088
```
**Configuration:** `EnvelopeGenerator.API/yarp.json`
**Configuration:** `EnvelopeGenerator.WebUI/yarp.json`
---
## ReceiverUI Route Structure
## WebUI Route Structure
### Root Route
| Route | File | Purpose |
|---|---|---|
| `/` | `Index.razor` | Application entry point (landing page). |
| Route | File | Location | Render Mode |
|---|---|---|---|
| `/` | `Index.razor` | `WebUI.Client/Pages/` | `@rendermode InteractiveWebAssembly` |
### Sender Routes
| Route | File | Purpose |
|---|---|---|
| `/sender/login` | `LoginSenderPage.razor` | Username/password authentication |
| `/sender` | `EnvelopeSenderPage.razor` | Sender dashboard (envelope list) |
| Route | File | Location | Render Mode |
|---|---|---|---|
| `/sender/login` | `LoginSenderPage.razor` | `WebUI.Client/Pages/` | `@rendermode InteractiveWebAssembly` |
| `/sender` | `EnvelopeSenderPage.razor` | `WebUI.Client/Pages/` | `@rendermode InteractiveWebAssembly` |
### Receiver Routes
| Route | File | Purpose |
|---|---|---|
| `/envelope/login/{EnvelopeKey}` | `LoginReceiverPage.razor` | Access code authentication for specific envelope |
| `/envelope/{EnvelopeKey}` | `EnvelopeReceiverPage.razor` | View & sign envelope (PDF.js viewer) |
### Receiver Routes (PDF Viewers)
| Route | File | Location | Render Mode |
|---|---|---|---|
| `/envelope/login/{EnvelopeKey}` | `LoginReceiverPage.razor` | `WebUI.Client/Pages/` | `@rendermode InteractiveWebAssembly` |
| `/envelope/{EnvelopeKey}` | `EnvelopeReceiverPage.razor` | `WebUI/Components/Pages/` | `@rendermode InteractiveServer` |
| `/envelope/DxPdfViewer` | `EnvelopeReceiverPage_DxPdfViewer.razor` | `WebUI/Components/Pages/` | `@rendermode InteractiveServer` |
| `/envelope/{EnvelopeKey}/DxReportViewer` | `EnvelopeReceiverPage_DxReportViewer.razor` | `WebUI/Components/Pages/` | `@rendermode InteractiveServer` |
| `/envelope/Embed` | `EnvelopeReceiverPage_embed.razor` | `WebUI/Components/Pages/` | `@rendermode InteractiveServer` |
**Multi-Envelope Support:** Receivers can login to multiple envelopes simultaneously (per-envelope cookie authentication).
**Render Mode Strategy:**
- **Client-side Pages (WASM):** Login, Sender dashboard, Index (no DevExpress backend required)
- **Server-side Pages (Server):** PDF viewers (DevExpress DxPdfViewer requires backend)
---
## Architecture Evolution
### Old Architecture (Deprecated)
### Old Architecture (Deprecated v1)
- **Sender UI:** `EnvelopeGenerator.Web` (Razor Pages + PSPDFKit)
- **Receiver UI:** `EnvelopeGenerator.ReceiverUI` (Blazor WASM + PDF.js)
- **Receiver UI:** Separate project
- **Backend:** `EnvelopeGenerator.API`
### Current Architecture
- **Unified Frontend:** `EnvelopeGenerator.ReceiverUI` (Blazor WASM)**Both Senders & Receivers**
- **Backend:** `EnvelopeGenerator.API`**Both Senders & Receivers**
### Intermediate Architecture (Deprecated v2)
- **Unified Frontend:** `EnvelopeGenerator.ReceiverUI` (Pure Blazor WASM)
- **Backend:** `EnvelopeGenerator.API`
- **Issue:** DevExpress `DxPdfViewer` displayed blank screen (no backend services in WASM)
### Current Architecture (Active)
- **Frontend:** `EnvelopeGenerator.WebUI` (Blazor Auto - Server+WASM Hybrid)
- **WebUI** (Server): Server-side components, YARP proxy, DevExpress backend
- **WebUI.Client** (WASM): Client-side components, services, business logic
- **Backend:** `EnvelopeGenerator.API`
- **Libraries:** DevExpress + PDF.js
- **PSPDFKit:** **REMOVED**
@@ -77,12 +112,14 @@ Client ? API:8088 (YARP Proxy) ? ReceiverUI:52936 (Blazor WASM)
| Project | Target | Purpose |
|---|---|---|
| `EnvelopeGenerator.API` | net8.0 | ASP.NET Core Web API. Backend for **both Senders & Receivers**. Auth, PDF serving, signature endpoints. |
| `EnvelopeGenerator.ReceiverUI` | net8.0 WASM | **Unified Blazor WebAssembly Frontend**. UI for **both Senders & Receivers**. YARP proxy to API. |
| `EnvelopeGenerator.WebUI` | net8.0 | **Blazor Auto Server Project**. YARP proxy, server-side components, DevExpress backend services. |
| `EnvelopeGenerator.WebUI.Client` | net8.0 WASM | **Blazor Auto Client Project**. Client-side components, services, business logic. |
| `EnvelopeGenerator.ReceiverUI` | net8.0 WASM | **DEPRECATED.** Pure Blazor WASM (migrated to WebUI). |
| `EnvelopeGenerator.Web` | net7/8/9 | **DEPRECATED.** Legacy Razor Pages (Sender UI). No longer used. |
| `EnvelopeGenerator.Application` | multi | MediatR CQRS handlers. Business logic. |
| `EnvelopeGenerator.Domain` | multi | Domain models, constants, interfaces. |
| `EnvelopeGenerator.Infrastructure` | multi | EF Core repos, DB context. |
| `EnvelopeGenerator.PdfEditor` | multi | iText7 utilities (NOT used in ReceiverUI). |
| `EnvelopeGenerator.PdfEditor` | multi | iText7 utilities (NOT used in WebUI). |
| `EnvelopeGenerator.DependencyInjection` | multi | DI registration helpers. |
| **VB.NET projects** (Service/Form/BBTests) | net462 | **Legacy. Do NOT touch.** |
@@ -90,18 +127,31 @@ Client ? API:8088 (YARP Proxy) ? ReceiverUI:52936 (Blazor WASM)
## Key Files & Routes
| File | Route/Purpose |
### Client-Side Pages (WebUI.Client)
| File | Route | Purpose |
|---|---|---|
| `WebUI.Client/Pages/Index.razor` | `/` | Application entry point (landing page). |
| `WebUI.Client/Pages/EnvelopeSenderPage.razor` | `/sender` | Sender dashboard (envelope list). |
| `WebUI.Client/Pages/LoginSenderPage.razor` | `/sender/login` | Sender username/password auth. |
| `WebUI.Client/Pages/LoginReceiverPage.razor` | `/envelope/login/{EnvelopeKey}` | Receiver access code auth. |
### Server-Side Pages (WebUI)
| File | Route | Purpose |
|---|---|---|
| `WebUI/Components/Pages/EnvelopeReceiverPage.razor` | `/envelope/{key}` | Receiver PDF viewer & signing (PDF.js). |
| `WebUI/Components/Pages/EnvelopeReceiverPage_DxPdfViewer.razor` | `/envelope/DxPdfViewer` | DevExpress PDF Viewer (test page). |
| `WebUI/Components/Pages/EnvelopeReceiverPage_DxReportViewer.razor` | `/envelope/{key}/DxReportViewer` | DevExpress Report Viewer. |
| `WebUI/Components/Pages/EnvelopeReceiverPage_embed.razor` | `/envelope/Embed` | Embedded PDF viewer (iframe). |
### Services & Assets
| File | Purpose |
|---|---|
| `ReceiverUI/Pages/Index.razor` | `/` — Application entry point (landing page). |
| `ReceiverUI/Pages/EnvelopeSenderPage.razor` | `/sender` — Sender dashboard (envelope list). |
| `ReceiverUI/Pages/EnvelopeReceiverPage.razor` | `/envelope/{key}` — Receiver PDF viewer & signing. |
| `ReceiverUI/Pages/LoginSenderPage.razor` | `/sender/login` — Sender username/password auth. |
| `ReceiverUI/Pages/LoginReceiverPage.razor` | `/envelope/login/{EnvelopeKey}` — Receiver access code auth. |
| `ReceiverUI/wwwroot/js/pdf-viewer.js` | PDF.js wrapper (zoom, pagination, thumbnails). |
| `ReceiverUI/wwwroot/js/receiver-signature.js` | Signature pad (draw/type/image). |
| `ReceiverUI/wwwroot/css/envelope-viewer.css` | EnvelopeViewer styles. |
| `ReceiverUI/Services/AuthService.cs` | Receiver + Sender authentication. |
| `ReceiverUI/Services/SignatureCacheService.cs` | Signature caching (Redis/SQL). |
| `WebUI.Client/Services/AuthService.cs` | Receiver + Sender authentication. |
| `WebUI.Client/Services/SignatureCacheService.cs` | Signature caching (Redis/SQL). |
| `WebUI.Client/Services/DocumentService.cs` | PDF document retrieval. |
| `WebUI/wwwroot/js/pdf-viewer.js` | PDF.js wrapper (zoom, pagination, thumbnails). |
| `WebUI/wwwroot/js/receiver-signature.js` | Signature pad (draw/type/image). |
| `WebUI/wwwroot/css/envelope-viewer.css` | EnvelopeViewer styles. |
| `API/Controllers/CacheController.cs` | Signature cache endpoints. |
---
@@ -138,8 +188,8 @@ Client ? API:8088 (YARP Proxy) ? ReceiverUI:52936 (Blazor WASM)
## EnvelopeReceiver — PDF.js Viewer & Signing
**Route:** `/envelope/{EnvelopeKey}`
**Tech:** PDF.js 3.11.174 + Blazor WASM + configurable quality
**File:** `ReceiverUI/Pages/EnvelopeReceiverPage.razor`
**Tech:** PDF.js 3.11.174 + Blazor Server (`@rendermode InteractiveServer`) + configurable quality
**File:** `WebUI/Components/Pages/EnvelopeReceiverPage.razor`
### Key Features
1. HiDPI/Retina support (4x quality)
@@ -150,7 +200,7 @@ Client ? API:8088 (YARP Proxy) ? ReceiverUI:52936 (Blazor WASM)
6. Responsive (desktop/mobile)
### Configuration
**File:** `ReceiverUI/wwwroot/appsettings.json`
**File:** `WebUI/wwwroot/appsettings.json`
```json
{
@@ -164,7 +214,7 @@ Client ? API:8088 (YARP Proxy) ? ReceiverUI:52936 (Blazor WASM)
```
### JavaScript API
**File:** `ReceiverUI/wwwroot/js/pdf-viewer.js`
**File:** `WebUI/wwwroot/js/pdf-viewer.js`
```javascript
window.pdfViewer = {
@@ -211,7 +261,7 @@ window.pdfViewer = {
- Session state: `_capturedSignature` (lost on refresh)
### Data Model
**File:** `ReceiverUI/Models/SignatureCaptureDto.cs`
**File:** `WebUI.Client/Models/SignatureCaptureDto.cs`
```csharp
public sealed record SignatureCaptureDto {
@@ -250,7 +300,7 @@ signature:91751687-8ae6-4777-bf5f-b8846085e62e:{envelopeKey}
```
### Service
**File:** `ReceiverUI/Services/SignatureCacheService.cs`
**File:** `WebUI.Client/Services/SignatureCacheService.cs`
```csharp
public class SignatureCacheService {
@@ -267,11 +317,11 @@ public class SignatureCacheService {
## Sender Login
**Route:** `/sender/login`
**File:** `ReceiverUI/Pages/LoginSenderPage.razor`
**File:** `WebUI.Client/Pages/LoginSenderPage.razor`
**Tech:** Bootstrap 5 + DevExpress Blazing Berry theme
### AuthService Extension
**File:** `ReceiverUI/Services/AuthService.cs`
**File:** `WebUI.Client/Services/AuthService.cs`
```csharp
public enum SenderLoginResult { Success, InvalidCredentials, Error }
@@ -316,7 +366,7 @@ public async Task<SenderLoginResult> LoginSenderAsync(string username, string pa
## Receiver Login
**Route:** `/envelope/login/{EnvelopeKey}`
**File:** `ReceiverUI/Pages/LoginReceiverPage.razor`
**File:** `WebUI.Client/Pages/LoginReceiverPage.razor`
**Multi-Envelope Support:** Cookies are stored per-envelope (e.g., `AuthTokenSignFLOWReceiver.{envelopeKey}`), allowing simultaneous authentication for multiple envelopes in the same browser session.
@@ -344,7 +394,7 @@ public async Task<EnvelopeLoginResult> LoginEnvelopeReceiverAsync(string key, st
---
## NuGet Packages (ReceiverUI)
## NuGet Packages (WebUI.Client)
| Package | Version | Purpose |
|---|---|---|
@@ -376,7 +426,8 @@ public async Task<EnvelopeLoginResult> LoginEnvelopeReceiverAsync(string key, st
### Deprecated Projects
**DO NOT USE:**
- `EnvelopeGenerator.Web` (Razor Pages) — Replaced by unified ReceiverUI
- `EnvelopeGenerator.ReceiverUI` (Pure Blazor WASM) — Migrated to WebUI (DevExpress compatibility issue)
- `EnvelopeGenerator.Web` (Razor Pages) — Replaced by unified WebUI
- PSPDFKit — Removed, use PDF.js + DevExpress instead
### Legacy Projects (VB.NET)
@@ -411,14 +462,16 @@ Proves database uses INCHES natively.
2. Prefer simplicity over complexity
3. Use `appsettings.json` for configuration
4. Keep consistent with existing design (Bootstrap 5 + Blazing Berry)
5. **Unified frontend:** ReceiverUI serves both Senders and Receivers
5. **Unified frontend:** WebUI serves both Senders and Receivers
6. **Render mode:** Client-side (WASM) for login/dashboard, Server-side for PDF viewers
### When debugging:
1. **Coordinates:** Always check unit system (inches/points/pixels)
2. **Authentication:** Check cookie name/domain/SameSite
3. **Cache:** Check Redis/SQL connection + key format
4. **Frontend confusion:** Only use ReceiverUI (Web is deprecated)
4. **Frontend confusion:** Only use WebUI (ReceiverUI/Web are deprecated)
5. **Blank DxPdfViewer:** Ensure page has `@rendermode InteractiveServer`
---
**Last Updated:** Session 19 (Razor file naming convention + Index route proxy)
**Last Updated:** 2025-01-27 (ReceiverUI ? WebUI migration complete)

View File

@@ -0,0 +1,21 @@
<nav class="navbar header-navbar p-0">
<button class="navbar-toggler bg-primary d-block" @onclick="OnToggleClick">
<span class="navbar-toggler-icon"></span>
</button>
<div class="ms-3 fw-bold title pe-4">EnvelopeGenerator.ReceiverUI</div>
</nav>
@code {
[Parameter] public bool ToggleOn { get; set; }
[Parameter] public EventCallback<bool> ToggleOnChanged { get; set; }
async Task OnToggleClick() => await Toggle();
async Task Toggle(bool? value = null) {
var newValue = value ?? !ToggleOn;
if(ToggleOn != newValue) {
ToggleOn = newValue;
await ToggleOnChanged.InvokeAsync(ToggleOn);
}
}
}

View File

@@ -1,9 +1,27 @@
@inherits LayoutComponentBase
@using EnvelopeGenerator.WebUI.Client.Services;
@inherits LayoutComponentBase
@Body
<div id="blazor-error-ui">
An unhandled error has occurred.
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
<div class="page">
<main>
<article class="content">
@Body
</article>
</main>
<footer class="receiver-footer">
<span>&copy; SignFlow 2023-2024 <a href="https://digitaldata.works" target="_blank" rel="noopener">Digital Data GmbH</a></span>
<span class="receiver-footer__sep">&#124;</span>
<a href="docs/privacy-policy.de-DE.html" target="_blank" rel="noopener">Datenschutz</a>
</footer>
</div>
@code {
[Inject] HttpClient Http { get; set; }
List<string> RequiredFonts = new() {
"opensans.ttf"
};
protected async override Task OnInitializedAsync() {
await FontLoader.LoadFonts(Http, RequiredFonts);
await base.OnInitializedAsync();
}
}

View File

@@ -0,0 +1,46 @@
<div class="top-row ps-3 navbar navbar-dark">
<div class="container-fluid">
<a class="navbar-brand" href="">EnvelopeGenerator.ReceiverUI</a>
<button title="Navigation menu" class="navbar-toggler" @onclick="ToggleNavMenu">
<span class="navbar-toggler-icon"></span>
</button>
</div>
</div>
<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
<nav class="flex-column">
@*
<div class="nav-item px-3">
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
<span class="oi oi-home" aria-hidden="true"></span> Home
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="documentviewer">
<span class="oi oi-plus" aria-hidden="true"></span> Document Viewer (JS-Based)
</NavLink>
</div>
*@
<div class="nav-item px-3">
<NavLink class="nav-link" href="receiver">
<span class="oi oi-plus" aria-hidden="true"></span> Empfänger-UI
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="sender">
<span class="oi oi-plus" aria-hidden="true"></span> Umschlag-UI
</NavLink>
</div>
</nav>
</div>
@code {
private bool collapseNavMenu = true;
private string NavMenuCssClass => collapseNavMenu ? "collapse" : null;
private void ToggleNavMenu()
{
collapseNavMenu = !collapseNavMenu;
}
}

View File

@@ -1,4 +1,4 @@
namespace EnvelopeGenerator.WebUI.Client.Models.Constants
namespace EnvelopeGenerator.WebUI.Client.Models.Constants
{
public enum SenderAppType
{

View File

@@ -1,4 +1,4 @@
namespace EnvelopeGenerator.WebUI.Client.Models.Constants;
namespace EnvelopeGenerator.WebUI.Client.Models.Constants;
/// <summary>
/// Represents the unit of measurement for coordinate values in signature positioning.
@@ -16,13 +16,13 @@ public enum UnitOfLength
/// <br/>
/// <b>Evidence:</b> VB.NET code directly assigns database values to annotation properties without conversion:
/// <code>
/// oAnnotation.Left = CSng(pElement.X) ' Direct assignment ? INCHES
/// oAnnotation.Left = CSng(pElement.X) ' Direct assignment INCHES
/// oAnnotation.Top = CSng(pElement.Y)
/// </code>
/// <b>Standard Page Dimensions:</b>
/// <list type="bullet">
/// <item>A4: 8.27" × 11.69" (210mm × 297mm)</item>
/// <item>Letter: 8.5" × 11"</item>
/// <item>A4: 8.27" × 11.69" (210mm × 297mm)</item>
/// <item>Letter: 8.5" × 11"</item>
/// </list>
/// </remarks>
Inch = 0,
@@ -41,18 +41,18 @@ public enum UnitOfLength
/// points = inches * 72.0
/// inches = points / 72.0
/// </code>
/// <b>Important:</b> Point ? Pixel!
/// <b>Important:</b> Point Pixel!
/// <list type="bullet">
/// <item><b>Point (pt):</b> Device-independent unit (always 1/72 inch)</item>
/// <item><b>Pixel (px):</b> Device-dependent unit (varies with screen DPI)</item>
/// <item>At 72 DPI: 1 point = 1 pixel (coincidence)</item>
/// <item>At 96 DPI: 1 point ? 1.33 pixels</item>
/// <item>At 300 DPI: 1 point ? 4.17 pixels</item>
/// <item>At 96 DPI: 1 point 1.33 pixels</item>
/// <item>At 300 DPI: 1 point 4.17 pixels</item>
/// </list>
/// <b>Standard Page Dimensions (in points):</b>
/// <list type="bullet">
/// <item>A4: 595 × 842 points (8.27" × 11.69" × 72)</item>
/// <item>Letter: 612 × 792 points (8.5" × 11" × 72)</item>
/// <item>A4: 595 × 842 points (8.27" × 11.69" × 72)</item>
/// <item>Letter: 612 × 792 points (8.5" × 11" × 72)</item>
/// </list>
/// <b>Usage in EnvelopeGenerator:</b>
/// <list type="bullet">

View File

@@ -102,4 +102,4 @@ public record ReceiverClientDto
public string? Signature { get; init; }
public DateTime AddedWhen { get; init; }
public DateTime? TfaRegDeadline { get; init; }
}
}

View File

@@ -31,6 +31,7 @@ public sealed record SignatureCaptureDto
/// Full name of the signer (first and last name).
/// <br/>
/// <b>Required:</b> Yes (validated in popup)
/// <br/>
/// <b>Display:</b> Bold text in applied signature block
/// <br/>
/// <b>Example:</b> "Max Mustermann"

View File

@@ -1,4 +1,4 @@
using EnvelopeGenerator.WebUI.Client.Models.Constants;
using EnvelopeGenerator.WebUI.Client.Models.Constants;
namespace EnvelopeGenerator.WebUI.Client.Models;
@@ -58,8 +58,8 @@ public class SignatureDto
// LegacyFormApp uses GdPicture14 with INCHES
return _unitOfLength switch
{
UnitOfLength.Inch => 1.0, // No conversion needed: INCHES ? INCHES
UnitOfLength.Point => 72.0, // INCHES ? PDF Points: 1 inch = 72 points (PDF standard, NOT pixels!)
UnitOfLength.Inch => 1.0, // No conversion needed: INCHES INCHES
UnitOfLength.Point => 72.0, // INCHES PDF Points: 1 inch = 72 points (PDF standard, NOT pixels!)
_ => throw new InvalidOperationException(
$"Unknown UnitOfLength: {_unitOfLength}. Expected '{nameof(UnitOfLength.Inch)}' or '{nameof(UnitOfLength.Point)}'.")
};
@@ -98,4 +98,4 @@ public static class SignatureDtoExtensions
return signatures;
}
}
}

View File

@@ -2,7 +2,7 @@ namespace EnvelopeGenerator.WebUI.Client.Options;
public class ApiOptions
{
public const string SectionName = "ApiOptions";
public const string SectionName = "Api";
public string BaseUrl { get; set; } = string.Empty;

View File

@@ -2,7 +2,7 @@ namespace EnvelopeGenerator.WebUI.Client.Options;
public class PdfViewerOptions
{
public const string SectionName = "PdfViewerOptions";
public const string SectionName = "PdfViewer";
/// <summary>
/// Base scale for thumbnail rendering (0.2 - 1.5 recommended)

View File

@@ -1,7 +0,0 @@
@page "/"
<PageTitle>Home</PageTitle>
<h1>Hello, world!</h1>
Welcome to your new app.

View File

@@ -1,4 +1,4 @@
using DevExpress.XtraReports.UI;
using DevExpress.XtraReports.UI;
namespace EnvelopeGenerator.WebUI.Client.PredefinedReports {
public class Report : XtraReport {
private TopMarginBand topMarginBand1;

View File

@@ -1,4 +1,4 @@
using DevExpress.XtraReports.UI;
using DevExpress.XtraReports.UI;
namespace EnvelopeGenerator.WebUI.Client.PredefinedReports {
public static class ReportsFactory

View File

@@ -12,7 +12,7 @@ namespace EnvelopeGenerator.WebUI.Client.Services;
/// During development, <c>BaseUrl</c> is empty so the request resolves to the
/// YARP-proxied route on the same origin, which currently serves
/// <c>fake-data/annotations.json</c>. To switch to real data, update the
/// YARP route in <c>yarp.json</c> no code change required.
/// YARP route in <c>yarp.json</c> no code change required.
/// </summary>
[Obsolete("Use SignatureService.")]
public class AnnotationService(HttpClient http, IOptions<ApiOptions> apiOptions)

View File

@@ -1,40 +1,39 @@
using DevExpress.DataAccess.Json;
using DevExpress.DataAccess.Json;
using DevExpress.DataAccess.Web;
using DevExpress.DataAccess.Wizard.Services;
namespace EnvelopeGenerator.WebUI.Client.Services
namespace EnvelopeGenerator.WebUI.Client.Services;
public class CustomDataSourceWizardJsonDataConnectionStorage : IDataSourceWizardJsonConnectionStorage
{
public class CustomDataSourceWizardJsonDataConnectionStorage : IDataSourceWizardJsonConnectionStorage
{
public static JsonDataConnection GetDefaultConnection() {
var uriJsonSource = new UriJsonSource() {
Uri = new Uri(@"https://raw.githubusercontent.com/DevExpress-Examples/DataSources/master/JSON/customers.json"),
};
return new JsonDataConnection(uriJsonSource) { StoreConnectionNameOnly = true, Name = "NWindProductsJson" };
}
public static List<JsonDataConnection> GetConnections() {
var connections = new List<JsonDataConnection> {
GetDefaultConnection()
};
return connections;
}
bool IJsonConnectionStorageService.CanSaveConnection => false;
bool IJsonConnectionStorageService.ContainsConnection(string connectionName) {
return GetConnections().Any(x => x.Name == connectionName);
}
IEnumerable<JsonDataConnection> IJsonConnectionStorageService.GetConnections() {
return GetConnections();
}
JsonDataConnection IJsonDataConnectionProviderService.GetJsonDataConnection(string name) {
var connection = GetConnections().FirstOrDefault(x => x.Name == name);
if(connection == null)
throw new InvalidOperationException();
return connection;
}
void IJsonConnectionStorageService.SaveConnection(string connectionName, JsonDataConnection connection, bool saveCredentials) { }
public static JsonDataConnection GetDefaultConnection() {
var uriJsonSource = new UriJsonSource() {
Uri = new Uri(@"https://raw.githubusercontent.com/DevExpress-Examples/DataSources/master/JSON/customers.json"),
};
return new JsonDataConnection(uriJsonSource) { StoreConnectionNameOnly = true, Name = "NWindProductsJson" };
}
}
public static List<JsonDataConnection> GetConnections() {
var connections = new List<JsonDataConnection> {
GetDefaultConnection()
};
return connections;
}
bool IJsonConnectionStorageService.CanSaveConnection => false;
bool IJsonConnectionStorageService.ContainsConnection(string connectionName) {
return GetConnections().Any(x => x.Name == connectionName);
}
IEnumerable<JsonDataConnection> IJsonConnectionStorageService.GetConnections() {
return GetConnections();
}
JsonDataConnection IJsonDataConnectionProviderService.GetJsonDataConnection(string name) {
var connection = GetConnections().FirstOrDefault(x => x.Name == name);
if(connection == null)
throw new InvalidOperationException();
return connection;
}
void IJsonConnectionStorageService.SaveConnection(string connectionName, JsonDataConnection connection, bool saveCredentials) { }
}

View File

@@ -1,24 +1,23 @@
using DevExpress.DataAccess.Json;
using DevExpress.DataAccess.Json;
using DevExpress.DataAccess.Web;
namespace EnvelopeGenerator.WebUI.Client.Services
{
public class CustomJsonDataConnectionProviderFactory : IJsonDataConnectionProviderFactory {
public IJsonDataConnectionProviderService Create() {
return new WebDocumentViewerJsonDataConnectionProvider(CustomDataSourceWizardJsonDataConnectionStorage.GetConnections());
}
}
namespace EnvelopeGenerator.WebUI.Client.Services;
public class WebDocumentViewerJsonDataConnectionProvider : IJsonDataConnectionProviderService
{
readonly List<JsonDataConnection> jsonDataConnections;
public WebDocumentViewerJsonDataConnectionProvider(List<JsonDataConnection> jsonDataConnections) {
this.jsonDataConnections = jsonDataConnections;
}
public JsonDataConnection GetJsonDataConnection(string name) {
var connection = jsonDataConnections.FirstOrDefault(x => x.Name == name);
if(connection == null)
throw new InvalidOperationException();
return connection;
}
public class CustomJsonDataConnectionProviderFactory : IJsonDataConnectionProviderFactory {
public IJsonDataConnectionProviderService Create() {
return new WebDocumentViewerJsonDataConnectionProvider(CustomDataSourceWizardJsonDataConnectionStorage.GetConnections());
}
}
public class WebDocumentViewerJsonDataConnectionProvider : IJsonDataConnectionProviderService
{
readonly List<JsonDataConnection> jsonDataConnections;
public WebDocumentViewerJsonDataConnectionProvider(List<JsonDataConnection> jsonDataConnections) {
this.jsonDataConnections = jsonDataConnections;
}
public JsonDataConnection GetJsonDataConnection(string name) {
var connection = jsonDataConnections.FirstOrDefault(x => x.Name == name);
if(connection == null)
throw new InvalidOperationException();
return connection;
}
}

View File

@@ -1,21 +1,20 @@
using DevExpress.XtraReports.UI;
using DevExpress.XtraReports.UI;
using DevExpress.XtraReports.Services;
using EnvelopeGenerator.WebUI.Client.PredefinedReports;
namespace EnvelopeGenerator.WebUI.Client.Services
{
public class CustomReportProvider : IReportProviderAsync {
private readonly InMemoryReportStorageWebExtension reportStorage;
namespace EnvelopeGenerator.WebUI.Client.Services;
public CustomReportProvider(InMemoryReportStorageWebExtension reportStorage) {
this.reportStorage = reportStorage;
}
public class CustomReportProvider : IReportProviderAsync {
private readonly InMemoryReportStorageWebExtension reportStorage;
public Task<XtraReport> GetReportAsync(string id, ReportProviderContext context) {
if(reportStorage.TryGetReport(id, out var savedReport))
return Task.FromResult(savedReport);
return Task.FromResult(ReportsFactory.GetReport(id));
}
public CustomReportProvider(InMemoryReportStorageWebExtension reportStorage) {
this.reportStorage = reportStorage;
}
}
public Task<XtraReport> GetReportAsync(string id, ReportProviderContext context) {
if(reportStorage.TryGetReport(id, out var savedReport))
return Task.FromResult(savedReport);
return Task.FromResult(ReportsFactory.GetReport(id));
}
}

View File

@@ -1,12 +1,12 @@
using DevExpress.Drawing;
using DevExpress.Drawing;
namespace EnvelopeGenerator.WebUI.Client.Services {
public static class FontLoader {
public async static Task LoadFonts(HttpClient httpClient, List<string> fontNames) {
foreach(var fontName in fontNames) {
var fontBytes = await httpClient.GetByteArrayAsync($"fonts/{fontName}");
DXFontRepository.Instance.AddFont(fontBytes);
}
namespace EnvelopeGenerator.WebUI.Client.Services;
public static class FontLoader {
public async static Task LoadFonts(HttpClient httpClient, List<string> fontNames) {
foreach(var fontName in fontNames) {
var fontBytes = await httpClient.GetByteArrayAsync($"fonts/{fontName}");
DXFontRepository.Instance.AddFont(fontBytes);
}
}
}
}

View File

@@ -1,9 +1,9 @@
using DevExpress.DataAccess.Web;
using DevExpress.DataAccess.Web;
namespace EnvelopeGenerator.WebUI.Client.Services {
public class ObjectDataSourceWizardCustomTypeProvider : IObjectDataSourceWizardTypeProvider {
public IEnumerable<Type> GetAvailableTypes(string context) {
return new[] { typeof(Data.DataItemList) };
}
namespace EnvelopeGenerator.WebUI.Client.Services;
public class ObjectDataSourceWizardCustomTypeProvider : IObjectDataSourceWizardTypeProvider {
public IEnumerable<Type> GetAvailableTypes(string context) {
return new[] { typeof(Data.DataItemList) };
}
}
}

View File

@@ -5,13 +5,18 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<base href="/" />
<link rel="stylesheet" href="app.css" />
<link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" />
<link rel="stylesheet" href="css/app.css" />
<link rel="stylesheet" href="css/envelope-viewer.css" />
<link rel="stylesheet" href="EnvelopeGenerator.WebUI.styles.css" />
<HeadOutlet @rendermode="InteractiveAuto" />
</head>
<body>
<Routes @rendermode="InteractiveAuto" />
<script src="_content/DevExpress.Blazor.Resources/js/preload-script.js"></script>
<script src="js/typed.umd.js"></script>
<script src="js/receiver-signature.js?v=9"></script>
<script src="_framework/blazor.web.js"></script>
</body>

View File

@@ -16,6 +16,12 @@
</ItemGroup>
<ItemGroup>
<Content Update="wwwroot\docs\privacy-policy.en-US.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="wwwroot\docs\privacy-policy.fr-FR.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="yarp.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>

View File

@@ -11,12 +11,19 @@ builder.Services.AddRazorComponents()
.AddInteractiveServerComponents()
.AddInteractiveWebAssemblyComponents();
// HttpClient for server-side components (e.g., MainLayout with FontLoader)
builder.Services.AddScoped<HttpClient>(sp => {
var httpClientFactory = sp.GetRequiredService<IHttpClientFactory>();
return httpClientFactory.CreateClient();
});
builder.Services.AddHttpClient();
// YARP Reverse Proxy
builder.Services.AddReverseProxy()
.LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));
// DevExpress Server-Side Services (CRITICAL for DxPdfViewer)
builder.Services.AddDevExpressBlazor(configure => configure.BootstrapVersion = BootstrapVersion.v5);
builder.Services.AddDevExpressBlazor();
builder.Services.AddDevExpressServerSideBlazorPdfViewer();
// Configuration Options

View File

@@ -1,29 +0,0 @@
h1:focus {
outline: none;
}
.valid.modified:not([type=checkbox]) {
outline: 1px solid #26b050;
}
.invalid {
outline: 1px solid #e50000;
}
.validation-message {
color: #e50000;
}
.blazor-error-boundary {
background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
padding: 1rem 1rem 1rem 3.7rem;
color: white;
}
.blazor-error-boundary::after {
content: "An error has occurred."
}
.darker-border-checkbox.form-check-input {
border-color: #929292;
}

View File

@@ -1,8 +1,9 @@
{
"ApiOptions": {
"BaseUrl": ""
"Api": {
"BaseUrl": "",
"UsePredefinedReports": false
},
"PdfViewerOptions": {
"PdfViewer": {
"ThumbnailBaseScale": 0.75,
"ThumbnailEnableHiDPI": true,
"ThumbnailMaxDPR": 2.0,

View File

@@ -0,0 +1,167 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Datenschutzinformation für das Fernsignatursystem: signFLOW</title>
<link rel="stylesheet" href="../css/privacy-policy.min.css">
</head>
<body>
<header>
<h1>Datenschutzinformation für das Fernsignatursystem signFLOW</h1>
<p><strong>Stand:</strong> 18.11.2025</p>
</header>
<section>
<h2>1. Allgemeine Informationen</h2>
<p>In der heutigen schnelllebigen und zunehmend digitalen Welt, sind personenbezogene Daten eine wichtige
Ressource. Ihre Daten sind wertvoll und müssen daher mit der durch diverse Gesetze und Vorschriften (DSGVO,
TDDDG, ...) gebotenen Sorgfalt behandelt werden.</p>
<p>Als Anbieter von lokalen Lösungen (OnPremise) setzt der Hersteller des signFLOWs, die Digital Data GmbH,
einen klaren Schwerpunkt auf Datenschutz und Datensicherheit. Für Sie bedeutet dies, dass nur die nötigsten
Daten erhoben und gespeichert werden (Datensparsamkeit bzw. Datenminimierung). Außerdem kommen bei der
Verarbeitung aktuelle und als sicher geltende Technologien zum Einsatz.</p>
<p><strong>Kontaktdaten des Herstellers:</strong></p>
<address>
Digital Data GmbH<br>
Ludwig-Rinn-Straße 16<br>
35452 Heuchelheim<br>
<a href="https://digitaldata.works">https://digitaldata.works</a><br>
<a href="mailto:info-flow@digitaldata.works">info-flow@digitaldata.works</a><br>
Telefon: 0049 641 202360<br>
</address>
<p><strong>Kontakt zum Datenschutzbeauftragten:</strong> <a
href="mailto:privacy-flow@digitaldata.works">privacy-flow@digitaldata.works</a></p>
</section>
<section>
<h2>2. Verantwortliche Stelle der Datenverarbeitung</h2>
<p>Ihre Daten werden vertrauensvoll verarbeitet von:</p>
<address>
Digital Data GmbH<br>
Ludwig-Rinn-Straße 16<br>
35452 Heuchelheim<br>
<a href="https://digitaldata.works">https://digitaldata.works</a><br>
<a href="mailto:info-flow@digitaldata.works">info-flow@digitaldata.works</a><br>
Telefon: 0049 641 202360<br>
</address>
<p><strong>Kontakt zu unserer Datenschutzbeauftragten:</strong> <a
href="mailto:privacy-flow@digitaldata.works">privacy-flow@digitaldata.works</a></p>
</section>
<section>
<h2>3. Datenerhebung</h2>
<h3>3.1 Die folgenden Kategorien personenbezogener Daten werden verarbeitet</h3>
<ul>
<li>Namen: Benutzername, Vor- und Zunamen sowie Ihre digitale Unterschrift</li>
<li>Kontaktdaten: Telefonnummer, Mobilfunknummer und E-Mail-Adresse</li>
<li>Technische Daten: IP-Adresse, Zeitpunkt des Zugriffs oder Zugriffsversuchs</li>
</ul>
<h3>3.2 Ursprung der personenbezogenen Daten</h3>
<p>Sie haben die unter 3.1 stehenden Daten vorab an Ihren Geschäftspartner (die verantwortliche Stelle)
übermittelt. Diese Übermittlung kann mündlich per Telefon, im persönlichen Kontakt, per E-Mail oder per
Kontaktformular geschehen sein.</p>
<p>Ihre digitale Signatur übermitteln Sie eigenständig, bei der Unterzeichnung eines Dokuments.</p>
<h3>3.3 Aufbewahrungsfristen / Speicherungsdauer</h3>
<ul>
<li>Die automatische E-Mail Korrespondenz wird für 6 Jahre aufbewahrt.</li>
<li>Unterzeichnete Verträge werden für die Dauer ihrer Laufzeit + 10 Jahre aufbewahrt.</li>
<li>Der technische Vorgang wird in der signFLOW Softwarelösung je nach Dokumentart bzw. Vertragsart
unbegrenzt aufbewahrt.</li>
</ul>
<p>Ihre personenbezogenen Daten werden aber grundsätzlich anonymisiert, wenn:</p>
<ul>
<li>Der Vertrag ausgelaufen ist und die gesetzliche Aufbewahrungszeit vorbei ist.</li>
<li>Der Vertrag von Ihnen abgelehnt wurde bzw. nie unterschrieben wurde.</li>
</ul>
<p>Die gesetzlichen Grundlagen dieser Aufbewahrungsfristen bieten unter anderen:</p>
<ul>
<li>Handelsgesetzbuch (HGB)</li>
<li>Abgabenordnung (AO)</li>
<li>Grundsätze zur ordnungsgemäßen Führung und Aufbewahrung von Büchern, Aufzeichnungen und Unterlagen in
elektronischer Form sowie zum Datenzugriff (GoBD)</li>
</ul>
<p>
Abhängig von der spezifischen Dokumentart, kann die Aufbewahrungsfrist variieren. Außerdem können sich
Zeiten
verlängern, falls Unregelmäßigkeiten auftreten. Z.B.: eine rechtliche Auseinandersetzung droht oder aktuell
läuft.
</p>
<h3>3.4 Verarbeitungszweck</h3>
<p>Die unter 3.1 definierten personenbezogenen Daten werden verarbeitet um:</p>
<ul>
<li>Den technisch notwendigen Prozess abzubilden bzw. anbieten zu können.</li>
<li>Damit Sie als Endbenutzer in der Lage sind, ein zu unterzeichnendes Dokument, digital zu unterschreiben,
ist die Feststellung der Identität des Antragstellers, Antragsprüfung und -abwicklung, Abrechnung sowie
die Wahrung der Dokumentationspflichten notwendig.</li>
</ul>
<p>In Einzelfällen werden Daten zur Fehlerbehebung insbesondere bei Supportanfragen von der IT-Abteilung
gesondert behandelt oder ggf. an den Hersteller weitergegeben.</p>
<p>Im Zuge der Maßnahmen zur Sicherstellung der Informationssicherheit, insbesondere zur Identifizierung und
Abwehr von Angriffen, sowie zur Durchführung interner und externer Audits, der Exportkontrolle und der
Prüfung von Sanktionslisten, erfolgt ebenfalls eine Datenverarbeitung. Bei Anfragen gemäß §8 Abs. 2 VDG
werden die entsprechenden Informationen an die zuständigen Stellen übermittelt.</p>
<h3>3.5 Rechtmäßigkeit der Verarbeitung</h3>
<p>Ihre Daten werden auf Grundlage einer sich anbahnenden oder bereits vorhandenen Geschäftsbeziehung erhoben.
</p>
<p>Die rechtliche Grundlage für die Übermittlung an zuständige Stellen bildet §8 Abs. 2 VDG. Anfragen von
Betroffenen werden gemäß den Artikeln 12 bis 23 der DSGVO sowie den Paragraphen 32 bis 37 des BDSG
bearbeitet.</p>
<h3>3.6 Berechtigte Interessen</h3>
<p>Ein berechtigtes Interesse der verantwortlichen Stelle gemäß Art. 6 Abs. 1 lit. f DSGVO besteht in den
folgenden Fällen:</p>
<p>Es werden Maßnahmen zur Informationssicherheit ergriffen, die sowohl präventive technische als auch
organisatorische Maßnahmen sowie die Behandlung von Vorfällen umfassen. Ziel ist es, potenzielle Schäden für
das Unternehmen, die von der Datenverarbeitung betroffenen Personen und die Nutzer der Vertrauensdienste zu
bewerten und zu vermeiden.</p>
<h3>3.7 Erforderlichkeit der Daten</h3>
<p>Die erhobenen Daten stellen das Minimum der nötigen Daten zwecks digitaler Unterschrift dar. Ohne die unter
3.1 genannten Daten, kann der Dienst nicht betrieben werden.</p>
<p>Besonders wichtig ist die Angabe einer Mobilfunknummer oder einer deutschen Festnetznummer, da diese für die
Authentifizierung und die Signaturauslösung als zweiter Faktor verwendet wird. Ohne diesen
Sicherheitsmechanismus kann der Dienst leider nicht bereitgestellt werden.</p>
<h3>3.8 Weitergabe von Daten</h3>
<p>Eine systemische Übermittlung von Daten findet nicht statt.</p>
<p>Daten werden nur in Ausnahmefällen, zwecks Supportleistungen, an den Hersteller weitergegeben. Mit der
Hersteller besteht ein gültiger Auftragsdatenverarbeitungsvertrag (AVV), welcher die Sicherheit und
Integrität des Umgangs mit Ihren Daten gewährleistet.</p>
</section>
<section>
<h2>4. Verwendung von Cookies</h2>
<p>
Beim Besuch bestimmter Seiten kommen temporäre Cookies zum Einsatz, die für die technische Bereitstellung
der Dienste notwendig sind. Diese sogenannten Session-Cookies enthalten keine personenbezogenen Daten und
werden nach Beendigung der Sitzung automatisch gelöscht. Methoden wie Java-Applets oder Active-X-Controls,
die das Nutzerverhalten nachvollziehbar machen könnten, werden nicht verwendet.
</p>
</section>
<section>
<h2>5. Betroffenenrechte</h2>
<p>
Wenn Sie Fragen zu Ihren Daten haben oder eine Berichtigung, Löschung oder Einschränkung der Verarbeitung
wünschen, senden Sie bitte Ihre Anfrage per Post oder E-Mail an die oben angegebene Adresse. Dies gilt auch,
wenn Sie gemäß Art. 21 DSGVO Widerspruch gegen die Verarbeitung einlegen möchten oder eine Anfrage zur
Datenübertragbarkeit haben.
</p>
<p>
Bei Fragen oder Beschwerden zu einem Verfahren können Sie sich ebenfalls über die genannten
Kontaktmöglichkeiten an uns wenden. Sollten Sie darüber hinaus einen Grund zur Beschwerde haben, haben Sie
die Möglichkeit, sich an unsere Aufsichtsbehörde zu wenden. Welche Aufsichtsbehörde für Sie zuständig ist,
erfahren Sie hier:
<a href="https://www.bfdi.bund.de/DE/Service/Anschriften/Laender/Laender-node.html">Laender-node.html</a>
</p>
</section>
</body>
</html>

View File

@@ -0,0 +1,158 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Data Protection Information for the Remote Signature System signFLOW</title>
<link rel="stylesheet" href="css/privacy-policy.min.css">
</head>
<body>
<header>
<h1>Data Protection Information for the Remote Signature System: signFLOW</h1>
<p><strong>As of:</strong> 18.11.2025</p>
</header>
<section>
<h2>1. General Information</h2>
<p>In today's fast-paced and increasingly digital world, personal data is an important resource. Your data is
valuable and must therefore be handled with the care required by various laws and regulations (GDPR, TDDDG,
...).</p>
<p>As a provider of local solutions (OnPremise), the manufacturer of signFLOW, Digital Data GmbH, places a clear
focus on data protection and data security. For you, this means that only the necessary data is collected
and stored (data minimization). Furthermore, current and secure technologies are used in processing.</p>
<p><strong>Contact details of the manufacturer:</strong></p>
<address>
Digital Data GmbH<br>
Ludwig-Rinn-Straße 16<br>
35452 Heuchelheim<br>
<a href="https://digitaldata.works">https://digitaldata.works</a><br>
<a href="mailto:info-flow@digitaldata.works">info-flow@digitaldata.works</a><br>
Phone: 0049 641 202360<br>
</address>
<p><strong>Contact the Data Protection Officer:</strong> <a
href="mailto:privacy-flow@digitaldata.works">privacy-flow@digitaldata.works</a></p>
</section>
<section>
<h2>2. Responsible Entity for Data Processing</h2>
<p>Your data is processed with confidence by:</p>
<address>
Digital Data GmbH<br>
Ludwig-Rinn-Straße 16<br>
35452 Heuchelheim<br>
<a href="https://digitaldata.works">https://digitaldata.works</a><br>
<a href="mailto:info-flow@digitaldata.works">info-flow@digitaldata.works</a><br>
Phone: 0049 641 202360<br>
</address>
<p><strong>Contact our Data Protection Officer:</strong> <a
href="mailto:privacy-flow@digitaldata.works">privacy-flow@digitaldata.works</a></p>
</section>
<section>
<h2>3. Data Collection</h2>
<h3>3.1 The following categories of personal data are processed</h3>
<ul>
<li>Names: Username, first and last names as well as your digital signature</li>
<li>Contact details: Phone number, mobile phone number, and email address</li>
<li>Technical data: IP address, time of access, or access attempts</li>
</ul>
<h3>3.2 Source of the personal data</h3>
<p>You have previously provided the data mentioned under 3.1 to your business partner (the responsible entity).
This transmission may have occurred verbally over the phone, in personal contact, via email, or via a
contact form.</p>
<p>You transmit your digital signature independently when signing a document.</p>
<h3>3.3 Retention periods / Storage duration</h3>
<ul>
<li>Automatic email correspondence is stored for 6 years.</li>
<li>Signed contracts are retained for the duration of their term + 10 years.</li>
<li>The technical process is stored in the signFLOW software solution indefinitely, depending on the
document or contract type.</li>
</ul>
<p>Your personal data will generally be anonymized when:</p>
<ul>
<li>The contract has expired, and the statutory retention period is over.</li>
<li>The contract was rejected by you or never signed.</li>
</ul>
<p>The legal basis for these retention periods includes:</p>
<ul>
<li>Commercial Code (HGB)</li>
<li>Tax Code (AO)</li>
<li>Principles for the Proper Keeping and Retention of Books, Records, and Documents in Electronic Form and
for Data Access (GoBD)</li>
</ul>
<p>
Depending on the specific type of document, the retention period may vary. Additionally, the periods may be
extended in case of irregularities, such as a pending or ongoing legal dispute.
</p>
<h3>3.4 Purpose of processing</h3>
<p>The personal data defined under 3.1 is processed to:</p>
<ul>
<li>Support or provide the technically necessary process.</li>
<li>Enable you, as the end user, to sign a document digitally. This requires the identification of the
applicant, application verification and processing, billing, and compliance with documentation
requirements.</li>
</ul>
<p>In individual cases, data is processed separately by the IT department, particularly in response to support
requests, or possibly forwarded to the manufacturer for further processing.</p>
<p>Data processing also occurs to ensure information security, especially for the identification and prevention
of attacks, and for conducting internal and external audits, export controls, and sanctions list checks.
Information may also be transmitted to the relevant authorities in accordance with Section 8 (2) VDG.</p>
<h3>3.5 Legality of processing</h3>
<p>Your data is collected based on an impending or already existing business relationship.</p>
<p>The legal basis for the transmission to competent authorities is Section 8 (2) VDG. Requests from data
subjects are processed in accordance with Articles 12 to 23 of the GDPR and Sections 32 to 37 of the Federal
Data Protection Act (BDSG).</p>
<h3>3.6 Legitimate interests</h3>
<p>A legitimate interest of the responsible entity in accordance with Article 6 (1) (f) GDPR exists in the
following cases:</p>
<p>Measures are taken for information security, which include both preventive technical and organizational
measures as well as incident handling. The aim is to assess and avoid potential harm to the company, the
individuals affected by data processing, and the users of trust services.</p>
<h3>3.7 Necessity of data</h3>
<p>The collected data represents the minimum necessary for the digital signature. Without the data mentioned
under 3.1, the service cannot be operated.</p>
<p>It is particularly important to provide a mobile number or a German landline number, as this is used for
authentication and signature triggering as a second factor. Without this security mechanism, the service
cannot be provided.</p>
<h3>3.8 Data transfer</h3>
<p>Systematic data transmission does not take place.</p>
<p>Data is only forwarded to the manufacturer for support services in exceptional cases. A valid data processing
agreement (DPA) exists with the manufacturer, which ensures the security and integrity of the handling of
your data.</p>
</section>
<section>
<h2>4. Use of Cookies</h2>
<p>
When visiting certain pages, temporary cookies are used, which are necessary for the technical provision of
the services. These so-called session cookies do not contain any personal data and are automatically deleted
after the session ends. Methods such as Java applets or Active-X controls that could track user behavior are
not used.
</p>
</section>
<section>
<h2>5. Rights of Affected Persons</h2>
<p>
If you have questions about your data or wish to request correction, deletion, or restriction of processing,
please send your request by mail or email to the address provided above. This also applies if you wish to
object to the processing in accordance with Article 21 GDPR or request data portability.
</p>
<p>
If you have questions or complaints about a procedure, you can also contact us using the contact details
provided. If you have further grounds for complaint, you can contact our supervisory authority. You can find
out which supervisory authority is responsible for you here:
<a href="https://www.bfdi.bund.de/DE/Service/Anschriften/Laender/Laender-node.html">Laender-node.html</a>
</p>
</section>
</body>
</html>

View File

@@ -0,0 +1,126 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Informations sur la protection des données pour le système de signature à distance signFLOW</title>
<link rel="stylesheet" href="css/privacy-policy.min.css">
</head>
<body>
<header>
<h1>Informations sur la protection des données pour le système de signature à distance : signFLOW</h1>
<p><strong>À jour au :</strong> 18.11.2025</p>
</header>
<section>
<h2>1. Informations générales</h2>
<p>Dans le monde actuel, rapide et de plus en plus numérique, les données personnelles sont une ressource précieuse. Vos données sont importantes et doivent donc être traitées avec le soin requis par les différentes lois et réglementations (RGPD, TDDDG, ...).</p>
<p>En tant que fournisseur de solutions locales (OnPremise), le fabricant de signFLOW, Digital Data GmbH, accorde une attention particulière à la protection et à la sécurité des données. Pour vous, cela signifie que seules les données nécessaires sont collectées et stockées (minimisation des données). De plus, des technologies actuelles et sécurisées sont utilisées lors du traitement.</p>
<p><strong>Coordonnées du fabricant :</strong></p>
<address>
Digital Data GmbH<br>
Ludwig-Rinn-Straße 16<br>
35452 Heuchelheim<br>
<a href="https://digitaldata.works">https://digitaldata.works</a><br>
<a href="mailto:info-flow@digitaldata.works">info-flow@digitaldata.works</a><br>
Téléphone : 0049 641 202360<br>
</address>
<p><strong>Contactez le délégué à la protection des données :</strong> <a href="mailto:privacy-flow@digitaldata.works">privacy-flow@digitaldata.works</a></p>
</section>
<section>
<h2>2. Responsable du traitement des données</h2>
<p>Vos données sont traitées en toute confiance par :</p>
<address>
Digital Data GmbH<br>
Ludwig-Rinn-Straße 16<br>
35452 Heuchelheim<br>
<a href="https://digitaldata.works">https://digitaldata.works</a><br>
<a href="mailto:info-flow@digitaldata.works">info-flow@digitaldata.works</a><br>
Téléphone : 0049 641 202360<br>
</address>
<p><strong>Contactez notre délégué à la protection des données :</strong> <a href="mailto:privacy-flow@digitaldata.works">privacy-flow@digitaldata.works</a></p>
</section>
<section>
<h2>3. Collecte des données</h2>
<h3>3.1 Catégories de données personnelles traitées</h3>
<ul>
<li>Noms : nom dutilisateur, prénom et nom ainsi que votre signature numérique</li>
<li>Coordonnées : numéro de téléphone, numéro de mobile et adresse e-mail</li>
<li>Données techniques : adresse IP, heure daccès ou tentatives daccès</li>
</ul>
<h3>3.2 Source des données personnelles</h3>
<p>Vous avez précédemment fourni les données mentionnées en 3.1 à votre partenaire commercial (le responsable du traitement). Cette transmission a pu se faire verbalement par téléphone, lors dun contact personnel, par e-mail ou via un formulaire de contact.</p>
<p>Vous transmettez votre signature numérique de manière autonome lors de la signature dun document.</p>
<h3>3.3 Durée de conservation / stockage</h3>
<ul>
<li>La correspondance par e-mail automatique est conservée pendant 6 ans.</li>
<li>Les contrats signés sont conservés pendant leur durée + 10 ans.</li>
<li>Le processus technique est stocké dans la solution logicielle signFLOW indéfiniment, selon le type de document ou de contrat.</li>
</ul>
<p>Vos données personnelles seront généralement anonymisées lorsque :</p>
<ul>
<li>Le contrat a expiré et la période légale de conservation est terminée.</li>
<li>Le contrat a été refusé par vous ou jamais signé.</li>
</ul>
<p>La base légale de ces périodes de conservation comprend :</p>
<ul>
<li>Code de commerce (HGB)</li>
<li>Code fiscal (AO)</li>
<li>Principes de tenue correcte et de conservation des livres, registres et documents sous forme électronique et daccès aux données (GoBD)</li>
</ul>
<p>
Selon le type spécifique de document, la période de conservation peut varier. De plus, ces périodes peuvent être prolongées en cas dirrégularités, telles quun litige en cours ou imminent.
</p>
<h3>3.4 Finalité du traitement</h3>
<p>Les données personnelles définies en 3.1 sont traitées pour :</p>
<ul>
<li>Supporter ou fournir le processus techniquement nécessaire.</li>
<li>Permettre à lutilisateur final de signer un document numériquement. Cela nécessite lidentification du demandeur, la vérification et le traitement de la demande, la facturation et le respect des obligations documentaires.</li>
</ul>
<p>Dans certains cas, les données sont traitées séparément par le département informatique, notamment en réponse à des demandes de support, ou éventuellement transmises au fabricant pour un traitement supplémentaire.</p>
<p>Le traitement des données intervient également pour garantir la sécurité de linformation, notamment pour identifier et prévenir les attaques, et pour effectuer des audits internes et externes, le contrôle des exportations et les vérifications des listes de sanctions. Les informations peuvent également être transmises aux autorités compétentes conformément à larticle 8 (2) VDG.</p>
<h3>3.5 Licéité du traitement</h3>
<p>Vos données sont collectées sur la base dune relation commerciale existante ou imminente.</p>
<p>La base légale pour la transmission aux autorités compétentes est larticle 8 (2) VDG. Les demandes des personnes concernées sont traitées conformément aux articles 12 à 23 du RGPD et aux articles 32 à 37 de la loi fédérale sur la protection des données (BDSG).</p>
<h3>3.6 Intérêts légitimes</h3>
<p>Un intérêt légitime du responsable du traitement conformément à larticle 6 (1) (f) RGPD existe dans les cas suivants :</p>
<p>Des mesures sont prises pour la sécurité de linformation, incluant à la fois des mesures techniques et organisationnelles préventives ainsi que la gestion des incidents. Lobjectif est dévaluer et déviter les dommages potentiels pour lentreprise, les personnes concernées par le traitement des données et les utilisateurs des services de confiance.</p>
<h3>3.7 Nécessité des données</h3>
<p>Les données collectées représentent le minimum nécessaire pour la signature numérique. Sans les données mentionnées en 3.1, le service ne peut pas être opéré.</p>
<p>Il est particulièrement important de fournir un numéro de mobile ou un numéro fixe allemand, car cela est utilisé pour lauthentification et le déclenchement de la signature comme second facteur. Sans ce mécanisme de sécurité, le service ne peut être fourni.</p>
<h3>3.8 Transfert de données</h3>
<p>Aucun transfert systématique de données na lieu.</p>
<p>Les données ne sont transmises au fabricant pour le support que dans des cas exceptionnels. Un accord de traitement des données (DPA) valide existe avec le fabricant, garantissant la sécurité et lintégrité de la gestion de vos données.</p>
</section>
<section>
<h2>4. Utilisation des cookies</h2>
<p>
Lors de la visite de certaines pages, des cookies temporaires sont utilisés, nécessaires à la fourniture technique des services. Ces cookies de session ne contiennent aucune donnée personnelle et sont automatiquement supprimés à la fin de la session. Aucune méthode telle que les applets Java ou les contrôles Active-X, pouvant suivre le comportement de lutilisateur, nest utilisée.
</p>
</section>
<section>
<h2>5. Droits des personnes concernées</h2>
<p>
Si vous avez des questions concernant vos données ou souhaitez demander une correction, suppression ou limitation du traitement, veuillez envoyer votre demande par courrier ou e-mail à ladresse indiquée ci-dessus. Cela sapplique également si vous souhaitez vous opposer au traitement conformément à larticle 21 RGPD ou demander la portabilité des données.
</p>
<p>
Si vous avez des questions ou des plaintes concernant une procédure, vous pouvez également nous contacter via les coordonnées fournies. Si vous avez dautres motifs de plainte, vous pouvez contacter notre autorité de contrôle. Vous pouvez vérifier quelle autorité de contrôle est compétente pour vous ici :
<a href="https://www.bfdi.bund.de/DE/Service/Anschriften/Laender/Laender-node.html">Laender-node.html</a>
</p>
</section>
</body>
</html>

View File

@@ -0,0 +1,20 @@
[
{
"id": 1,
"page": 1,
"x": 390.0,
"y": 380.0
},
{
"id": 2,
"page": 2,
"x": 390.0,
"y": 680.0
},
{
"id": 3,
"page": 3,
"x": 390.0,
"y": 980.0
}
]

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="_x31_" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 62 62" style="enable-background:new 0 0 62 62;" xml:space="preserve">
<style type="text/css">
.st0{fill-rule:evenodd;clip-rule:evenodd;}
</style>
<path id="_x32_" class="st0" d="M31,61C14.4,61,1,47.6,1,31S14.4,1,31,1s30,13.4,30,30S47.6,61,31,61z M31,5C16.6,5,5,16.6,5,31
s11.6,26,26,26s26-11.6,26-26S45.4,5,31,5z M52,34.5L49.5,37L46,33.5L42.5,37L40,34.5l3.5-3.5L40,27.5l2.5-2.5l3.5,3.5l3.5-3.5
l2.5,2.5L48.5,31L52,34.5z M43,44c0,1.1-0.9,2-2,2H21c-1.1,0-2-0.9-2-2s0.9-2,2-2h20C42.1,42,43,42.9,43,44z M19.5,37L16,33.5
L12.5,37L10,34.5l3.5-3.5L10,27.5l2.5-2.5l3.5,3.5l3.5-3.5l2.5,2.5L18.5,31l3.5,3.5L19.5,37z"/>
</svg>

After

Width:  |  Height:  |  Size: 893 B

View File

@@ -481,7 +481,7 @@ window.pdfViewer = {
// Signature button functionality
signatureButtons: [],
appliedSignatures: [], // Track which signatures have been applied (ID list)
appliedSignatureElements: [], // ? NEW: Track applied signature DOM elements
appliedSignatureElements: [], // NEW: Track applied signature DOM elements
_lastViewedSignatureId: null, // Track last viewed signature for navigation
/**
@@ -743,7 +743,7 @@ window.pdfViewer = {
// CRITICAL: Filter OUT already applied signatures!
const appliedIds = new Set(this.appliedSignatures.map(s => s.id));
const pageSignatures = signatures.filter(sig =>
sig.page === currentPageNum && !appliedIds.has(sig.id) // ?? Skip applied ones!
sig.page === currentPageNum && !appliedIds.has(sig.id) // ? Skip applied ones!
);
if (pageSignatures.length === 0) {
@@ -777,7 +777,7 @@ window.pdfViewer = {
const xPx = (sig.x * this.scale);
const yPx = (sig.y * this.scale);
// ? FIXED: Scale button size proportionally with zoom
// FIXED: Scale button size proportionally with zoom
const baseScale = 1.5; // Reference scale (initial load)
const scaleFactor = this.scale / baseScale;
const baseWidth = 150;
@@ -799,8 +799,8 @@ window.pdfViewer = {
button.style.position = 'absolute';
button.style.left = `${xPx}px`;
button.style.top = `${yPx}px`;
button.style.width = `${scaledWidth}px`; // ? Scaled
button.style.height = `${scaledHeight}px`; // ? Scaled
button.style.width = `${scaledWidth}px`; // Scaled
button.style.height = `${scaledHeight}px`; // Scaled
button.style.backgroundColor = '#4F46E5';
button.style.color = 'white';
button.style.border = 'none';
@@ -820,14 +820,14 @@ window.pdfViewer = {
// Add text
const textDiv = document.createElement('div');
textDiv.textContent = 'Unterschreiben';
textDiv.style.fontSize = `${scaledFontSize}px`; // ? Scaled
textDiv.style.fontSize = `${scaledFontSize}px`; // Scaled
textDiv.style.fontWeight = '700';
// Add SVG icon
const svgNS = 'http://www.w3.org/2000/svg';
const svg = document.createElementNS(svgNS, 'svg');
svg.setAttribute('width', scaledIconSize); // ? Scaled
svg.setAttribute('height', scaledIconSize); // ? Scaled
svg.setAttribute('width', scaledIconSize); // Scaled
svg.setAttribute('height', scaledIconSize); // Scaled
svg.setAttribute('viewBox', '0 8 32 36');
svg.setAttribute('fill', 'none');
svg.style.filter = 'drop-shadow(0 1px 2px rgba(0,0,0,0.2))';
@@ -929,14 +929,14 @@ window.pdfViewer = {
const signature = this._allSignatures.find(s => s.id === signatureId);
if (signature) {
// ? Position calculation (same as renderSignatureButtons)
// Position calculation (same as renderSignatureButtons)
const xPx = signature.x * this.scale;
const yPx = signature.y * this.scale;
container.style.left = `${xPx}px`;
container.style.top = `${yPx}px`;
// ? FIXED: Apply comprehensive scaling using helper method
// FIXED: Apply comprehensive scaling using helper method
this.scaleAppliedSignature(container, this.scale);
// Show/hide based on current page
@@ -958,7 +958,7 @@ window.pdfViewer = {
});
this.signatureButtons = [];
// ? FIXED: Update applied signatures (position + scaling)
// FIXED: Update applied signatures (position + scaling)
this.appliedSignatureElements.forEach(container => {
const signatureId = parseInt(container.getAttribute('data-signature-id'));
const signature = this._allSignatures?.find(s => s.id === signatureId);
@@ -1034,7 +1034,7 @@ window.pdfViewer = {
signatureContainer.className = 'applied-signature';
signatureContainer.setAttribute('data-signature-id', signatureId);
// ? FIXED: Store base values for scaling
// FIXED: Store base values for scaling
signatureContainer.setAttribute('data-base-width', '230');
signatureContainer.setAttribute('data-base-padding', '12');
signatureContainer.setAttribute('data-base-font-size', '9');
@@ -1072,8 +1072,8 @@ window.pdfViewer = {
// Text information container
const infoContainer = document.createElement('div');
infoContainer.className = 'signature-info-text'; // ? Add class (for querySelector)
infoContainer.setAttribute('data-base-font-size', '9'); // ? Store base font size
infoContainer.className = 'signature-info-text'; // Add class (for querySelector)
infoContainer.setAttribute('data-base-font-size', '9'); // Store base font size
infoContainer.style.fontSize = '9px';
infoContainer.style.lineHeight = '1.4';
infoContainer.style.color = '#495057';
@@ -1107,10 +1107,10 @@ window.pdfViewer = {
// Add to signature layer
signatureLayer.appendChild(signatureContainer);
// ? FIXED: Track applied signature element for zoom updates
// FIXED: Track applied signature element for zoom updates
this.appliedSignatureElements.push(signatureContainer);
// ? FIXED: Apply initial scaling based on current zoom
// FIXED: Apply initial scaling based on current zoom
this.scaleAppliedSignature(signatureContainer, this.scale);
console.log(`Signature #${signatureId} applied successfully`);
@@ -1129,3 +1129,10 @@ window.pdfViewer = {
return div.innerHTML;
}
};

View File

@@ -23,7 +23,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{134D4164-B29
ProjectSection(SolutionItems) = preProject
COPILOT_CONTEXT.md = COPILOT_CONTEXT.md
FORM_APPLICATION_CONTEXT.md = FORM_APPLICATION_CONTEXT.md
MIGRATION_CONTEXT.md = MIGRATION_CONTEXT.md
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{0CBC2432-A561-4440-89BC-671B66A24146}"

File diff suppressed because it is too large Load Diff