PDF 里的超链接本该是最省事的一环:点一下就到,不用复制、不用手打。可要加的时候,麻烦事不少。新建一份文档时希望它一开始就带上下载页、联系邮箱和产品页。拿到一份已经排好的旧文档,又希望正文里反复出现的产品名都指向同一页介绍。手工做法是一处一处框选、粘贴地址,文档一大就做不完。
把这件事挪到浏览器里做,用的是 Spire.PDF for JavaScript:它基于 WebAssembly 直接加载、修改与保存 PDF 文档,PdfTextWebLink 一步画出可见的链接文字,PdfFileLinkAnnotation 挂上文档链接。全过程在本地完成,通过虚拟文件系统(VFS)读写文件,无需后端配合。
本文介绍两个核心功能点:
有关安装和项目配置,请参考 React 项目中集成 Spire.PDF for JavaScript。以下示例默认已安装 Spire.PDF 并完成 WebAssembly 模块初始化。
在 PDF 文档中添加超链接
从零新建文档时,页面上的一切都由代码决定,链接也分了几类:
- 简单文字链接:把网址当普通文字画出来,代码不给它挂任何注解;能不能点由阅读器决定,Chrome 等会自动把网址识别成可点链接,所以它照样能点,这里只作与显式挂注解链接的对照。
- 超文本链接:
PdfTextWebLink配好Text、Url、Font、Brush,DrawTextWebLink()一步画出带下划线的链接文字。 - 文档链接:
PdfFileLinkAnnotation(热区矩形, 目标文件),热区要自己定义。
function App() {
const addLinksToNewPdf = async () => {
// 获取 Spire.PDF WASM 模块
const pdfModule = window.wasmModule?.spirepdf;
// 检查模块是否就绪
if (!pdfModule) {
alert('Spire.PDF is not ready yet');
return;
}
// 画中文要用字体,先载入 VFS
const fontVfs = '/Library/Fonts/ARIAL UNICODE MS.TTF';
await window.spire.FetchFileToVFS('ARIAL UNICODE MS.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
// 创建空白文档
const doc = new pdfModule.PdfDocument();
// 新建一个节,整节页边距设为 0:坐标即页面坐标
const section = doc.Sections.Add();
section.PageSettings.Size = pdfModule.PdfPageSize.A4();
const margins = new pdfModule.PdfMargins();
margins.Top = 0;
margins.Bottom = 0;
margins.Left = 0;
margins.Right = 0;
section.PageSettings.Margins = margins;
const page = section.Pages.Add();
const font = new pdfModule.PdfTrueTypeFont({ fontFile: fontVfs, size: 13 });
const linkFont = new pdfModule.PdfTrueTypeFont({ fontFile: fontVfs, size: 13, style: pdfModule.PdfFontStyle.Underline });
const format = new pdfModule.PdfStringFormat({ alignment: pdfModule.PdfTextAlignment.Left });
// 画一行普通文字当标签
const drawLabel = (text, y) => {
page.Canvas.DrawString({
s: text, font: font, brush: pdfModule.PdfBrushes.get_Black(), x: 60, y: y, format: format,
});
};
// 画一行带下划线的文字,并把它的矩形交回去当热区
const drawLinkText = (text, y) => {
const size = linkFont.MeasureString({ text: text });
page.Canvas.DrawString({
s: text, font: linkFont, brush: pdfModule.PdfBrushes.get_Blue(), x: 160, y: y, format: format,
});
return new pdfModule.RectangleF({ x: 160, y: y, width: size.Width, height: size.Height });
};
const url = 'https://www.e-iceblue.cn/Introduce/Spire-PDF-Javascript.html';
// 1) 简单文字链接:只把网址当普通文字画出来,不挂注解(可点与否取决于阅读器)
let y = 80;
drawLabel('简单文字链接:', y);
drawLinkText(url, y);
// 2) 超文本链接:DrawTextWebLink 一步画出文字并挂上链接
y += 34;
drawLabel('超文本链接:', y);
const webLink = new pdfModule.PdfTextWebLink();
webLink.Text = '访问官网';
webLink.Url = url;
webLink.Font = linkFont;
webLink.Brush = pdfModule.PdfBrushes.get_Blue();
webLink.DrawTextWebLink({ graphics: page.Canvas, location: new pdfModule.PointF(160, y) });
// 3) 邮件链接:同一个类,Url 换成 mailto:
y += 34;
drawLabel('邮件链接:', y);
const mailLink = new pdfModule.PdfTextWebLink();
mailLink.Text = '联系技术支持';
mailLink.Url = 'mailto:该Email地址已收到反垃圾邮件插件保护。要显示它您需要在浏览器中启用JavaScript。';
mailLink.Font = linkFont;
mailLink.Brush = pdfModule.PdfBrushes.get_Blue();
mailLink.DrawTextWebLink({ graphics: page.Canvas, location: new pdfModule.PointF(160, y) });
// 4) 文档链接:热区矩形 + 目标文件
y += 34;
drawLabel('文档链接:', y);
const fileName = 'Report.xlsx';
const fileBounds = drawLinkText('打开文件', y);
page.Annotations.Add(new pdfModule.PdfFileLinkAnnotation(fileBounds, fileName));
// 保存生成的文档
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 downloadUrl = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = downloadUrl;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(downloadUrl);
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>添加超链接</h1>
<button onClick={addLinksToNewPdf}>
开始创建
</button>
</div>
);
}
export default App;
新建文档中简单文字、超文本、邮件与文档四种链接均已就位:

给检索到的文字添加超链接
已有文档要加链接,做法反过来:先在文档里找位置,再往上面盖链接。PdfTextFinder 按关键字检索指定页面上的文字,每处命中都带一个包围矩形。拿它构造 PdfUriAnnotation 并加入页面注解集合,链接就精确落在原文上,不用自己去量坐标。
function App() {
const addHyperlinksToText = 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);
// 在该页检索关键字;Parameter 默认区分大小写,这里改成忽略大小写
const finder = new pdfModule.PdfTextFinder(page);
finder.Options.Parameter = pdfModule.TextFindParameter.IgnoreCase;
const finds = finder.Find('Aurora');
// 每一处命中都生成一个链接注解
const linkUrl = 'https://www.e-iceblue.cn/Introduce/Spire-PDF-Javascript.html';
for (let i = 0; i < finds.length; i++) {
// 命中项的包围矩形就是链接热区
const annotation = new pdfModule.PdfUriAnnotation({ rectangle: finds.get(i).Bounds[0] });
annotation.Uri = linkUrl;
// 把注解挂到页面注解集合上
page.Annotations.Add(annotation);
}
// 保存生成的文档
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 downloadUrl = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = downloadUrl;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(downloadUrl);
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>给检索到的文字添加超链接</h1>
<button onClick={addHyperlinksToText}>
开始添加
</button>
</div>
);
}
export default App;
文档中三处 Aurora 均已挂上超链接:

