Migrate PDF.js to DevExpress DxPdfViewer

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`.
This commit is contained in:
2026-06-30 16:12:05 +02:00
parent a10ee590c9
commit 99fbb33f1c
5 changed files with 1009 additions and 23 deletions

View File

@@ -185,11 +185,79 @@ For signature placeholders, the service:
Current receiver viewer characteristics:
- route: `/envelope/{EnvelopeKey}`
- render mode: `InteractiveServer`
- PDF rendering: `PDF.js`
- PDF rendering: **Migration in progress from `PDF.js` to `DxPdfViewer`**
- toolbar: page navigation, zoom, thumbnail toggle, signature navigation, signature reset
- signature popup: `DxPopup`
- thumbnail sidebar: resizable and stored in `localStorage`
### ⚠️ CRITICAL: DevExpress DxPdfViewer Control Requirements
**Verified API for installed `DevExpress.Blazor.PdfViewer` v25.2.3:**
| Property | Access | Notes |
|----------|--------|-------|
| `DocumentContent` | `[Parameter]` GET/SET | Feed PDF as `byte[]` |
| `ZoomLevel` | `[Parameter]` GET/SET | **Factor** (not percentage): `1.5` = 150% |
| `IsSinglePagePreview` | `[Parameter]` GET/SET | Single page mode |
| `CssClass` | `[Parameter]` GET/SET | CSS class |
| `DocumentName` | `[Parameter]` GET/SET | Download filename |
| `SizeMode` | `[Parameter]` GET/SET | `Small` / `Medium` / `Large` |
| `PageCount` | Read-only GET | Total pages — **no JS call needed** |
| `ActivePageIndex` | Read-only GET | Current page (0-based) — **cannot SET** |
| `CustomizeToolbar` | Event | Only available toolbar event |
**Does NOT exist in v25.2.3 — do NOT use:**
- `GoToPageAsync()`
- `GoToNextPageAsync()`
- `ZoomAsync()`
- `PageNumberChanged` event ❌
- `ZoomLevelChanged` event ❌
- `ToolbarVisible` property ❌
**Correct approach:**
```razor
<DxPdfViewer @ref="_pdfViewer"
DocumentContent="@_pdfDocumentContent"
ZoomLevel="@_viewerZoomLevel"
IsSinglePagePreview="true"
CustomizeToolbar="OnCustomizeToolbar" />
```
```csharp
// ZoomLevel: always divide by 100 (factor, not percentage)
_viewerZoomLevel = _currentZoom / 100d; // 150 -> 1.5
// PageCount: read directly, no JS needed
_totalPages = _pdfViewer.PageCount;
// Page navigation: only via CustomizeToolbar buttons
protected void OnCustomizeToolbar(ToolbarModel toolbarModel)
{
toolbarModel.AllItems.Clear();
var nextButton = new ToolbarItem
{
IconCssClass = "dx-icon-chevronnext",
Enabled = _currentPage < _totalPages,
Click = async (args) =>
{
_currentPage++;
_viewerZoomLevel = _currentZoom / 100d;
await InvokeAsync(StateHasChanged);
await RenderSignatureButtonsAsync();
}
};
toolbarModel.AllItems.Add(nextButton);
}
```
**JavaScript role after migration:**
- Overlay geometry calculations only
- Thumbnail rendering via PDF.js helper
- Custom UI interactions (sidebar resize, signature canvas)
- **NOT** for controlling DxPdfViewer page or zoom
See `DEVEXPRESS_V25_LIMITATIONS.md` for complete verified API reference.
### JS Assets
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/wwwroot/js/pdf-viewer.js`
- `EnvelopeGenerator.Server/EnvelopeGenerator.Server/wwwroot/js/receiver-signature.js`