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:
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user