常见问题
正文里明明有这个字,Find() 却一处都没搜到
原因:Options.Parameter 默认是 TextFindParameter.None(区分大小写),检索 aurora 匹配不到 Aurora;而且 finder 只在构造时传入的那一页上检索。
解决:设成 IgnoreCase;要检索全文档就逐页各建一个 finder:
finder.Options.Parameter = pdfModule.TextFindParameter.IgnoreCase;
实测:Aurora 在第 1 页出现 3 处,区分大小写只能搜到 0 处。
链接加上了,打开文件却看不到任何提示
原因:这类链接注解只是点击热区,Spire 不为它生成外观流(/AP),实测 Chrome(PDFium)与 PyMuPDF(mupdf)都不给它画边框;设 Border/Color 也只写进注解字典(/Border [0 0 1]、/C [0 0 1]),页面上照样看不出来。
解决:链接照样可点。功能点 1 的超文本、邮件、简单文字、文档链接,文字都是代码自己画上去的,本就可见;功能点 2 盖在原文上的热区想看得见,别靠注解边框——在链接位置用 page.Canvas.DrawString() 画一条下划线。
获取免费许可证
如果您希望删除结果文档中的评估消息,或者摆脱功能限制,请该Email地址已收到反垃圾邮件插件保护。要显示它您需要在浏览器中启用JavaScript。获取有效期 30 天的临时许可证。







