You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让PDF.js渲染的PDF文本支持选中功能?

解决PDF.js模态框中PDF文本不可选中的问题

在模态框中使用PDF.js渲染PDF后,无法选中文本以提取内容,现有代码已尝试添加文本层但未生效,以下是具体解决思路和修正后的代码。

核心解决思路

  • 修正文本层定位:给.modal-body添加position: relative,让绝对定位的.textLayer能相对于模态框内容区域正确定位,避免脱离可视范围。
  • 补充文本层必要样式:添加PDF.js文本层所需的基础样式,让文本透明显示但保持可选中状态,同时确保文本块的排版与Canvas渲染内容对齐。
  • 异步处理文本内容:等待page.getTextContent()的Promise完成后再渲染文本层,确保传入有效的文本数据。
  • 清理旧文本层:每次切换页面时清空.textLayer,避免旧内容残留干扰新页面的文本选择。
  • 移除无效设置:删除对Canvas的userSelect设置,因为文本选中依赖于上层的文本层而非Canvas本身。

修正后的完整代码

{% extends 'base.html' %}

{% block title %}Get Started{% endblock %}

{% block content %}
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL" crossorigin="anonymous"></script>

<style>
    #get-started-page {
        text-align: center;
        margin-top: 50px; /* Add margin-top for space */
    }
    .get-started-card {
        margin: auto;
        margin-bottom: 50px;
        padding: 20px;
        text-align: left; /* Align text to the left */
    }
    .get-started-btn {
        margin-top: 10px; /* Add margin-top for space below the input */
    }
    .modal-body {
        display: flex;
        align-items: center;
        justify-content: center;
        height: 80vh; /* Adjust the height of the modal body */
        overflow-y: auto; /* Enable vertical scrolling if content exceeds the modal height */
        position: relative; /* 新增:让文本层相对此容器定位 */
    }

    #the-canvas {
        max-width: 100%; /* Make sure the canvas doesn't exceed its container */
        max-height: 100%; /* Make sure the canvas doesn't exceed its container */
    }

    .textLayer {
        position: absolute;
        top: 0;
        left: 0;
        height: 100%;
        width: 100%;
        --scale-factor: 1; /* Set the --scale-factor CSS variable */
        pointer-events: none; /* 让文本层不阻挡Canvas的交互,但文本仍可选中 */
    }

    /* 新增:PDF.js文本层必要样式 */
    .textLayer > div {
        color: transparent; /* 文本透明,只保留选中功能 */
        position: absolute;
        white-space: pre;
        cursor: text;
        pointer-events: auto; /* 允许文本块接收鼠标事件 */
        transform-origin: 0% 0%;
    }

    .textLayer ::selection {
        background: rgba(0, 0, 255, 0.3); /* 选中时的高亮样式 */
    }
</style>

<section id="get-started-page">
    <div class="container">
        <div class="row justify-content-center">
            <div class="col-md-8">
                <div class="card bg-dark text-white transparent-card get-started-card">
                    <div class="card-body">
                        <h2 class="text-center mb-4">Get Started with IntelliQuest</h2>
                        <div class="mb-3 centerrrrr">
                            <label class="form-label">Upload PDF</label>
                            <div>
                                <input type="file" accept=".pdf" id="pdfInput" />
                            </div>
                            <button type="button" class="btn btn-primary get-started-btn" id="uploadButton">Upload PDF</button>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</section>

<!-- Bootstrap Modal -->
<div class="modal fade" id="pdfModal" tabindex="-1" aria-labelledby="pdfModalLabel" aria-hidden="true">
    <div class="modal-dialog modal-lg">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title" id="pdfModalLabel">Full-size PDF (Page 1 of 1)</h5>
                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
            </div>
            <div class="modal-body">
                <canvas id="the-canvas"></canvas>
                <div class="textLayer"></div>
            </div>
            <!-- Add navigation buttons inside the modal -->
            <div class="modal-footer">
                <button type="button" class="btn btn-secondary" id="prevPageBtn">Previous Page</button>
                <button type="button" class="btn btn-secondary" id="nextPageBtn">Next Page</button>
            </div>
        </div>
    </div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.js" integrity="sha512-dfMpvQclalfL7nRtHdy4+U2GLYb2XJJOgGLgKibrbcbarI/ZLgCAaBCS6+AuWN0OtLn/zFpu+Cggd8TCBYx9Ag==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

<script>
    // Set the worker source for PDF.js
    pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.js';

    function handleUpload() {
        var fileInput = document.getElementById('pdfInput');
        var uploadButton = document.getElementById('uploadButton');

        uploadButton.addEventListener('click', function () {
            var file = fileInput.files[0];

            if (file) {
                var pdfUrl = URL.createObjectURL(file);

                loadPdf(pdfUrl);
            } else {
                alert('Please select a PDF file for upload.');
            }
        });
    }

    function loadPdf(url) {
        pdfjsLib.getDocument(url).promise.then(function (pdfDoc_) {
            pdfDoc = pdfDoc_;
            var canvas = document.getElementById('the-canvas');
            var textLayer = document.querySelector(".textLayer");
            var ctx = canvas.getContext('2d');
            var scale = 1;
            var numPages = pdfDoc.numPages;
            var pageNum = 1;

            function renderPage(num) {
                pdfDoc.getPage(num).then(function (page) {
                    var viewport = page.getViewport({ scale: scale });
                    canvas.height = viewport.height;
                    canvas.width = viewport.width;

                    var renderContext = {
                        canvasContext: ctx,
                        viewport: viewport
                    };
                    page.render(renderContext);

                    // 清空旧文本层内容
                    textLayer.innerHTML = '';

                    // 异步获取文本内容后渲染文本层
                    page.getTextContent().then(function(textContent) {
                        pdfjsLib.textLayer.render({
                            textContent: textContent,
                            container: textLayer,
                            viewport: viewport,
                            textDivs: []
                        });
                    });

                    // 更新模态框标题的页码
                    document.getElementById('pdfModalLabel').textContent = `Full-size PDF (Page ${num} of ${numPages})`;
                });
            }

            renderPage(pageNum);

            document.getElementById('prevPageBtn').addEventListener('click', function () {
                if (pageNum > 1) {
                    pageNum--;
                    renderPage(pageNum);
                }
            });

            document.getElementById('nextPageBtn').addEventListener('click', function () {
                if (pageNum < numPages) {
                    pageNum++;
                    renderPage(pageNum);
                }
            });

            var pdfModal = new bootstrap.Modal(document.getElementById('pdfModal'));
            pdfModal.show();
        });
    }

    handleUpload();
</script>
{% endblock %}

额外说明

修正后,你可以直接选中PDF上的文本,若要将选中内容提取到控制台,可添加如下监听事件(放在handleUpload函数之后或合适位置):

document.addEventListener('mouseup', function() {
    var selectedText = window.getSelection().toString().trim();
    if (selectedText) {
        console.log('选中的文本:', selectedText);
    }
});

内容的提问来源于stack exchange,提问作者ItsCoder1092

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 02:35:57