一份几十页的合同或报告交到手上,要确认某条条款、某个金额出现过几次、都出现在哪里,靠眼睛翻页容易漏。把命中的文字标出来是最省事的做法,但桌面软件里的查找高亮很难嵌进 Web 流程,逐页截图再标注也不现实。
Spire.PDF for JavaScript 基于 WebAssembly 在浏览器端加载、处理与保存 PDF 文档,查找与高亮全部在本地完成,通过虚拟文件系统(VFS)读写文件,无需后端配合。本文用 PdfTextFinder 来实现三种查找高亮方式:全篇高亮、限定区域内高亮、按正则表达式高亮。
本文介绍三个核心功能点:
有关安装和项目配置,请参考 React 项目中集成 Spire.PDF for JavaScript。以下示例默认已安装 Spire.PDF 并完成 WebAssembly 模块初始化。
查找并高亮全部匹配文本
PdfTextFinder 用于在页面的文本层里定位指定文本。它按页工作,遍历文档的每一页各建一个 finder,就能把整份文档里的匹配项一次找全;命中的每一处调用 HighLight() 即完成高亮,默认是黄色,需要区分不同关键词时再传入颜色。
function App() {
const findAndHighlightAll = async () => {
// 获取 Spire.PDF WASM 模块
const pdfModule = window.wasmModule?.spirepdf;
// 检查模块是否就绪
if (!pdfModule) {
alert('Spire.PDF is not ready yet');
return;
}
// 将待处理的 PDF 文件载入 VFS
const inputFileName = '花卉.pdf';
await window.spire.FetchFileToVFS(inputFileName, "", `${process.env.PUBLIC_URL}/data/`);
// 创建 PdfDocument 对象并加载 PDF 文档
let doc = new pdfModule.PdfDocument();
doc.LoadFromFile(inputFileName);
// 逐页查找,命中的每一处都加上高亮
for (let i = 0; i < doc.Pages.Count; i++) {
const finder = new pdfModule.PdfTextFinder(doc.Pages.get_Item(i));
finder.Options.Parameter = pdfModule.TextFindParameter.IgnoreCase;
const finds = finder.Find('观赏');
for (let j = 0; j < finds.length; j++) {
finds.get(j).HighLight();
}
}
// 保存文档
const outputFileName = '查找高亮.pdf';
doc.SaveToFile(outputFileName);
doc.Close();
// 从 VFS 读取生成的文件,触发下载
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/pdf' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>查找并高亮全部匹配文本</h1>
<button onClick={findAndHighlightAll}>
开始查找并高亮
</button>
</div>
);
}
export default App;
整份文档里的 观赏 全部被高亮:

在指定区域内查找并高亮
页面上的同名文字往往只有一部分需要标注,PdfTextFinder 还提供 Options.Area,把查找范围收进一个矩形,落在框外的匹配不会返回,也就不会被高亮。矩形用页面坐标描述,原点在页面左上角、单位是磅。
function App() {
const findAndHighlightInArea = async () => {
// 获取 Spire.PDF WASM 模块
const pdfModule = window.wasmModule?.spirepdf;
// 检查模块是否就绪
if (!pdfModule) {
alert('Spire.PDF is not ready yet');
return;
}
// 将待处理的 PDF 文件载入 VFS
const inputFileName = '花卉.pdf';
await window.spire.FetchFileToVFS(inputFileName, "", `${process.env.PUBLIC_URL}/data/`);
// 创建 PdfDocument 对象并加载 PDF 文档
let doc = new pdfModule.PdfDocument();
doc.LoadFromFile(inputFileName);
// 指定查找范围:页面坐标,原点在左上角,单位磅
const area = new pdfModule.RectangleF({ x: 60, y: 488, width: 420, height: 160 });
const finder = new pdfModule.PdfTextFinder(doc.Pages.get_Item(0));
finder.Options.Parameter = pdfModule.TextFindParameter.IgnoreCase;
finder.Options.Area = area;
// 只有落在矩形内的匹配会被返回
const finds = finder.Find('观赏');
for (let j = 0; j < finds.length; j++) {
finds.get(j).HighLight();
}
// 保存文档
const outputFileName = '区域高亮.pdf';
doc.SaveToFile(outputFileName);
doc.Close();
// 从 VFS 读取生成的文件,触发下载
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/pdf' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>在指定区域内查找并高亮</h1>
<button onClick={findAndHighlightInArea}>
开始查找并高亮
</button>
</div>
);
}
export default App;
只有对照表区域内的 观赏 被高亮,正文与列表里的保持不变:

