Add documentation for receiver PDF viewer context

Added `RECEIVER_PDF_VIEWER_CONTEXT.md` to the `src` project, documenting the current implementation and behavior of the receiver-side PDF viewing and signing experience in the `EnvelopeGenerator` project.

The document outlines the use of `PDF.js` as the current rendering engine, the planned migration to `DxPdfViewer`, and the functional capabilities that must be preserved. Key features include single-page PDF viewing, navigation, zoom, signature overlays, and metadata validation.

This addition ensures clarity for future development and emphasizes the importance of maintaining existing workflows during the migration or other changes.
This commit is contained in:
2026-06-29 10:56:10 +02:00
parent 96a84ba1a5
commit 6ca03a50eb
2 changed files with 582 additions and 0 deletions

View File

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

View File

@@ -0,0 +1,581 @@
# EnvelopeGenerator — Receiver PDF Viewer Context
## Purpose
This document summarizes the active receiver-side PDF viewing and signing experience so that other agents can understand the current implementation quickly without re-reading all related files.
This summary is based on the active host and current implementation in:
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Components/Pages/EnvelopeReceiverPage.razor`
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/wwwroot/js/pdf-viewer.js`
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/wwwroot/js/receiver-signature.js`
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/wwwroot/css/envelope-viewer.css`
---
## Active Page
**Primary receiver page:**
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Components/Pages/EnvelopeReceiverPage.razor`
**Route:**
- `/envelope/{EnvelopeKey}`
**Render mode:**
- `InteractiveServer`
This page is the active receiver PDF viewing and signing UI.
---
## Core Architecture
The receiver experience is built from three layers:
### 1. Blazor page layer
`EnvelopeReceiverPage.razor` is responsible for:
- authorization flow
- loading receiver-specific document data
- loading signature placeholders
- loading and saving cached signature data
- UI state management
- toolbar interactions
- popup interactions
- JS interop orchestration
### 2. PDF rendering layer
The active implementation currently uses `PDF.js` for:
- rendering the active PDF page
- rendering thumbnails
- handling zoom and page navigation state
Referenced assets in the current implementation:
- `https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js`
- `https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf_viewer.min.css`
### Planned target rendering layer
The main migration goal is to replace `PDF.js` in `EnvelopeReceiverPage.razor` with `DxPdfViewer` while preserving the complete receiver signing experience documented in this file.
This means `DxPdfViewer` must become the main document rendering surface without regressing:
- single active page viewing behavior
- page navigation behavior
- zoom behavior
- thumbnail sidebar behavior
- receiver-specific signature placeholder overlays
- applied signature overlays
- signature navigation across pages
- overlay repositioning and resizing after zoom/page changes
### 3. Custom enhancement layer
Extra behavior is implemented through custom JavaScript and CSS:
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/wwwroot/js/pdf-viewer.js`
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/wwwroot/js/receiver-signature.js`
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/wwwroot/css/envelope-viewer.css`
These files extend the base PDF.js experience with receiver-specific features.
---
## Main Functional Capabilities
## 1. Single-page PDF viewing in the main viewer
The page shows one active PDF page at a time in the main canvas.
Main viewer elements:
- `pdf-canvas`
- `pdf-text-layer`
- `pdf-signature-layer`
This means the page is not using a continuous full-document scroll layout in the main area. Instead, it behaves like a single active page viewer with controlled navigation.
---
## 2. Page navigation
The page supports multiple navigation methods:
- previous page button
- next page button
- direct page number input
- thumbnail click navigation
- signature navigation that may jump to another page automatically
Relevant Blazor methods include:
- `PreviousPage()`
- `NextPage()`
- `OnPageInputChanged(...)`
- `GoToPageFromThumbnail(...)`
- `OnPageChangedBySignatureNav(...)`
---
## 3. Zoom support
The page supports zooming in and out for the active PDF page.
Available zoom behavior:
- zoom in button
- zoom out button
- zoom slider
- programmatic scale setting
- fit-to-width helper exists in code
Relevant methods:
- `ZoomIn()`
- `ZoomOut()`
- `SetZoom(int percentage)`
- `OnZoomSliderChanged(...)`
- `FitToWidth()`
- `OnZoomChanged(double scale)`
Current zoom constraints in the page:
- minimum: `50%`
- maximum: `300%`
Important behavior:
- when zoom changes, signature placeholders and applied signature overlays are re-rendered/repositioned
- position and visual size are expected to stay synchronized with the current PDF scale
---
## 4. Thumbnail sidebar
The page has a thumbnail sidebar for document page previews.
Supported sidebar behavior:
- show/hide toggle
- click thumbnail to navigate to page
- highlight active page
- resizable width
- width persistence in `localStorage`
Relevant state:
- `_showThumbnails`
- `_thumbnailWidth`
- `_isResizing`
Relevant methods:
- `ToggleThumbnails()`
- `RenderThumbnailsAsync()`
- `OnSplitterMouseDown(...)`
- `OnSplitterMouseMove(...)`
- `OnSplitterMouseUp()`
Persistence key:
- `envelopeViewer_thumbnailWidth`
Important implementation detail:
- thumbnail rendering is done sequentially with delay to avoid overloading the browser
---
## 5. Signature placeholder buttons on top of the PDF
The page places clickable signature buttons over the PDF for receiver-specific signature fields.
Behavior:
- placeholders are loaded server-side
- placeholders are rendered on the client as overlay elements
- placeholders are shown for the current page
- clicking a placeholder applies the captured signature to that location
Relevant Blazor method:
- `RenderSignatureButtonsAsync()`
Relevant JS interop call:
- `pdfViewer.renderSignatureButtons`
Important note:
- this is an overlay workflow, not direct PDF stamping
- placeholders belong to the current authenticated receiver
---
## 6. Applying a real signature after clicking a placeholder
Once a receiver has a captured signature, clicking a signature placeholder applies the real signature overlay to the PDF viewer.
Applied signature data includes:
- signature image data URL
- signer full name
- signer position
- place
Relevant method:
- `OnSignatureButtonClick(int signatureId)`
Relevant JS interop call:
- `pdfViewer.applySignature`
Important note:
- the active implementation visually places the signature in the viewer layer
- the page is not currently doing final PDF binary stamping in this UI flow
---
## 7. Signature navigation across all signature fields
The toolbar supports fast navigation between signature fields.
Supported behavior:
- go to previous signature
- go to next signature
- maintain current signature index
- show signed / unsigned / total counts
- automatically move between pages if the next signature is on another page
Relevant methods:
- `GoToPreviousSignature()`
- `GoToNextSignature()`
- `OnSignatureNavChanged()`
- `OnPageChangedBySignatureNav(int newPage)`
- `UpdateSignatureCounterAsync()`
Relevant JS interop calls:
- `pdfViewer.goToPreviousSignature`
- `pdfViewer.goToNextSignature`
- `pdfViewer.getSignatureNavState`
Counter state maintained in Blazor:
- `_totalSignatures`
- `_signedSignatures`
- `_unsignedSignatures`
- `_currentSignatureIndex`
---
## 8. Automatic signature/button repositioning and resizing on zoom
A critical current feature is that zoom changes should keep overlay elements visually aligned with the document.
This applies to:
- signature placeholder buttons
- applied signature overlays
Expected behavior:
- position updates when zoom changes
- size updates when zoom changes
- re-render happens after page change and zoom change
This behavior is triggered from methods such as:
- `OnZoomChanged(...)`
- `ZoomIn()`
- `ZoomOut()`
- `OnZoomSliderChanged(...)`
- `NextPage()`
- `PreviousPage()`
- `GoToPageFromThumbnail(...)`
- `OnPageChangedBySignatureNav(...)`
In practice, the page repeatedly calls:
- `RenderSignatureButtonsAsync()`
This is one of the key behaviors other agents must preserve.
---
## 9. Signature capture popup
Signature creation is handled with a `DxPopup`.
Popup capabilities:
- draw signature
- create typed signature
- upload signature image
- capture required signer metadata
- validate required fields before save
Tabs:
- `draw`
- `text`
- `image`
Relevant constants:
- `SignatureTabDraw`
- `SignatureTabText`
- `SignatureTabImage`
Relevant canvas/input IDs:
- `envelope-signature-pad`
- `envelope-typed-signature-pad`
- `envelope-signature-image-input`
- `envelope-image-signature-pad`
---
## 10. Signature capture modes
The current UI supports three signature creation modes.
### Draw mode
Receiver signs directly on a canvas.
Relevant JS usage:
- `receiverSignature.initialize`
- `receiverSignature.clear`
- `receiverSignature.getDataUrl`
- `receiverSignature.loadExistingSignature`
### Text mode
Receiver enters a signature as text and chooses a font.
Relevant JS usage:
- `receiverSignature.initializeTyped`
- `receiverSignature.renderTypedSignature`
- `receiverSignature.clearTyped`
- `receiverSignature.getTypedDataUrl`
### Image mode
Receiver uploads an image of their signature.
Relevant JS usage:
- `receiverSignature.initializeImage`
- `receiverSignature.clearImage`
- `receiverSignature.getImageDataUrl`
---
## 11. Signature metadata requirements
The popup captures extra metadata besides the signature image.
### Required
- full name
- place
### Optional
- position
Relevant bound fields:
- `_signerFullName`
- `_signaturePlace`
- `_signerPosition`
Validation behavior in `SaveSignatureAsync()`:
- full name must not be empty
- place must not be empty
- signature image/data must not be empty
---
## 12. Cached signature reuse
The page attempts to load a previously cached signature for the receiver.
Behavior:
- if cache exists, the popup does not open automatically
- if cache does not exist, the popup opens on initial load
- saving a signature stores it back through the page data service
Relevant service usage:
- `PageDataService.GetCachedSignatureAsync(...)`
- `PageDataService.SaveCachedSignatureAsync(...)`
Related state:
- `_capturedSignature`
- `_signaturePopupVisible`
Important note:
- the active cache handling is server-side through distributed cache
- the receiver signature is not only in browser state
---
## 13. Signature change locking after signing starts
The current page prevents changing the captured signature after at least one signature has already been applied.
Behavior:
- signature change button becomes disabled when `_signedSignatures > 0`
- title explains the signature is locked
- reset requires restarting the signing session
Relevant methods:
- `GetSignatureButtonTitle()`
- `HandleSignatureChangeClick()`
- `RestartSigning()`
Important implication:
- once actual field signing begins, the selected/captured signature is treated as fixed for that session unless the page is reset
---
## 14. Reset/restart signing flow
The page includes a reset/restart behavior.
Current behavior:
- page reload is used to reset signing UI state
- this clears current in-view applied signatures and session UI state
Relevant method:
- `RestartSigning()`
Implementation detail:
- current reset behavior is based on `Navigation.NavigateTo(Navigation.Uri, forceLoad: true)`
---
## 15. PDF quality and rendering options
The page sends rendering options from .NET to JavaScript before viewer initialization.
Relevant options include:
- thumbnail base scale
- thumbnail HiDPI support
- thumbnail max DPR
- main canvas HiDPI support
- main canvas max DPR
- smooth zoom enablement
- zoom transition duration
- rendering opacity
- zoom step percentage
These come from:
- `IOptions<PdfViewerOptions>`
Relevant JS call:
- `pdfViewer.setQualityOptions(...)`
This means rendering quality and viewer performance are configurable from server-side options.
---
## 16. Local storage usage
The page currently uses `localStorage` at least for UI preferences.
Known usage:
- thumbnail sidebar width persistence
Known key:
- `envelopeViewer_thumbnailWidth`
This is UI preference storage, not the main signature cache mechanism.
---
## 17. Receiver authorization dependency
The page is not a public PDF viewer. It depends on receiver-specific authorization.
Before loading the document:
- `ReceiverAuthorizationService.AuthorizeAsync(EnvelopeKey)` is executed
- unauthorized users are redirected to `/envelope/login/{EnvelopeKey}`
Implication for other agents:
- viewer behavior is tied to authenticated receiver context
- placeholder loading and cached signature loading are receiver-specific
---
## 18. Receiver-specific data loading
The page loads its data through `EnvelopeReceiverPageDataService`.
Main server-side data loaded:
- document bytes
- signature placeholders
- receiver envelope data
- cached signature
Important domain behavior:
- signature placeholders are filtered for the authenticated receiver
- placeholder coordinates are converted to points before UI use
This matters for any future changes involving coordinate systems or overlay placement.
---
## Important Non-Goals / Current Constraints
### Not acceptable as a migration outcome
The migration is **not** successful if it results in:
- losing any receiver-side behavior documented in this file
- replacing the current workflow with a simplified read-only PDF viewer
- removing signature placeholder overlays or applied signature overlays
- removing cross-page signature navigation
- breaking zoom-time overlay synchronization
- falling back to legacy `ReceiverUI` as the main implementation target
- changing the flow into direct PDF binary stamping-only behavior in the main receiver UI
### Current active model
The current implementation **is** based on:
- `EnvelopeGenerator.Server`
- `EnvelopeReceiverPage.razor`
- `PDF.js`
- custom overlay and JS interop behavior
### Target model
The intended target model is:
- `EnvelopeGenerator.Server`
- `EnvelopeReceiverPage.razor`
- `DxPdfViewer` as the main PDF rendering component
- equivalent custom behavior for overlays, navigation, zoom synchronization, and receiver signing interactions
- preservation of the current receiver-specific authorization and page data loading flow
---
## Key Behaviors Other Agents Must Preserve
If another agent modifies this area, these behaviors are important to keep intact:
1. Receiver authorization before document access
2. Single active page PDF viewing
3. Page navigation and thumbnail navigation
4. Zoom in/out with correct overlay synchronization
5. Signature placeholder rendering on the correct page
6. Clicking a placeholder applies the captured signature overlay
7. Fast previous/next signature navigation across pages
8. Automatic overlay position and size updates after zoom/page changes
9. Signature popup with draw/text/image modes
10. Required metadata validation for full name and place
11. Cached signature reuse
12. Signature lock after signing has started
13. Reset/restart signing behavior
14. Thumbnail width persistence
## Migration Requirement
Any migration from `PDF.js` to `DxPdfViewer` in `EnvelopeReceiverPage.razor` must be treated as a rendering engine swap, not a workflow redesign.
The expected outcome is:
- `DxPdfViewer` replaces `PDF.js` as the main viewer technology
- the receiver authorization model remains unchanged
- the existing signature capture popup model remains unchanged
- signature placeholder rendering remains receiver-specific
- applied signature visuals remain aligned with the rendered document
- page navigation, zoom, thumbnails, and signature navigation continue to behave equivalently
- no documented feature in this file is dropped during the migration
---
## Suggested Mental Model
A useful way to think about the page is:
- `EnvelopeReceiverPage.razor` = orchestration and state
- current rendering engine = `PDF.js`
- target rendering engine = `DxPdfViewer`
- current viewer behavior layer = `pdf-viewer.js` for overlays, navigation, thumbnails, resize logic
- `receiver-signature.js` = signature capture/creation logic
- server page data service = receiver-specific document and signature source
---
## Files Most Likely Relevant For Future Work
### Main page
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Components/Pages/EnvelopeReceiverPage.razor`
### JavaScript
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/wwwroot/js/pdf-viewer.js`
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/wwwroot/js/receiver-signature.js`
### CSS
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/wwwroot/css/envelope-viewer.css`
### Data/auth services
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Services/EnvelopeReceiverAuthorizationService.cs`
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/Services/EnvelopeReceiverPageDataService.cs`
---
## Summary
The active receiver document experience is currently a `PDF.js`-based, server-orchestrated, overlay-signing workflow in `EnvelopeGenerator.Server`.
It supports:
- single-page PDF display
- page navigation
- zoom
- thumbnails
- receiver-specific signature placeholders
- actual signature overlay placement
- fast signature navigation across pages
- overlay rescaling/repositioning during zoom
- signature capture via draw/text/image
- cached signature reuse
The current migration goal is to move this experience to `DxPdfViewer` in `EnvelopeReceiverPage.razor` without losing any of the capabilities listed above.
Any future changes in this area should be made with the assumption that this is a custom receiver signing experience whose rendering engine may change, but whose functional behavior must remain intact; it is not a basic document embed and not a direct PDF stamping pipeline.