PDF 里的文字看着能选中,真要成批取出来却不顺手:合同里的条款、报表里的数字、说明书里的参数,散在一页页的版式里,选不全、复制下来还带着错位的空格和换行,更没法直接拿去做检索、比对或入库。
Spire.PDF for JavaScript 基于 WebAssembly 在浏览器端加载并解析 PDF 文档,通过虚拟文件系统(VFS)读写文件。本文用 PdfTextExtractor 来实现四种提取:保留布局、不保留布局、只取指定区域,以及只取高亮批注覆盖的文字。
本文介绍四个核心功能点:
有关安装和项目配置,请参考 React 项目中集成 Spire.PDF for JavaScript。以下示例默认已安装 Spire.PDF 并完成 WebAssembly 模块初始化。
提取保留布局的 PDF 文本
Spire.PDF for JavaScript 提供 PdfTextExtractor,用于把页面上绘制的文字取回成字符串。不设置任何选项时,它按页面原有的列位置与行距还原文本,表格的列、段落的缩进都会保留下来。提取以页为单位,多页文档要逐页处理再拼接。
提取行为由 PdfTextExtractOptions 控制,常用属性如下:
| 属性 | 说明 |
|---|---|
IsSimpleExtraction |
指定是否执行简单文本提取,即不保留版面布局。 |
IsExtractAllText |
指定是否提取全部文本。 |
ExtractArea |
定义提取区域,用 RectangleF 圈定。 |
IsShowHiddenText |
指定是否提取隐藏文本。 |
function App() {
const extractTextWithLayout = 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 options = new pdfModule.PdfTextExtractOptions();
// 逐页提取后拼接
let text = "";
for (let i = 0; i < doc.Pages.Count; i++) {
const page = doc.Pages.get_Item(i);
const extractor = new pdfModule.PdfTextExtractor(page);
text += extractor.ExtractText(options);
}
// 写入 VFS,再从 VFS 读回触发下载
const outputFileName = '保留布局文本.txt';
window.dotnetRuntime.Module.FS.writeFile(outputFileName, text);
doc.Close();
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'text/plain' });
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>提取保留布局的 PDF 文本</h1>
<button onClick={extractTextWithLayout}>开始提取</button>
</div>
);
}
export default App;
按原始排版提取出的文本,表格列与缩进保持不变:

提取不保留布局的 PDF 文本
只要文字本身、不要版式时,把 PdfTextExtractOptions.IsSimpleExtraction 设为 true,提取就切到简单模式:不再计算列位置,文字按阅读顺序连成一段,适合拿去分词、检索或入库。代价是原本并排的两栏会串到一起,表格也不再对齐。
function App() {
const extractTextWithoutLayout = 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 options = new pdfModule.PdfTextExtractOptions();
options.IsSimpleExtraction = true;
// 逐页提取后拼接
let text = "";
for (let i = 0; i < doc.Pages.Count; i++) {
const page = doc.Pages.get_Item(i);
const extractor = new pdfModule.PdfTextExtractor(page);
text += extractor.ExtractText(options);
}
// 写入 VFS,再从 VFS 读回触发下载
const outputFileName = '纯文本.txt';
window.dotnetRuntime.Module.FS.writeFile(outputFileName, text);
doc.Close();
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'text/plain' });
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>提取不保留布局的 PDF 文本</h1>
<button onClick={extractTextWithoutLayout}>开始提取</button>
</div>
);
}
export default App;
简单模式提取出的文本,文字按阅读顺序连成一段:

提取 PDF 指定区域的文本
需要只取页面上某一块的文字时,用 PdfTextExtractOptions.ExtractArea 圈定范围。它接收一个 RectangleF,坐标以页面左上角为原点、单位为磅,只有落在这个矩形里的文字会被取出,其余内容一律丢弃。
function App() {
const extractTextFromArea = 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 page = doc.Pages.get_Item(0);
// 圈定提取范围:页面左上角为原点,单位为磅
const options = new pdfModule.PdfTextExtractOptions();
options.ExtractArea = new pdfModule.RectangleF({ x: 80, y: 180, width: 500, height: 200 });
const extractor = new pdfModule.PdfTextExtractor(page);
const text = extractor.ExtractText(options);
// 写入 VFS,再从 VFS 读回触发下载
const outputFileName = '区域文本.txt';
window.dotnetRuntime.Module.FS.writeFile(outputFileName, text);
doc.Close();
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'text/plain' });
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>提取 PDF 指定区域的文本</h1>
<button onClick={extractTextFromArea}>开始提取</button>
</div>
);
}
export default App;
只取到矩形范围内的文本:

