Files
EnvelopeGenerator/COPILOT_CONTEXT_EN.md
TekH b6d86aa3eb Refactor documentation for unified architecture
Updated documentation to reflect the transition to a unified Blazor WASM frontend for both Senders and Receivers. Removed references to PSPDFKit and legacy components, emphasizing the use of PDF.js and DevExpress.

Key changes include:
- Revised purpose and architecture sections.
- Updated solution structure to mark `EnvelopeGenerator.Web` as deprecated.
- Enhanced coordinate system explanation with conversion formulas.
- Documented new `EnvelopeViewer` features and signature workflows.
- Added details on signature caching and login flows for Senders and Receivers.
- Expanded "Mistakes History" to highlight lessons learned.
- Added quick reference for debugging and development consistency.

These changes improve clarity, maintainability, and alignment with the current system architecture.
2026-06-11 10:25:44 +02:00

12 KiB
Raw Blame History

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.

Primary Libraries: DevExpress + PDF.js (PSPDFKit removed)


Architecture Evolution

Old Architecture (Deprecated)

  • Sender UI: EnvelopeGenerator.Web (Razor Pages + PSPDFKit)
  • Receiver UI: EnvelopeGenerator.ReceiverUI (Blazor WASM + PDF.js)
  • Backend: EnvelopeGenerator.API

Current Architecture

  • Unified Frontend: EnvelopeGenerator.ReceiverUI (Blazor WASM) — Both Senders & Receivers
  • Backend: EnvelopeGenerator.APIBoth Senders & Receivers
  • Libraries: DevExpress + PDF.js
  • PSPDFKit: REMOVED

Solution Structure

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.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.DependencyInjection multi DI registration helpers.
VB.NET projects (Service/Form/BBTests) net462 Legacy. Do NOT touch.

Key Files & Routes

File Route/Purpose
ReceiverUI/Pages/EnvelopeViewer.razor /envelope/{key} — PDF.js viewer (read-only).
ReceiverUI/Pages/LoginReceiver.razor /login/{EnvelopeKey} — Access code auth.
ReceiverUI/Pages/LoginSender.razor /login — Username/password 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).
API/Controllers/CacheController.cs Signature cache endpoints.

Coordinate System — CRITICAL

Database Format: INCHES (GdPicture14 native)
Origin: Top-left corner
Axes: X right, Y down

Conversion Formulas

From INCHES to Formula Example
DevExpress DX x_DX = x_inches * 100 1.5" ? 150 DX
PDF Points x_pt = x_inches * 72 1.5" ? 108 pt
PDF.js Pixels Normalize ? scale (x_inches / pageWidth) * canvasWidth * scale

A4 Dimensions:

  • Width: 8.27" = 595pt = 827 DX
  • Height: 11.69" = 842pt = 1169 DX

Unit Systems

