This commit introduces a detailed migration plan to replace the `PDF.js` rendering engine with `DxPdfViewer` in the receiver signing experience (`EnvelopeReceiverPage.razor`). The migration preserves the existing signing workflow and behavior while introducing a new rendering layer. Key changes: - Replaced `PDF.js` rendering surface with `DxPdfViewer`. - Preserved page-level orchestration for authorization, document loading, signature handling, and toolbar interactions. - Introduced a custom overlay adapter for signature placeholders and applied signature overlays. - Centralized page/zoom geometry acquisition for overlay alignment. - Maintained signature navigation logic and thumbnail sidebar behavior. - Updated `pdf-viewer.js` to separate engine-specific logic and adapt it for `DxPdfViewer`. - Updated styles in `envelope-viewer.css` to support the new viewer. This migration ensures that all existing workflow behaviors remain functional, including navigation, zoom, signature placement, and validation, while transitioning to the new rendering engine.
34 KiB
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.razorEnvelopeGenerator.Server/EnvelopeGenerator.Server/wwwroot/js/pdf-viewer.jsEnvelopeGenerator.Server/EnvelopeGenerator.Server/wwwroot/js/receiver-signature.jsEnvelopeGenerator.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.jshttps://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.jsEnvelopeGenerator.Server/EnvelopeGenerator.Server/wwwroot/js/receiver-signature.jsEnvelopeGenerator.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-canvaspdf-text-layerpdf-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.goToPreviousSignaturepdfViewer.goToNextSignaturepdfViewer.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:
drawtextimage
Relevant constants:
SignatureTabDrawSignatureTabTextSignatureTabImage
Relevant canvas/input IDs:
envelope-signature-padenvelope-typed-signature-padenvelope-signature-image-inputenvelope-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.initializereceiverSignature.clearreceiverSignature.getDataUrlreceiverSignature.loadExistingSignature
Text mode
Receiver enters a signature as text and chooses a font.
Relevant JS usage:
receiverSignature.initializeTypedreceiverSignature.renderTypedSignaturereceiverSignature.clearTypedreceiverSignature.getTypedDataUrl
Image mode
Receiver uploads an image of their signature.
Relevant JS usage:
receiverSignature.initializeImagereceiverSignature.clearImagereceiverSignature.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
ReceiverUIas 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.ServerEnvelopeReceiverPage.razorPDF.js- custom overlay and JS interop behavior
Target model
The intended target model is:
EnvelopeGenerator.ServerEnvelopeReceiverPage.razorDxPdfVieweras 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:
- Receiver authorization before document access
- Single active page PDF viewing
- Page navigation and thumbnail navigation
- Zoom in/out with correct overlay synchronization
- Signature placeholder rendering on the correct page
- Clicking a placeholder applies the captured signature overlay
- Fast previous/next signature navigation across pages
- Automatic overlay position and size updates after zoom/page changes
- Signature popup with draw/text/image modes
- Required metadata validation for full name and place
- Cached signature reuse
- Signature lock after signing has started
- Reset/restart signing behavior
- 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:
DxPdfViewerreplacesPDF.jsas 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.jsfor overlays, navigation, thumbnails, resize logic receiver-signature.js= signature capture/creation logic- server page data service = receiver-specific document and signature source
Detailed Technical Migration Plan: PDF.js -> DxPdfViewer
This section records the implementation plan for migrating the active receiver signing experience from PDF.js to DxPdfViewer without losing any workflow behavior.
1. Core migration principle
This migration must be treated as:
- a rendering engine replacement
- not a signing workflow redesign
- not a simplification to a read-only viewer
- not a direct PDF stamping rewrite
The page-level orchestration in EnvelopeReceiverPage.razor remains the source of truth for:
- receiver authorization
- document loading
- signature placeholder loading
- cached signature loading/saving
- popup and validation flow
- signature locking rules
- reset/restart behavior
- toolbar intent
The rendering layer must be swapped while preserving those behaviors.
2. Current implementation findings from code
Based on the current files:
EnvelopeReceiverPage.razorwwwroot/js/pdf-viewer.jswwwroot/css/envelope-viewer.cssEnvelopeReceiverPage_DxPdfViewer.razor
the current page is tightly coupled to a custom pdfViewer JavaScript object.
That object currently owns all viewer-engine behaviors:
- PDF load and initialization
- current page state
- page rendering
- zoom rendering
- thumbnail rendering
- resize listener attachment
- signature button rendering
- applied signature overlay rendering
- signature navigation state
This means the migration cannot be done by replacing only the Razor markup. The current JavaScript object is effectively both:
- a
PDF.jsadapter - and a receiver-signing overlay controller
Those responsibilities must be separated conceptually during migration.
3. Verified coordinate and viewport behavior from PDF.js
Inspection of the PDF.js source comments and implementation confirms the following useful facts:
-
PageViewportis created from:viewBoxscalerotation- optional offsets
-
PDF.jsexplicitly documents thatPageViewportcreates a transform that converts:- PDF coordinate system
- into normal canvas-like coordinates
-
PageViewportParameters.viewBoxis documented as:xMin, yMin, xMax, yMax
-
getViewport(...)is documented as returning an object containing:widthheight- transforms required for rendering
-
convertToViewportPoint(x, y)is documented as converting:- PDF coordinates
- into viewport coordinates
-
convertToPdfPoint(x, y)is documented as converting:- viewport coordinates
- back into PDF coordinates
- specifically useful for converting canvas pixel locations into PDF coordinates
-
rawDimsexposes unscaled page dimensions:pageWidthpageHeightpageXpageY
-
For rotation
0,PageViewportflips the Y axis into canvas-style coordinates unlessdontFlipis used.
4. Practical meaning of the current overlay math
The current pdf-viewer.js implementation does not call convertToViewportPoint(...) directly.
Instead, it uses this simplified mapping:
xPx = sig.x * scaleyPx = sig.y * scale
This works only because the current upstream data contract already prepares signature coordinates in a way that matches the displayed page layout used by this app.
From the wider workspace context, the receiver page data service already converts placeholder coordinates to UnitOfLength.Point before they reach the UI.
Therefore, the active overlay contract is effectively:
- incoming signature coordinates are already normalized for the receiver UI
- JavaScript currently assumes a top-left, page-relative, point-based overlay space
- visual pixel placement is obtained by multiplying by current display scale
This is extremely important for the migration:
- do not casually reinterpret existing signature coordinates as raw PDF bottom-left coordinates
- do not assume
DxPdfVieweruses the same visible page coordinate origin - preserve the effective contract already used by the receiver workflow
5. Why the migration is technically hard
Although both viewers display PDFs, they are fundamentally different integration surfaces.
Current PDF.js model
- low-level canvas rendering
- direct access to viewport scale
- direct control over single page rendering
- page DOM is owned by our code
- overlays are placed over known custom elements:
pdf-canvaspdf-text-layerpdf-signature-layer
Target DxPdfViewer model
- component-driven render surface
- internal DOM is owned by DevExpress
- page layout lifecycle is controlled by the component
- zoom/page events may differ from
PDF.js - overlay host placement must be rediscovered or reintroduced
So the migration is not a canvas -> component rename.
It is a viewer capability remapping problem.
6. Technical target architecture
The target architecture should be treated as four cooperating layers.
6.1 Blazor orchestration layer
Owned by EnvelopeReceiverPage.razor.
Remains responsible for:
- auth and redirect
- data loading
- signature popup state
- signature metadata validation
- cached signature reuse
- signed/unsigned counts
- restart behavior
- toolbar user intent
6.2 Viewer host layer
Main display surface becomes DxPdfViewer.
This layer must provide equivalents for:
- document load
- current page tracking
- total pages tracking
- page navigation
- zoom control
- layout change detection
6.3 Overlay adapter layer
Custom layer that translates page/zoom/layout information into overlay placement.
This layer must handle:
- signature placeholder buttons
- applied signature overlays
- page-relative visibility
- redraw after page/zoom/layout changes
6.4 Thumbnail/navigation support layer
Custom or hybrid support for:
- left sidebar thumbnails
- active page highlight
- width persistence
- signature previous/next navigation
7. Mandatory first implementation step: extract the current viewer contract
Before changing behavior, the following current pdfViewer capabilities must be listed and then mapped one-by-one to the target implementation:
initializegetTotalPagesgetCurrentPagenextPagepreviousPagegoToPagezoomInzoomOutsetScalegetScalefitToWidthrenderThumbnailattachResizeListenersstartResizerenderSignatureButtonsapplySignaturegetSignatureNavStategoToNextSignaturegoToPreviousSignaturedispose
The migration should preserve this capability contract at the page level even if the internal engine changes.
8. Planned migration strategy
Phase 1 — Confirm the DxPdfViewer integration surface
Use EnvelopeReceiverPage_DxPdfViewer.razor as a reference and determine exactly what DxPdfViewer exposes for:
- document content binding
- page navigation
- current page reading
- page change events
- zoom setting
- zoom change events
- page layout readiness
- DOM surface that can host overlays
Key question:
- can we reliably obtain visible page geometry from the live
DxPdfViewerDOM?
If yes, overlays can be positioned relative to the rendered page. If no, a wrapper-based approximation or alternative integration is required.
Phase 2 — Replace the main render surface in EnvelopeReceiverPage.razor
The current custom canvas + text layer + signature layer block should be replaced with a DxPdfViewer host region while keeping:
- the same page route
- the same page state
- the same toolbar
- the same popup
- the same thumbnail sidebar shell
At this stage, only the main document display needs to work. Signature overlays may temporarily be disabled while the host geometry is established.
Phase 3 — Introduce a dedicated overlay host above DxPdfViewer
The new viewer surface should be wrapped with a custom page container.
Planned structure:
- outer frame
- thumbnail sidebar
- splitter
- main viewer wrapper
DxPdfViewer- absolute-positioned custom overlay layer
The overlay layer must be independently controlled by our code and not depend on internal PDF.js DOM ids.
Phase 4 — Rebuild page/zoom geometry acquisition
Because the current implementation derives position using only sig.x * scale, the target implementation must determine:
- currently visible page rectangle
- page top-left origin inside the wrapper
- effective display scale relative to logical point coordinates
- current page number
At redraw time, the adapter must produce page-relative pixel coordinates for:
- placeholder buttons
- applied signature blocks
This should be centralized in one geometry function rather than scattered across multiple viewer actions.
Phase 5 — Move signature overlay state to a canonical model
The current JavaScript keeps runtime state in:
signatureButtonsappliedSignaturesappliedSignatureElements_allSignatures_lastViewedSignatureId
This must remain stable across redraws.
Preferably:
- Blazor continues to own the authoritative business state
- JavaScript owns transient rendered DOM state only
If necessary, applied signature state should be re-sendable from .NET after viewer redraw. That prevents losing visual signatures when page layout changes.
Phase 6 — Re-implement signature placeholder rendering on top of DxPdfViewer
The current implementation filters placeholders by:
- current page
- not already applied
That same behavior must remain.
Rendering rules to preserve:
- only placeholders for the active page are shown
- already applied placeholders are hidden as buttons
- button size grows/shrinks with zoom
- button click invokes
.NETcallbackOnSignatureButtonClick
The existing scaling behavior uses baseScale = 1.5 as the visual reference.
That visual convention should be preserved initially unless a new normalized sizing model is intentionally introduced.
Phase 7 — Re-implement applied signature rendering on top of DxPdfViewer
The current applied signature overlay includes:
- signature image
- horizontal separator line
- signer full name
- optional position
- place and date
The same visual block must be retained.
Required behavior:
- clicking a placeholder converts it to an applied signature overlay
- applied signature remains visible on the correct page
- applied signature rescales with zoom
- applied signature repositions on page/zoom changes
- applied signature is hidden when the user navigates to a different page
Phase 8 — Re-implement page navigation through a central page-change pipeline
The following actions must all converge into a single page navigation routine:
- previous page button
- next page button
- direct page number input
- thumbnail click
- signature previous/next navigation when target is on another page
That routine must do all of the following in order:
- instruct viewer to change page
- update canonical current page state
- wait for rendered page surface readiness if needed
- redraw placeholder overlays
- refresh signature navigation counter state
Phase 9 — Re-implement zoom through a central zoom-change pipeline
The following actions must converge into one zoom update path:
- toolbar zoom in
- toolbar zoom out
- slider change
- fit-to-width
- viewer-native zoom events if the user triggers zoom through gestures
That routine must:
- clamp to
50% - 300% - update canonical zoom state
- wait for layout/render completion if needed
- redraw placeholder overlays
- rescale applied signatures
The current implementation already treats redraw after zoom as mandatory. That must remain true.
Phase 10 — Preserve signature navigation independently from the viewer engine
Current navigation logic is driven by the global ordered signature list and not by PDF rendering internals alone.
This behavior must remain engine-independent:
- previous/next traverses the full signature list
- traversal wraps around at the edges
- if the target signature is on another page, the viewer jumps first
- after page jump, the target element is scrolled into view
- toolbar counters reflect total/signed/unsigned/current index
If DxPdfViewer changes scrolling mechanics, the scrollToElement / scrollToButton logic must be adapted, but the traversal rules must remain unchanged.
Phase 11 — Thumbnail sidebar should remain custom unless DxPdfViewer can match all current behavior
The current sidebar is not just decorative. It also provides:
- show/hide toggle
- click navigation
- active page highlight
- resizable width
- width persistence in
localStorage - progressive rendering strategy
Because of that, the safest plan is:
- keep the custom sidebar shell and resize behavior
- only replace the source of main document rendering
If DxPdfViewer cannot provide matching thumbnails directly, a hybrid solution is acceptable:
- main document rendered by
DxPdfViewer - thumbnails still generated through a separate custom preview pipeline
This still satisfies the requirement that DxPdfViewer becomes the main viewer technology.
Phase 12 — Keep signature capture popup unchanged unless integration forces a minimal adjustment
receiver-signature.js and the popup flow should remain mostly untouched.
Preserve exactly:
- draw tab
- text tab
- image tab
- full name required
- place required
- signature data required
- cached signature reuse
- signature change lock after signing starts
This area is low-risk and should not be refactored unnecessarily during the viewer migration.
9. File-by-file execution plan
EnvelopeGenerator.Server/EnvelopeGenerator.Server/Components/Pages/EnvelopeReceiverPage.razor
Planned work:
- remove
PDF.jsCDN asset dependency from the active receiver page - replace the custom canvas-based main surface with a
DxPdfViewerhost - preserve toolbar, popup, auth, state and receiver-specific loading logic
- redirect viewer method calls through a new or adapted JS interop surface
- preserve current state fields wherever possible to minimize workflow regressions
EnvelopeGenerator.Server/EnvelopeGenerator.Server/wwwroot/js/pdf-viewer.js
Planned work:
- split engine-specific logic from workflow-specific logic
- remove dependency on direct
pdf-canvasrendering for the main viewer path - introduce
DxPdfViewer-compatible geometry and overlay placement helpers - preserve signature navigation logic semantics
- preserve overlay rendering semantics
- preserve resize integration for sidebar handling
This file will likely become a viewer adapter rather than a pure PDF.js implementation.
EnvelopeGenerator.Server/EnvelopeGenerator.Server/wwwroot/css/envelope-viewer.css
Planned work:
- keep page shell, toolbar, thumbnails, splitter and popup-related styles
- replace canvas/text-layer specific assumptions with viewer-wrapper styles
- introduce overlay host styles for the
DxPdfViewersurface - ensure z-index ordering still makes signature buttons clickable
- avoid CSS leaking into DevExpress internal DOM more than necessary
EnvelopeGenerator.Server/EnvelopeGenerator.Server/Components/Pages/EnvelopeReceiverPage_DxPdfViewer.razor
Planned role:
- reference only
- used to understand document binding and minimal
DxPdfViewersetup - not the primary delivery target unless portions are borrowed into the main receiver page
10. Risks that must be explicitly tested during implementation
DxPdfViewermay not expose visible page geometry directly.DxPdfViewermay render asynchronously in a way that requires delayed overlay redraw.- Built-in zoom/page state may not map 1:1 to current custom toolbar assumptions.
- Sidebar width changes may require explicit overlay recalculation.
- Applied signatures may visually drift if scaling math is not centralized.
- Signature navigation may fail if target DOM nodes are created later than expected.
- Built-in viewer scrolling may differ from the current wrapper scrolling model.
11. Acceptance checklist for the migration
The migration is only acceptable if all of the following still work in the active page:
- receiver authorization before document access
- document load for the authenticated receiver
- single active page viewing
- previous/next page navigation
- direct page input navigation
- thumbnail click navigation
- zoom in/out and slider zoom
- overlay alignment after zoom changes
- signature placeholder rendering on the correct page
- placeholder click applying signature overlay
- previous/next signature navigation across pages
- signed/unsigned counters updating correctly
- cached signature reuse
- signature popup validation
- signature change lock after signing starts
- restart signing behavior
- thumbnail width persistence
12. Final implementation guidance
The correct technical approach is:
- keep
EnvelopeReceiverPage.razoras the receiver workflow orchestrator - treat
DxPdfVieweras the new main rendering engine - rebuild overlay placement as a viewer-agnostic custom layer
- preserve the current receiver-specific state machine
- preserve signature navigation rules
- preserve thumbnail/sidebar interaction model where practical
In short:
- replace the renderer
- preserve the workflow
- rebuild the overlay adapter
- do not redesign the signing experience
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.jsEnvelopeGenerator.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.csEnvelopeGenerator.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.