# 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` 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.