Transitioned PDF rendering in `EnvelopeReceiverPage.razor` from `PDF.js` to `DevExpress DxPdfViewer`. Updated code and documentation to reflect the verified API of `DevExpress.Blazor.PdfViewer` v25.2.3, addressing its limitations (e.g., lack of `GoToPageAsync`, `PageNumberChanged`). Implemented `CustomizeToolbar` for navigation/zoom controls and manual state tracking for `_currentPage` and `_viewerZoomLevel`. Replaced JavaScript interop for page count with the `PageCount` property. Retained the custom thumbnail sidebar due to API constraints. Added temporary debug tools for DOM analysis and navigation testing. Updated `TESTING_CHECKLIST.md` and added `DEVEXPRESS_V25_LIMITATIONS.md` to document the new strategy, API limitations, and testing scenarios. Cross-page signature navigation implemented with state updates, though visible page changes remain manual. Prepared for future improvements while ensuring functional migration to `DxPdfViewer`.
1273 lines
40 KiB
Markdown
1273 lines
40 KiB
Markdown
# 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
|
|
|
|
### ? CRITICAL: DevExpress Integration Strategy
|
|
|
|
**Current implementation uses DevExpress built-in toolbar with C# API event handling.**
|
|
|
|
**?? DevExpress v25.2.3 Limitation: No `ToolbarVisible` property exists!**
|
|
- Toolbar cannot be completely hidden
|
|
- Use `CustomizeToolbar` event to minimize toolbar items
|
|
|
|
**Primary control method:**
|
|
- DevExpress `DxPdfViewer` component with `CustomizeToolbar` event
|
|
- Toolbar customized to show only essential navigation/zoom controls
|
|
- Custom signature-specific toolbar preserved separately
|
|
|
|
**C# API Integration:**
|
|
|
|
1. **Toolbar Customization** ? Minimize DevExpress toolbar:
|
|
```csharp
|
|
<DxPdfViewer CustomizeToolbar="OnCustomizeToolbar" />
|
|
|
|
protected void OnCustomizeToolbar(ToolbarModel toolbarModel)
|
|
{
|
|
// Keep only essential items
|
|
var essentialItems = toolbarModel.AllItems
|
|
.Where(item => item.Id == ToolbarItemId.PreviousPage ||
|
|
item.Id == ToolbarItemId.NextPage ||
|
|
item.Id == ToolbarItemId.ZoomIn ||
|
|
item.Id == ToolbarItemId.ZoomOut)
|
|
.ToList();
|
|
|
|
toolbarModel.AllItems.Clear();
|
|
foreach (var item in essentialItems)
|
|
toolbarModel.AllItems.Add(item);
|
|
}
|
|
```
|
|
|
|
2. **Page Navigation Events** ? React to DevExpress navigation:
|
|
```csharp
|
|
<DxPdfViewer PageNumberChanged="OnPageNumberChanged" />
|
|
|
|
private async Task OnPageNumberChanged(int newPageNumber)
|
|
{
|
|
_currentPage = newPageNumber;
|
|
await RenderSignatureButtonsAsync(); // Update overlays
|
|
}
|
|
```
|
|
|
|
3. **Zoom Events** ? React to DevExpress zoom:
|
|
```csharp
|
|
<DxPdfViewer ZoomLevelChanged="OnZoomLevelChanged" />
|
|
|
|
private async Task OnZoomLevelChanged(double newZoomLevel)
|
|
{
|
|
_viewerZoomLevel = newZoomLevel;
|
|
_currentZoom = (int)Math.Round(newZoomLevel * 100);
|
|
await RenderSignatureButtonsAsync(); // Update overlays
|
|
}
|
|
```
|
|
|
|
**JavaScript role (LIMITED):**
|
|
- ? Overlay geometry calculations (signature placeholder positioning)
|
|
- ? PDF.js helper for thumbnail generation
|
|
- ? Custom UI interactions (sidebar resize, signature canvas)
|
|
- ? Scroll-to-element helper for signature navigation
|
|
|
|
**JavaScript MUST NOT:**
|
|
- ? Attempt to control DxPdfViewer page navigation via DOM manipulation
|
|
- ? Attempt to control DxPdfViewer zoom via DOM manipulation
|
|
- ? Use jQuery/DevExtreme client API to control the component
|
|
|
|
**IMPORTANT: CustomizeToolbar Event**
|
|
- DevExpress `CustomizeToolbar` event is the ONLY way to modify toolbar in v25.2.3
|
|
- Cannot hide toolbar completely - can only customize items
|
|
- See: https://docs.devexpress.com/Blazor/DevExpress.Blazor.PdfViewer.DxPdfViewer.CustomizeToolbar
|
|
|
|
**Reference:**
|
|
- Official API: https://docs.devexpress.com/Blazor/DevExpress.Blazor.PdfViewer.DxPdfViewer
|
|
- DevExpress Documentation MCP Server: https://docs.devexpress.com/GeneralInformation/405551/help-resources/dev-express-documentation-mcp-server-configure-an-ai-powered-assistant
|
|
|
|
### 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 PDF viewer experience with receiver-specific features.
|
|
|
|
**Important:** After `DxPdfViewer` migration, `pdf-viewer.js` role changes:
|
|
- **BEFORE migration:** Full PDF.js control (page nav, zoom, rendering)
|
|
- **AFTER migration:** Limited to overlay geometry and PDF.js thumbnail helper only
|
|
- **DxPdfViewer control:** Exclusively through C# API in `EnvelopeReceiverPage.razor`
|
|
|
|
---
|
|
|
|
## 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
|
|
|
|
---
|
|
|
|
## Current Post-Migration Status and Step-by-Step Feature Recovery Plan
|
|
|
|
This section replaces the earlier generic migration plan and records the current real status after the first `DxPdfViewer` swap attempt.
|
|
|
|
### 1. Current status summary
|
|
|
|
The main document is now visible with `DxPdfViewer`, which means:
|
|
|
|
- receiver authorization still works
|
|
- document loading still works
|
|
- the route and page host still work
|
|
- `DxPdfViewer` is now the visible main rendering surface
|
|
|
|
However, the receiver experience is currently **feature-incomplete**.
|
|
|
|
At the moment, the page is effectively in this state:
|
|
|
|
- PDF is displayed
|
|
- custom workflow shell still exists
|
|
- but the old `PDF.js` behavior contract is no longer fully connected to the new viewer
|
|
|
|
So this is no longer a rendering failure.
|
|
It is now a **behavior recovery** problem.
|
|
|
|
### 2. What is currently missing or unreliable
|
|
|
|
The following features are currently missing, incomplete, or highly likely to be non-functional in the present state.
|
|
|
|
#### 2.1 Page navigation is not truly connected to `DxPdfViewer`
|
|
|
|
Symptoms:
|
|
|
|
- previous page button may update Blazor state only
|
|
- next page button may update Blazor state only
|
|
- direct page input may update Blazor state only
|
|
- thumbnail click may update Blazor state only
|
|
- signature navigation page jumps may update Blazor state only
|
|
|
|
Why:
|
|
|
|
- the earlier attempt tried to use `ActivePageIndex`, but `DxPdfViewer` does not expose it as a component parameter in this version
|
|
- after removing that invalid parameter, the page still updates `_currentPage`, but that does not automatically move the DevExpress viewer to another page
|
|
- in other words, there is currently no verified programmatic page-navigation bridge into `DxPdfViewer`
|
|
|
|
Impact:
|
|
|
|
- all features that depend on true page switching are blocked or only partially simulated
|
|
|
|
#### 2.2 Zoom controls are not truly connected to `DxPdfViewer`
|
|
|
|
Symptoms:
|
|
|
|
- zoom in/out buttons may update `_currentZoom`
|
|
- slider may update `_currentZoom`
|
|
- ctrl+wheel logic may update `_currentZoom`
|
|
- but the visible viewer may not actually zoom accordingly, or may not do so reliably
|
|
|
|
Why:
|
|
|
|
- `ZoomLevel` is exposed as a property on `DxPdfViewer`, but it is not yet confirmed that changing the bound value gives the same runtime behavior expected by the old `PDF.js` flow
|
|
- the current implementation updates Blazor state and triggers overlay refresh, but the real DevExpress zoom lifecycle and its timing have not yet been validated
|
|
- no verified zoom-changed callback from `DxPdfViewer` has been integrated
|
|
|
|
Impact:
|
|
|
|
- zoom UX is incomplete
|
|
- overlay synchronization cannot be trusted until real zoom behavior is confirmed
|
|
|
|
#### 2.3 Overlay positioning is heuristic, not yet proven
|
|
|
|
Symptoms:
|
|
|
|
- signature placeholders may not appear
|
|
- or may appear in the wrong location
|
|
- or may not track zoom/page changes correctly
|
|
|
|
Why:
|
|
|
|
- old implementation owned the page canvas directly
|
|
- new implementation tries to detect the visible page surface inside `DxPdfViewer` DOM heuristically
|
|
- this means the current adapter is guessing which DOM element is the active rendered page surface
|
|
- page geometry, offsets, scaling and scroll surface are not yet bound to a stable DevExpress contract
|
|
|
|
Impact:
|
|
|
|
- placeholder buttons are not yet production-safe
|
|
- applied signatures are not yet production-safe
|
|
|
|
#### 2.4 Applied signature overlay behavior is not yet dependable
|
|
|
|
Symptoms:
|
|
|
|
- clicking a placeholder may not place the visual signature correctly
|
|
- applied signatures may drift after zoom
|
|
- applied signatures may disappear or misalign when navigation changes
|
|
|
|
Why:
|
|
|
|
- applied signatures currently depend on the same unverified geometry bridge as placeholders
|
|
- they also depend on reliable page identity and zoom identity from the DevExpress surface
|
|
|
|
Impact:
|
|
|
|
- the core signing UX is not yet restored
|
|
|
|
#### 2.5 Signature navigation is structurally present but functionally blocked
|
|
|
|
Symptoms:
|
|
|
|
- previous/next signature buttons may update internal navigation state
|
|
- but page-jump and scroll-to-target behavior may fail or be inconsistent
|
|
|
|
Why:
|
|
|
|
- signature navigation depends on:
|
|
- real page navigation
|
|
- real overlay rendering
|
|
- reliable scroll container discovery
|
|
- since all three are not yet stabilized, signature navigation cannot yet be trusted
|
|
|
|
Impact:
|
|
|
|
- navigation across signature fields is not complete
|
|
|
|
#### 2.6 Thumbnail sidebar is only partially preserved
|
|
|
|
What still exists:
|
|
|
|
- sidebar shell
|
|
- width persistence
|
|
- resize logic
|
|
- thumbnail rendering through helper pipeline
|
|
|
|
What is missing or uncertain:
|
|
|
|
- guaranteed synchronization between thumbnail click and visible page in `DxPdfViewer`
|
|
- guaranteed active-page highlight based on real viewer page
|
|
|
|
Why:
|
|
|
|
- active thumbnail currently follows `_currentPage`
|
|
- but `_currentPage` is not yet guaranteed to reflect the actual active page inside `DxPdfViewer`
|
|
|
|
#### 2.7 Single-page behavior is not yet guaranteed to match the old experience
|
|
|
|
Why:
|
|
|
|
- old implementation enforced a single active rendered page in a fully custom surface
|
|
- new implementation uses `DxPdfViewer IsSinglePagePreview="true"`
|
|
- but this still needs runtime verification against:
|
|
- page scroll behavior
|
|
- zoom behavior
|
|
- page switch timing
|
|
- overlay surface alignment
|
|
|
|
### 3. Root cause analysis: why features disappeared
|
|
|
|
The features disappeared for one main reason:
|
|
|
|
- the old receiver experience was not just a PDF viewer
|
|
- it was a **custom stateful viewer platform** built on top of `PDF.js`
|
|
|
|
In the old system:
|
|
|
|
- page navigation was owned by our JavaScript
|
|
- zoom was owned by our JavaScript
|
|
- page metrics were owned by our JavaScript
|
|
- active page surface was owned by our JavaScript
|
|
- overlays were drawn on DOM elements owned by our JavaScript
|
|
|
|
After the swap:
|
|
|
|
- the visible rendering surface belongs to DevExpress
|
|
- but the custom behavior layer still expects low-level ownership similar to `PDF.js`
|
|
|
|
So the missing features are not random regressions.
|
|
They are the direct result of replacing the renderer before fully rebuilding the viewer behavior bridge.
|
|
|
|
### 4. New recommended implementation strategy
|
|
|
|
Do **not** try to restore everything at once.
|
|
|
|
The correct path now is to restore behavior in controlled stages, one capability at a time.
|
|
|
|
### 5. Step-by-step recovery plan
|
|
|
|
#### Step 1 — Confirm the real `DxPdfViewer` API surface
|
|
|
|
Goal:
|
|
|
|
- stop guessing which `DxPdfViewer` properties and behaviors are actually bindable and runtime-reactive
|
|
|
|
Tasks:
|
|
|
|
- inspect the actual supported parameter surface for the installed DevExpress version
|
|
- determine which of the following are truly supported and reactive:
|
|
- zoom binding
|
|
- page navigation binding
|
|
- single-page mode behavior
|
|
- toolbar customization / suppression
|
|
- determine whether DevExpress exposes any JS/API for page switching or current page retrieval
|
|
|
|
Expected output:
|
|
|
|
- a verified list of what `DxPdfViewer` can really do directly
|
|
- a list of behaviors that must remain custom because DevExpress does not expose them
|
|
|
|
This step is mandatory before further feature work.
|
|
|
|
#### Step 2 — Restore real zoom behavior first
|
|
|
|
Goal:
|
|
|
|
- make zoom in/out/slider visibly affect `DxPdfViewer`
|
|
|
|
Why first:
|
|
|
|
- zoom is simpler than full page navigation
|
|
- it is also required before overlay sizing can be trusted
|
|
|
|
Tasks:
|
|
|
|
- verify whether `ZoomLevel` binding actually updates the viewer at runtime
|
|
- if yes, stabilize it
|
|
- if no, find the supported DevExpress-side mechanism
|
|
- disable or adjust any overlay refresh logic until true zoom behavior is confirmed
|
|
|
|
Acceptance for this step:
|
|
|
|
- zoom in button visibly zooms document
|
|
- zoom out button visibly zooms document
|
|
- slider visibly changes viewer zoom
|
|
- `_currentZoom` matches what the user sees
|
|
|
|
#### Step 3 — Restore real page navigation
|
|
|
|
Goal:
|
|
|
|
- make previous/next/page input/thumbnail click truly change the visible page in `DxPdfViewer`
|
|
|
|
Tasks:
|
|
|
|
- identify supported page navigation mechanism for this DevExpress version
|
|
- connect `_currentPage` to the real viewer page
|
|
- if direct binding is unavailable, create a supported indirect mechanism
|
|
|
|
Acceptance for this step:
|
|
|
|
- previous page works
|
|
- next page works
|
|
- direct page input works
|
|
- `_currentPage` reflects visible page
|
|
|
|
This step must be completed before signature navigation or page-specific overlays are considered reliable.
|
|
|
|
#### Step 4 — Stabilize active page geometry extraction
|
|
|
|
Goal:
|
|
|
|
- determine a reliable way to identify the active visible rendered page inside the DevExpress DOM
|
|
|
|
Tasks:
|
|
|
|
- inspect the actual DOM produced by `DxPdfViewer`
|
|
- identify stable selectors for:
|
|
- viewer scroll container
|
|
- active page surface
|
|
- page content bounds
|
|
- replace heuristic “largest visible element” logic if a more stable pattern exists
|
|
|
|
Acceptance for this step:
|
|
|
|
- geometry retrieval returns correct page bounds repeatedly
|
|
- geometry remains stable after zoom
|
|
- geometry remains stable after page changes
|
|
|
|
#### Step 5 — Restore signature placeholder overlay rendering
|
|
|
|
Goal:
|
|
|
|
- make unsigned signature placeholders render at correct positions on the current page
|
|
|
|
Tasks:
|
|
|
|
- connect placeholder rendering to verified page bounds
|
|
- preserve current filtering rules:
|
|
- active page only
|
|
- hide already-signed fields
|
|
- validate size scaling against visible zoom
|
|
|
|
Acceptance for this step:
|
|
|
|
- placeholder appears on correct page
|
|
- placeholder appears in correct location
|
|
- placeholder size follows zoom
|
|
|
|
#### Step 6 — Restore applied signature overlays
|
|
|
|
Goal:
|
|
|
|
- clicking a placeholder should place the actual signature overlay correctly
|
|
|
|
Tasks:
|
|
|
|
- keep current visual block format
|
|
- bind position/size to the same verified geometry system as placeholders
|
|
- ensure page changes hide/show correctly
|
|
|
|
Acceptance for this step:
|
|
|
|
- clicking placeholder places signature correctly
|
|
- applied signature stays aligned after zoom
|
|
- applied signature stays aligned after page change
|
|
|
|
#### Step 7 — Restore signature navigation
|
|
|
|
Goal:
|
|
|
|
- previous/next signature should work across all pages again
|
|
|
|
Tasks:
|
|
|
|
- keep the existing global signature ordering logic
|
|
- connect it to real page switching
|
|
- connect it to real overlay availability
|
|
- connect it to real scroll-to-target behavior
|
|
|
|
Acceptance for this step:
|
|
|
|
- next signature works on same page
|
|
- next signature jumps across pages
|
|
- previous signature works
|
|
- current index / signed / unsigned counters are correct
|
|
|
|
#### Step 8 — Validate and finish thumbnail behavior
|
|
|
|
Goal:
|
|
|
|
- make sidebar behavior trustworthy again
|
|
|
|
Tasks:
|
|
|
|
- confirm thumbnails still render correctly
|
|
- ensure thumbnail click changes actual visible page
|
|
- ensure active thumbnail reflects actual page
|
|
- keep width persistence and resize behavior
|
|
|
|
Acceptance for this step:
|
|
|
|
- thumbnail click works
|
|
- active highlight is correct
|
|
- width persistence still works
|
|
|
|
### 6. Recommended order of implementation
|
|
|
|
The recommended execution order is:
|
|
|
|
1. verify `DxPdfViewer` real API surface
|
|
2. restore zoom
|
|
3. restore page navigation
|
|
4. stabilize page geometry extraction
|
|
5. restore placeholder overlays
|
|
6. restore applied signatures
|
|
7. restore signature navigation
|
|
8. finish thumbnail synchronization
|
|
|
|
This order is important because later features depend on earlier ones.
|
|
|
|
### 7. What should not be changed yet
|
|
|
|
Until the viewer bridge is stable, avoid refactoring these parts unnecessarily:
|
|
|
|
- receiver authorization flow
|
|
- page data loading service
|
|
- cached signature flow
|
|
- popup tabs and signature capture JS
|
|
- signature metadata validation
|
|
- signature lock behavior
|
|
- restart signing behavior
|
|
|
|
These parts are not the current problem.
|
|
|
|
### 8. Next implementation checkpoint
|
|
|
|
The next actual coding step should be:
|
|
|
|
- **Step 1 + Step 2** together if possible:
|
|
- confirm supported `DxPdfViewer` interaction surface
|
|
- restore real zoom behavior first
|
|
|
|
That is the safest first vertical slice because:
|
|
|
|
- it is easy to verify visually
|
|
- it reduces uncertainty in overlay scaling
|
|
- it does not yet require full signature flow completion
|
|
|
|
### 9. Verified DevExpress API findings and current progress
|
|
|
|
The following points are now verified for the currently installed `DevExpress.Blazor.PdfViewer` package version `25.2.3`:
|
|
|
|
**Available `[Parameter]` properties (GET/SET):**
|
|
- `DocumentContent` (`byte[]`) — feed PDF as byte array ?
|
|
- `ZoomLevel` (`double`) — zoom factor, not percentage. `1.5` = `150%` ?
|
|
- `IsSinglePagePreview` (`bool`) — single page mode ?
|
|
- `CssClass` (`string`) — assign CSS class ?
|
|
- `DocumentName` (`string`) — download filename, default `"Document"` ?
|
|
- `SizeMode` (`SizeMode?`) — `Small`, `Medium`, `Large` ?
|
|
|
|
**Read-only properties (GET only, no setter):**
|
|
- `ActivePageIndex` (`int`) — active page index, 0-based. Cannot SET, no programmatic navigation. ?
|
|
- `PageCount` (`int`) — total pages in document. **Replaces JS `getTotalPages()` call.** ?
|
|
|
|
**Available event:**
|
|
- `CustomizeToolbar` — only event available for toolbar customization ?
|
|
|
|
**Does NOT exist in v25.2.3:**
|
|
- `PageNumberChanged` event ?
|
|
- `ZoomLevelChanged` event ?
|
|
- `ToolbarVisible` property ?
|
|
- `GoToPageAsync()` method ?
|
|
- `GoToNextPageAsync()` method ?
|
|
- `ZoomAsync()` method ?
|
|
|
|
**Critical ZoomLevel rule:**
|
|
- ZoomLevel is a **factor**, not a percentage
|
|
- `_viewerZoomLevel = _currentZoom / 100d` — always divide by 100
|
|
- Example: `_currentZoom = 150` ? `_viewerZoomLevel = 1.5`
|
|
- Using `150` directly causes DevExpress to display `15000%`
|
|
|
|
**PageCount usage (no JS needed):**
|
|
```csharp
|
|
// In OnAfterRenderAsync
|
|
if (_pdfViewer is not null && _pdfViewer.PageCount > 0)
|
|
{
|
|
_totalPages = _pdfViewer.PageCount; // direct, no JS interop
|
|
_pdfLoaded = true;
|
|
await InvokeAsync(StateHasChanged);
|
|
}
|
|
```
|
|
|
|
### 10. Recovery progress update
|
|
|
|
The first functional recovery step is zoom restoration.
|
|
|
|
Implemented direction:
|
|
|
|
- bind receiver zoom state to `DxPdfViewer.ZoomLevel`
|
|
- remove competing custom JS ctrl+wheel zoom logic
|
|
- keep overlay redraw pipeline in place
|
|
- avoid `ZoomLevelChanged` usage because it is not available on the installed `25.2.3` `DxPdfViewer` surface
|
|
|
|
Expected outcome after this step:
|
|
|
|
- built-in DevExpress zoom UI can be used without the runtime parameter exception
|
|
- custom zoom duplication is removed
|
|
|
|
### 11. Additional findings after first zoom integration attempt
|
|
|
|
After the first live integration attempt against the installed `DevExpress.Blazor.PdfViewer` package version `25.2.3`, one important runtime behavior was confirmed:
|
|
|
|
- the `DxPdfViewer.ZoomLevel` value must be treated as a zoom factor for normal positive values in the live UI flow used here
|
|
- in practice, `1.5` corresponds to `150%`
|
|
- using `150` as the bound value causes the DevExpress viewer UI to display `15000%`
|
|
|
|
This means the receiver page must keep two different zoom representations:
|
|
|
|
- `_currentZoom` = receiver custom workflow percentage view, for example `150`
|
|
- `_viewerZoomLevel` = DevExpress viewer value, for example `1.5`
|
|
|
|
### 12. FINAL DECISION: CustomizeToolbar + Manual State Tracking
|
|
|
|
**Implementation strategy (verified for v25.2.3):**
|
|
|
|
1. **Remove custom PDF toolbar** (page navigation, zoom controls from our HTML)
|
|
- DevExpress provides these via `CustomizeToolbar` event
|
|
- Add custom prev/next/zoom-in/zoom-out as `ToolbarItem` objects
|
|
- Each button manually updates `_currentPage`, `_viewerZoomLevel`, then calls `RenderSignatureButtonsAsync()`
|
|
|
|
2. **Preserve signature-specific toolbar** (separate from DxPdfViewer toolbar)
|
|
- Signature change button
|
|
- Previous/Next signature navigation
|
|
- Signature counter (signed/unsigned/total)
|
|
- Reset button
|
|
|
|
3. **Correct DxPdfViewer usage:**
|
|
|
|
```razor
|
|
<DxPdfViewer @ref="_pdfViewer"
|
|
CssClass="envelope-dx-pdf-viewer"
|
|
DocumentContent="@_pdfDocumentContent"
|
|
ZoomLevel="@_viewerZoomLevel"
|
|
IsSinglePagePreview="true"
|
|
CustomizeToolbar="OnCustomizeToolbar" />
|
|
```
|
|
|
|
```csharp
|
|
// Two zoom representations required:
|
|
// _currentZoom = 150 (UI display: "150%")
|
|
// _viewerZoomLevel = 1.5 (DxPdfViewer parameter: factor)
|
|
|
|
protected void OnCustomizeToolbar(ToolbarModel toolbarModel)
|
|
{
|
|
toolbarModel.AllItems.Clear();
|
|
|
|
var prevButton = new ToolbarItem
|
|
{
|
|
IconCssClass = "dx-icon-chevronprev",
|
|
Enabled = _currentPage > 1,
|
|
Click = async (args) =>
|
|
{
|
|
if (_currentPage > 1)
|
|
{
|
|
_currentPage--;
|
|
_viewerZoomLevel = _currentZoom / 100d;
|
|
await InvokeAsync(StateHasChanged);
|
|
await RenderSignatureButtonsAsync();
|
|
}
|
|
}
|
|
};
|
|
// ... add nextButton, zoomIn, zoomOut similarly
|
|
toolbarModel.AllItems.Add(prevButton);
|
|
}
|
|
```
|
|
|
|
**Why this is the correct approach for v25.2.3:**
|
|
- `GoToPageAsync()` does NOT exist ?
|
|
- `PageNumberChanged` event does NOT exist ?
|
|
- `ZoomLevelChanged` event does NOT exist ?
|
|
- `ToolbarVisible` property does NOT exist ?
|
|
- `CustomizeToolbar` is the ONLY available hook ?
|
|
- `ZoomLevel` binding works but needs factor format (divide by 100) ?
|
|
- `PageCount` property is available directly ?
|
|
|
|
### 13. Current UI decision for thumbnails
|
|
|
|
**Decision: Keep custom thumbnail sidebar**
|
|
|
|
Reason:
|
|
|
|
- Current receiver workflow depends on custom thumbnail shell behavior
|
|
- Width persistence and resizable splitter are already implemented in the custom sidebar
|
|
- Thumbnail click updates `_currentPage` state and calls `RenderSignatureButtonsAsync()`
|
|
- **Known limitation:** thumbnail click cannot move DevExpress viewer to target page (no `GoToPageAsync()` in v25.2.3)
|
|
- Active thumbnail highlight follows `_currentPage` state
|
|
|
|
**What thumbnail click can do:**
|
|
```csharp
|
|
async Task GoToPageFromThumbnail(int pageNum)
|
|
{
|
|
if (pageNum < 1 || pageNum > _totalPages) return;
|
|
_currentPage = pageNum; // state updated
|
|
_viewerZoomLevel = _currentZoom / 100d;
|
|
await InvokeAsync(StateHasChanged);
|
|
await RenderSignatureButtonsAsync(); // overlays refreshed for new page
|
|
// NOTE: DxPdfViewer visible page does NOT change - v25.2.3 has no navigation API
|
|
}
|
|
```
|
|
|
|
**Future consideration:**
|
|
- Evaluate DevExpress `ThumbnailPanelVisible` property if it becomes available
|
|
- Full thumbnail navigation requires v25.2.3 API upgrade or alternative approach
|
|
|
|
### 14. Signature Navigation Implementation
|
|
|
|
**Cross-page signature navigation limitation in v25.2.3:**
|
|
|
|
Because `GoToPageAsync()` does NOT exist, cross-page signature navigation is limited.
|
|
The current workaround updates `_currentPage` state and refreshes overlays, but the
|
|
DxPdfViewer visible page does not programmatically change.
|
|
|
|
```csharp
|
|
private async Task GoToNextSignature()
|
|
{
|
|
// Find next signature across all pages
|
|
var nextSig = FindNextSignatureFromCurrent();
|
|
if (nextSig == null) return;
|
|
|
|
if (nextSig.PageNumber != _currentPage)
|
|
{
|
|
// Update state - overlays will refresh for new page
|
|
// NOTE: DxPdfViewer visible page does NOT change (no GoToPageAsync in v25.2.3)
|
|
// The user must use the custom toolbar prev/next buttons to navigate pages
|
|
_currentPage = nextSig.PageNumber;
|
|
_viewerZoomLevel = _currentZoom / 100d;
|
|
await InvokeAsync(StateHasChanged);
|
|
}
|
|
|
|
// Refresh overlays for current page
|
|
await RenderSignatureButtonsAsync();
|
|
await UpdateSignatureCounterAsync();
|
|
|
|
// Scroll to signature element if on same page (JS helper for UI only)
|
|
await JSRuntime.InvokeVoidAsync("pdfViewer.scrollToSignature", nextSig.Id);
|
|
}
|
|
```
|
|
|
|
**Why GoToPageAsync is not available:**
|
|
- `GoToPageAsync()` does NOT exist in v25.2.3 ?
|
|
- `PageNumberChanged` event does NOT exist to confirm navigation ?
|
|
- Only `CustomizeToolbar` buttons can trigger verifiable page state changes ?
|
|
|
|
**Acceptable workaround:**
|
|
- Custom toolbar prev/next buttons are the only reliable navigation mechanism
|
|
- Signature navigation updates state and refreshes overlays
|
|
- Users navigate to the correct page using toolbar buttons when cross-page jump is needed
|
|
|
|
---
|
|
|
|
## 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.
|