Add text layer support for PDF rendering and selection

Integrated PDF.js to enable text selection and copy-paste functionality in the PDF viewer. Updated `EnvelopeViewer.razor` to include the necessary scripts and styles, and modified the HTML structure to add a text layer container.

Enhanced `envelope-viewer.css` with styles for the text layer and optimized canvas rendering. Added a `renderTextLayer` method in `pdf-viewer.js` to extract and render text content from PDF pages. Updated the rendering process to overlay the text layer on the canvas.
This commit is contained in:
2026-06-06 16:13:32 +02:00
parent fc267e1eb4
commit d32050ce03
3 changed files with 77 additions and 2 deletions

View File

@@ -13,6 +13,7 @@
<link href="_content/DevExpress.Blazor.Themes/blazing-berry.bs5.min.css" rel="stylesheet" />
<link href="css/envelope-viewer.css" rel="stylesheet" />
<link href="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf_viewer.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js"></script>
<script src="js/pdf-viewer.js"></script>
@@ -151,7 +152,10 @@
</div>
}
<div class="pdf-canvas-wrapper">
<canvas id="pdf-canvas" class="pdf-canvas"></canvas>
<div class="pdf-page-container">
<canvas id="pdf-canvas" class="pdf-canvas"></canvas>
<div id="pdf-text-layer" class="pdf-text-layer"></div>
</div>
</div>
</div>
</div>

View File

@@ -434,9 +434,14 @@ body.resizing {
justify-content: flex-start;
}
.pdf-canvas {
.pdf-page-container {
position: relative;
display: inline-block;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.pdf-canvas {
display: block;
vertical-align: top;
image-rendering: -webkit-optimize-contrast;
image-rendering: crisp-edges;
@@ -447,6 +452,34 @@ body.resizing {
opacity: 0;
}
.pdf-text-layer {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
overflow: hidden;
opacity: 1;
line-height: 1.0;
pointer-events: auto;
}
.pdf-text-layer > span {
color: transparent;
position: absolute;
white-space: pre;
cursor: text;
transform-origin: 0% 0%;
}
.pdf-text-layer ::selection {
background: rgba(126, 34, 206, 0.3);
}
.pdf-text-layer ::-moz-selection {
background: rgba(126, 34, 206, 0.3);
}
.error-container {
display: flex;
align-items: center;

View File

@@ -176,6 +176,9 @@ window.pdfViewer = {
this.currentRenderTask = page.render(renderContext);
await this.currentRenderTask.promise;
// Render text layer for copy-paste functionality
await this.renderTextLayer(page, viewport, dpr);
// Restore viewport center position AFTER rendering
if (container && oldWidth > 0 && oldHeight > 0) {
const scaleX = this.canvas.width / oldWidth;
@@ -210,6 +213,41 @@ window.pdfViewer = {
}
},
async renderTextLayer(page, viewport, dpr) {
try {
const textLayerDiv = document.getElementById('pdf-text-layer');
if (!textLayerDiv) {
console.warn('Text layer div not found');
return;
}
// Clear previous text layer
textLayerDiv.innerHTML = '';
// Set text layer dimensions to match canvas display size
textLayerDiv.style.width = `${viewport.width / dpr}px`;
textLayerDiv.style.height = `${viewport.height / dpr}px`;
// Get text content from PDF
const textContent = await page.getTextContent();
// Create viewport for text layer (without DPR scaling for positioning)
const textViewport = page.getViewport({ scale: this.scale });
// Render text layer using PDF.js built-in function
const pdfjsLib = window.pdfjsLib;
await pdfjsLib.renderTextLayer({
textContentSource: textContent,
container: textLayerDiv,
viewport: textViewport,
textDivs: []
}).promise;
} catch (error) {
console.error('Error rendering text layer:', error);
}
},
queueRenderPage(num) {
if (this.pageRendering) {
this.pageNumPending = num;