System Unit Origin Y-Axis
Database (GdPicture14) Inches Top-left Down
PDF.js Pixels Top-left Down
iText7 PDF Points (1/72") Bottom-left Up (flip required)
PSPDFKit Points Top-left REMOVED

EnvelopeViewer — PDF.js Viewer

Route: /envelope/{EnvelopeKey}
Tech: PDF.js 3.11.174 + Blazor WASM + configurable quality
File: ReceiverUI/Pages/EnvelopeViewer.razor

Key Features

  1. HiDPI/Retina support (4x quality)
  2. Configurable quality (appsettings.json)
  3. Unlimited zoom (50%-300%)
  4. Ctrl+Wheel global zoom
  5. Resizable thumbnail sidebar (150-400px, localStorage)
  6. Responsive (desktop/mobile)

Configuration

File: ReceiverUI/wwwroot/appsettings.json

{
  "PdfViewer": {
    "ThumbnailBaseScale": 0.75,
    "ThumbnailEnableHiDPI": true,
    "MainCanvasEnableHiDPI": true,
    "ZoomStepPercentage": 5
  }
}

JavaScript API

File: ReceiverUI/wwwroot/js/pdf-viewer.js

window.pdfViewer = {
    initialize(canvasId, pdfDataUrl, dotNetRef),
    renderPage(num),
    renderSignatureButtons(signatures, pageNum, dotNetRef),
    applySignature(signatureId, dataUrl, fullName, position, place),
    zoomIn(), zoomOut(), dispose()
}

Signature Workflow — EnvelopeViewer

IMPORTANT: iText7 NOT used (GPL license issue). Client-side overlay system only.

Workflow Steps

  1. Page Load:

    • Check SignatureCacheService for cached signature
    • If cached ? skip popup, load signature
    • If not ? show automatic popup (mandatory)
  2. Signature Popup (DxPopup):

    • Cannot close (no X, no ESC, no outside-click)
    • 3 Tabs: Draw (canvas) / Text (font select) / Image (upload)
    • Required: Full name, Place
    • Optional: Position
    • Save ? Store in _capturedSignature, cache via API
  3. Signature Buttons:

    • Render purple "Unterschreiben" buttons at signature field positions
    • Coordinates: INCHES ? POINTS ? Pixels (scaled)
    • File: pdf-viewer.js ? renderSignatureButtons()
  4. Apply Signature (Click "Unterschreiben"):

    • JS: Remove button, create HTML overlay
    • Format: Image + separator + text (Name, Position, Place, Date)
    • NOT stamped on PDF bytes (visual overlay only)
  5. Re-rendering:

    • Zoom/Page change ? recalculate button positions
    • Session state: _capturedSignature (lost on refresh)

Data Model

File: ReceiverUI/Models/SignatureCaptureDto.cs

public sealed record SignatureCaptureDto {
    public required string DataUrl { get; init; }    // base64 PNG
    public required string FullName { get; init; }
    public string Position { get; init; } = "";      // Optional
    public required string Place { get; init; }
}

Signature Caching

Purpose: Persist signature across page refreshes (distributed cache: Redis/SQL)

API Endpoints

Controller: API/Controllers/CacheController.cs

  • POST /api/Cache/SignatureCapture/{envelopeKey} — Save
  • GET /api/Cache/SignatureCapture/{envelopeKey} — Load
  • DELETE /api/Cache/SignatureCapture/{envelopeKey} — Delete

Cache Key Format:

signature:91751687-8ae6-4777-bf5f-b8846085e62e:{envelopeKey}

Configuration: appsettings.json

{
  "Cache": {
    "SignatureCacheExpiration": null  // or "02:00:00" for 2h
  }
}

Service

File: ReceiverUI/Services/SignatureCacheService.cs

public class SignatureCacheService {
    Task SaveSignatureAsync(string envelopeKey, SignatureCaptureDto signature);
    Task<SignatureCaptureDto?> GetSignatureAsync(string envelopeKey);
    Task DeleteSignatureAsync(string envelopeKey);
}

Error Handling: Fire-and-forget saves, graceful degradation on load failure.


Sender Login

Route: /login
File: ReceiverUI/Pages/LoginSender.razor
Tech: Bootstrap 5 + DevExpress Blazing Berry theme

AuthService Extension

File: ReceiverUI/Services/AuthService.cs

public enum SenderLoginResult { Success, InvalidCredentials, Error }

public async Task<SenderLoginResult> LoginSenderAsync(string username, string password) {
    var response = await http.PostAsJsonAsync(
        $"{_api.BaseUrl}/api/auth?cookie=true",
        new { username, password });
    
    return response.StatusCode switch {
        HttpStatusCode.OK => SenderLoginResult.Success,
        HttpStatusCode.Unauthorized => SenderLoginResult.InvalidCredentials,
        _ => SenderLoginResult.Error
    };
}

API Integration

Endpoint: POST /api/auth?cookie=true

Request:

{ "username": "TekH", "password": "***" }

Response:

  • 200 OK ? Cookie set, redirect to /
  • 401 Unauthorized ? Show error: "Ungültige Anmeldedaten"
  • Other ? Show error: "Serverfehler"

Cookie: HTTP-only, Secure (HTTPS), SameSite=Strict

UI Flow

  1. User enters username + password
  2. Click "Anmelden" or press Enter
  3. Call AuthService.LoginSenderAsync()
  4. Success ? Navigation.NavigateTo("/", forceLoad: true)
  5. Error ? Display alert

Receiver Login

Route: /login/{EnvelopeKey}
File: ReceiverUI/Pages/LoginReceiver.razor

AuthService Method

public enum EnvelopeLoginResult { Success, InvalidCode, NotFound, Error }

public async Task<EnvelopeLoginResult> LoginEnvelopeReceiverAsync(string key, string accessCode) {
    var form = new MultipartFormDataContent();
    form.Add(new StringContent(accessCode), "AccessCode");
    
    var response = await http.PostAsync(
        $"{_api.BaseUrl}/api/Auth/envelope-receiver/{Uri.EscapeDataString(key)}", form);
    
    return response.StatusCode switch {
        HttpStatusCode.OK => EnvelopeLoginResult.Success,
        HttpStatusCode.Unauthorized => EnvelopeLoginResult.InvalidCode,
        HttpStatusCode.NotFound => EnvelopeLoginResult.NotFound,
        _ => EnvelopeLoginResult.Error
    };
}

Success: Redirect to /envelope/{key}


NuGet Packages (ReceiverUI)

Package Version Purpose
DevExpress.Blazor.* 25.2.3 UI components (grids, popups, etc.)
SkiaSharp.* 3.119.1 WASM rendering
itext 8.0.5 NOT USED (GPL license)

External CDN:

  • PDF.js 3.11.174: https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js

Mistakes History — Do NOT Repeat

Mistake Why Wrong
Using iText7 in EnvelopeViewer GPL license issue. Use overlay system instead.
Using PSPDFKit Removed from architecture. Use PDF.js + DevExpress.
Hardcoded quality values in PDF.js Use appsettings.json for configurability.
Complex toolbar layouts User wants simplicity. Keep horizontal layout.
Over-designed UI (gradients/badges) User prefers simple text labels.
Ignoring "revert" instructions Revert HTML structure, not just CSS.
BottomMarginBand for signatures Repeats on every page. Use DetailBand.
imageY = (page-1) * 1169 + ann.Y Inflates DetailBand. Calculate per-page.

Development Notes

Deprecated Projects

DO NOT USE:

  • EnvelopeGenerator.Web (Razor Pages) — Replaced by unified ReceiverUI
  • PSPDFKit — Removed, use PDF.js + DevExpress instead

Legacy Projects (VB.NET)

DO NOT TOUCH: EnvelopeGenerator.Service, EnvelopeGenerator.Form, EnvelopeGenerator.BBTests

Signature Coordinate Evidence

File: EnvelopeGenerator.Form/frmFieldEditor.vb (VB.NET)

Private Const SIGNATURE_WIDTH As Single = 1.77   ' inches
Private Const SIGNATURE_HEIGHT As Single = 1.96  ' inches

Sub LoadAnnotation(pElement As Signature, ...)
    oAnnotation.Left = CSng(pElement.X)    ' Direct INCHES assignment
    oAnnotation.Top = CSng(pElement.Y)
End Sub

Proves database uses INCHES natively.


Quick Reference

When working with coordinates:

  1. Database ? UI: INCHES × 72 = PDF Points
  2. UI ? Display: Points × scale = Pixels
  3. iText7 stamping: Flip Y-axis (top-down ? bottom-up)

When adding features:

  1. Check Mistakes History first
  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

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)

Last Updated: Session 17 (Architecture unification documentation)