This commit is contained in:
2026-01-28 17:02:52 +00:00
parent 7e50b8cb3f
commit 2b048cf34f
2 changed files with 81 additions and 8 deletions

View File

@@ -515,6 +515,9 @@ export class DeesPdfViewer extends DeesElement {
textLayerDiv.style.width = `${viewport.width}px`;
textLayerDiv.style.height = `${viewport.height}px`;
// Set the scale factor CSS variable - required by PDF.js text layer
textLayerDiv.style.setProperty('--scale-factor', String(viewport.scale));
const textLayerRenderTask = pdfjs.renderTextLayer({
textContentSource: textContent,
container: textLayerDiv,
@@ -529,14 +532,80 @@ export class DeesPdfViewer extends DeesElement {
endOfContent.className = 'endOfContent';
textLayerDiv.appendChild(endOfContent);
// Selection class handling
textLayerDiv.addEventListener('mousedown', () => {
textLayerDiv.classList.add('selecting');
const onMouseUp = () => {
// Custom drag selection for Shadow DOM compatibility
// caretRangeFromPoint doesn't pierce shadow DOM, so we find spans manually
let isDragging = false;
let anchorNode: Node | null = null;
let anchorOffset = 0;
const getTextPositionFromPoint = (x: number, y: number): { node: Node; offset: number } | null => {
// Find span at coordinates by checking bounding rects
const spans = Array.from(textLayerDiv.querySelectorAll('span'));
for (const span of spans) {
const rect = span.getBoundingClientRect();
if (x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom) {
const textNode = span.firstChild;
if (textNode && textNode.nodeType === Node.TEXT_NODE) {
// Calculate character offset based on x position
const text = textNode.textContent || '';
const charWidth = rect.width / text.length;
const relativeX = x - rect.left;
const offset = Math.min(Math.round(relativeX / charWidth), text.length);
return { node: textNode, offset };
}
}
}
return null;
};
const handleMouseUp = () => {
if (isDragging) {
isDragging = false;
anchorNode = null;
textLayerDiv.classList.remove('selecting');
document.removeEventListener('mouseup', onMouseUp);
};
document.addEventListener('mouseup', onMouseUp);
}
document.removeEventListener('mouseup', handleMouseUp);
document.removeEventListener('mousemove', handleMouseMove);
};
const handleMouseMove = (e: MouseEvent) => {
if (!isDragging || !anchorNode) return;
e.preventDefault();
const pos = getTextPositionFromPoint(e.clientX, e.clientY);
if (pos) {
const selection = window.getSelection();
if (selection) {
try {
selection.setBaseAndExtent(anchorNode, anchorOffset, pos.node, pos.offset);
} catch (err) {
// Ignore errors from invalid selections
}
}
}
};
textLayerDiv.addEventListener('mousedown', (e: MouseEvent) => {
if (e.button !== 0) return;
const pos = getTextPositionFromPoint(e.clientX, e.clientY);
if (pos) {
// Prevent native selection behavior
e.preventDefault();
isDragging = true;
anchorNode = pos.node;
anchorOffset = pos.offset;
textLayerDiv.classList.add('selecting');
// Clear existing selection
const selection = window.getSelection();
selection?.removeAllRanges();
// Add document-level listeners for drag
document.addEventListener('mousemove', handleMouseMove);
document.addEventListener('mouseup', handleMouseUp);
}
});
pageInfo.textLayerRendered = true;

View File

@@ -289,12 +289,14 @@ export const viewerStyles = [
.text-layer {
position: absolute;
inset: 0;
overflow: clip;
overflow: visible;
line-height: 1;
text-size-adjust: none;
forced-color-adjust: none;
transform-origin: 0 0;
z-index: 1;
user-select: text;
-webkit-user-select: text;
}
.text-layer span,
@@ -304,6 +306,8 @@ export const viewerStyles = [
white-space: pre;
cursor: text;
transform-origin: 0% 0%;
user-select: text;
-webkit-user-select: text;
}
.text-layer ::selection {