用正则表达式查找并高亮
要找的目标未必是固定的几个字。Options.Parameter 决定匹配规则,取 Regex 时 Find() 的入参就是一个正则表达式,形态相同而内容各异的目标可以用一条模式一次圈出,默认取值按子串匹配,也就是前面两节的效果,同一枚举里还有 IgnoreCase、WholeWord 可用。
function App() {
const findAndHighlightByRegex = async () => {
// 获取 Spire.PDF WASM 模块
const pdfModule = window.wasmModule?.spirepdf;
// 检查模块是否就绪
if (!pdfModule) {
alert('Spire.PDF is not ready yet');
return;
}
// 将待处理的 PDF 文件载入 VFS
const inputFileName = '花卉.pdf';
await window.spire.FetchFileToVFS(inputFileName, "", `${process.env.PUBLIC_URL}/data/`);
// 创建 PdfDocument 对象并加载 PDF 文档
let doc = new pdfModule.PdfDocument();
doc.LoadFromFile(inputFileName);
// 逐页按正则表达式匹配,命中的每一处都加上高亮
for (let i = 0; i < doc.Pages.Count; i++) {
const finder = new pdfModule.PdfTextFinder(doc.Pages.get_Item(i));
finder.Options.Parameter = pdfModule.TextFindParameter.Regex;
const finds = finder.Find('图\\s*\\d');
for (let j = 0; j < finds.length; j++) {
finds.get(j).HighLight({ color: pdfModule.Color.get_Orange() });
}
}
// 保存文档
const outputFileName = '正则高亮.pdf';
doc.SaveToFile(outputFileName);
doc.Close();
// 从 VFS 读取生成的文件,触发下载
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/pdf' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>用正则表达式查找并高亮</h1>
<button onClick={findAndHighlightByRegex}>
开始查找并高亮
</button>
</div>
);
}
export default App;
三处图注被模式 图\s*\d 命中并高亮为橙色:

常见问题
高亮生效了,但在阅读器的注释面板里找不到它
原因:HighLight() 走的是页面内容,不是 PDF 注释。高亮色块在保存时被写进页面的内容流,文件体积只增大约 2 KB 量级,产物里并不会多出注释对象——用 PyMuPDF 读回 page.annots() 得到的是空集。
解决:把高亮当作页面图形看待即可,显示效果与标注一致;只是它没有注释身份,不能像批注那样在阅读器里逐条选中、删除或改色。需要按注释管理高亮时,应在保存前记下命中的位置,由业务侧自行维护这份清单。
设置了查找区域,却一处都没高亮
原因:Options.Area 用的是页面坐标(原点在页面左上角、单位磅),矩形写小了、位置偏了,匹配就全部落在框外。它也只在当前页生效——多页文档要把同一个矩形套到目标页的 finder 上。
解决:先按整页坐标量出目标区域,再往里收。下面这份样例里的对照表落在 x≈60–480、y≈488–648 之间,用 RectangleF({ x: 60, y: 488, width: 420, height: 160 }) 正好框住表格,矩形外的正文与列表都不会被匹配:
// 只查当前页,且只在这个矩形内匹配
const finder = new pdfModule.PdfTextFinder(doc.Pages.get_Item(0));
finder.Options.Area = new pdfModule.RectangleF({ x: 60, y: 488, width: 420, height: 160 });
拿不准坐标时,可以先不设 Area 查一遍,从命中的 finds.get(i).Bounds[0] 读出实际位置再反过来定矩形。
同一个正则,中文文档能匹配,换日文文档就失效
原因:正则匹配的是 PDF 文本层里的实际字符,不是语义。三份样例里表示区间的连字符并不一致:中文与英文用短横线 –(U+2013),日文用全角波浪线 ~(U+FF5E),只写一种模式就只能在一种文档上命中。
解决:把连字符写成字符组,一次兼容两种写法:
// 数字区间:6–9 月 / 1–3 m / 6~9月 都能匹配
finder.Options.Parameter = pdfModule.TextFindParameter.Regex;
const finds = finder.Find('[0-9]+\\s*[–-~]\\s*[0-9]+');
获取免费许可证
如果您希望删除结果文档中的评估消息,或者摆脱功能限制,请该Email地址已收到反垃圾邮件插件保护。要显示它您需要在浏览器中启用JavaScript。获取有效期 30 天的临时许可证。







