如何基于react-pdf实现PDF内容高亮标注功能?
实现React-PDF的文本高亮标注功能
核心思路
react-pdf本身没有内置标注API,但可以通过它的onTextRender回调捕获每个文本块的位置与内容,再在PDF渲染容器的上层叠加绝对定位的高亮元素,配合按钮事件控制高亮的显示范围。
具体实现步骤
1. 基础结构搭建
先完成PDF加载和按钮布局的基础代码:
import { Document, Page } from 'react-pdf'; import { useState, useRef } from 'react'; export default function PDFHighlighter() { const [numPages, setNumPages] = useState(null); const [currentPage, setCurrentPage] = useState(1); const [highlightType, setHighlightType] = useState(null); // 存储当前高亮类型:'title'/'author'/null const [textBlocks, setTextBlocks] = useState([]); // 存储所有文本块数据 const pdfContainerRef = useRef(null); // PDF加载完成回调 function onDocumentLoadSuccess({ numPages }) { setNumPages(numPages); } // 捕获文本渲染时的元数据 function onTextRender(textItem) { // 收集每个文本块的关键信息:内容、位置、页码 const blockData = { text: textItem.str, position: textItem.transform, // [缩放X, 倾斜, 倾斜, 缩放Y, 偏移X, 偏移Y] pageNumber: currentPage, width: textItem.width, height: textItem.height, fontSize: textItem.fontSize // 可用于优化匹配规则 }; setTextBlocks(prev => [...prev, blockData]); } return ( <div> <div className="controls"> <button onClick={() => setHighlightType('title')}>高亮标题</button> <button onClick={() => setHighlightType('author')}>高亮作者</button> <button onClick={() => setHighlightType(null)}>取消高亮</button> </div> <div ref={pdfContainerRef} className="pdf-container"> <Document file="your-pdf-file.pdf" onLoadSuccess={onDocumentLoadSuccess} > <Page pageNumber={currentPage} onTextRender={onTextRender} scale={1.5} // 可根据需求调整缩放比例 /> </Document> {/* 高亮元素将渲染在这里 */} {renderHighlights()} </div> <div className="pagination"> <button onClick={() => { setTextBlocks([]); setCurrentPage(prev => Math.max(prev - 1, 1)); }} disabled={currentPage === 1}>上一页</button> <span>第 {currentPage} 页 / 共 {numPages} 页</span> <button onClick={() => { setTextBlocks([]); setCurrentPage(prev => Math.min(prev + 1, numPages)); }} disabled={currentPage === numPages}>下一页</button> </div> </div> ); }
2. 实现高亮元素渲染
添加renderHighlights函数,根据当前高亮类型筛选匹配的文本块,计算绝对位置后渲染高亮层:
function renderHighlights() { if (!highlightType || !pdfContainerRef.current) return null; // 定义匹配规则:根据实际PDF内容调整 const matchRule = { title: (block) => { // 示例:匹配第一页、字号大于16、内容长度符合标题特征的文本 return block.pageNumber === 1 && block.fontSize > 16 && block.text.length > 10; }, author: (block) => { // 示例:匹配包含"作者"或"By:"关键词的文本 return block.text.includes('作者') || block.text.startsWith('By: '); } }; // 筛选当前页需要高亮的文本块 const filteredBlocks = textBlocks.filter(block => { return block.pageNumber === currentPage && matchRule[highlightType](block); }); return filteredBlocks.map((block, index) => { // 从transform数组中提取偏移量和缩放比例,计算绝对位置 const [scaleX, , , scaleY, offsetX, offsetY] = block.position; return ( <div key={index} className="highlight-box" style={{ position: 'absolute', left: `${offsetX * scaleX}px`, top: `${offsetY * scaleY}px`, width: `${block.width * scaleX}px`, height: `${block.height * scaleY}px`, backgroundColor: 'rgba(255, 255, 0, 0.3)', // 半透明黄色高亮 pointerEvents: 'none', // 不干扰PDF交互 zIndex: 10 }} /> ); }); }
3. 样式补充
给容器和高亮层添加基础样式:
.pdf-container { position: relative; overflow: auto; max-width: 100%; } .highlight-box { border-radius: 2px; } .controls { margin-bottom: 16px; gap: 8px; display: flex; } .pagination { margin-top: 16px; display: flex; gap: 16px; align-items: center; }
关键注意事项
- 文本块匹配规则:需要根据你的PDF实际内容调整
matchRule里的判断逻辑,比如结合文本的字号、位置、关键词等维度优化匹配精度。 - 页码切换处理:切换页码时必须清空旧的文本块数据,避免跨页高亮混乱,代码中已在分页按钮点击时重置
textBlocks。 - 缩放适配:如果允许用户调整PDF缩放比例,需要把
scale存在state中,计算高亮位置时使用当前的scale值。 - 长文本拆分:如果标题/作者被拆分成多个文本块,需要合并相邻的匹配块,避免出现多个分散的高亮框(可通过比较文本块的位置坐标实现合并)。
内容的提问来源于stack exchange,提问作者KAMRUL HASAN
相关产品推荐
相关产品推荐