提取 PDF 中的高亮文本
如果文档里已经用荧光笔划出了重点,就没必要再手工圈坐标:遍历页面的 Annotations,挑出 PdfTextMarkupAnnotationWidget,把它的 Bounds 直接交给 ExtractArea,被标注的文字就取出来了。同一个对象上还能读到 TextMarkupColor,用来判断是哪一种标记颜色。
function App() {
const extractHighlightedText = 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 page = doc.Pages.get_Item(0);
const extractor = new pdfModule.PdfTextExtractor(page);
let text = "";
// 逐个检查页面上的批注,只处理文本标注类
for (let i = 0; i < page.Annotations.Count; i++) {
const annotation = page.Annotations.get_Item(i);
if (annotation instanceof pdfModule.PdfTextMarkupAnnotationWidget) {
// 用批注矩形作为提取范围,只取被标注的文字
const options = new pdfModule.PdfTextExtractOptions();
options.ExtractArea = annotation.Bounds;
text += extractor.ExtractText(options) + "\n";
// 高亮颜色:annotation.TextMarkupColor
}
}
// 写入 VFS,再从 VFS 读回触发下载
const outputFileName = '高亮文本.txt';
window.dotnetRuntime.Module.FS.writeFile(outputFileName, text);
doc.Close();
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'text/plain' });
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>提取 PDF 中的高亮文本</h1>
<button onClick={extractHighlightedText}>开始提取</button>
</div>
);
}
export default App;
只取到高亮批注覆盖的文字:

常见问题
提取出来的中文是乱码或方框
原因:PDF 里存的是字形编号,不是 Unicode 字符,需要文档自带 ToUnicode 映射表才能还原成可读文字。扫描件、或者生成时没嵌入字体的文档往往缺这张表,ExtractText 只能拿到编号,输出自然是乱码或方框。
解决:先判断文档类型。文字型 PDF 且字体已内嵌时能正常提取;扫描件和缺映射的文档走不了这条路,需要先经 OCR 转成带文字的 PDF,再来提取。
指定区域提取时结果为空
原因:ExtractArea 的坐标口径是页面左上角为原点、单位为磅,如果按 PDF 原生坐标(左下角原点)或屏幕像素去设,矩形会落到没有文字的位置,取出来就是空串。
解决:换成左上原点、单位磅的矩形再传入;不清楚目标文字具体位置时,先把整页设成范围确认能取到,再逐步收窄:
// A4 页面,单位磅,先取整页确认,再逐步收窄矩形
options.ExtractArea = new pdfModule.RectangleF({ x: 0, y: 0, width: 595, height: 842 });
文档里明明有高亮,却提取不到文本
原因:PdfTextMarkupAnnotationWidget 只匹配文本标注类批注(高亮、下划线、波浪线、删除线)。如果文档里的“高亮”其实是用形状、文本框或半透明图片叠出来的,批注集合里根本没有这个类型,循环体一次都不会进。
解决:先确认页面上到底有没有批注,以及是哪种类型:
for (let i = 0; i < page.Annotations.Count; i++) {
const annotation = page.Annotations.get_Item(i);
console.log(annotation instanceof pdfModule.PdfTextMarkupAnnotationWidget);
}
Count 为 0 说明高亮不是批注;类型不匹配则改用对应的批注类判断,或者改回上一节的 ExtractArea,手工圈坐标提取。
获取免费许可证
如果您希望删除结果文档中的评估消息,或者摆脱功能限制,请该Email地址已收到反垃圾邮件插件保护。要显示它您需要在浏览器中启用JavaScript。获取有效期 30 天的临时许可证。







