本文详解如何在 pdf.js 的页面渲染流程中,在 canvas 上正确叠加自定义文本(如水印、页码或标注),重点解决因渲染覆盖导致文本消失的常见问题,并提供可立即运行的完整实现方案。
本文详解如何在 pdf.js 的页面渲染流程中,在 canvas 上正确叠加自定义文本(如水印、页码或标注),重点解决因渲染覆盖导致文本消失的常见问题,并提供可立即运行的完整实现方案。
在使用 pdf.js 渲染 PDF 页面时,开发者常希望在每页 Canvas 上添加动态文本(例如“机密”水印、“第 X 页”页眉、或调试标记)。但直接在 render() 调用前用 fillText() 绘制文本后,最终页面却不显示该文本——这并非代码语法错误,而是由 pdf.js 渲染机制的本质决定的:page.render() 方法会完全重绘整个 Canvas 内容,覆盖此前所有手动绘制操作。
? 根本原因:渲染顺序与 Canvas 覆盖
pdf.js 的 page.render() 并非“增量绘制”,而是将 PDF 页面内容作为位图(或矢量指令)全量写入目标 Canvas,等效于执行一次 clearRect(0, 0, width, height) + 重绘。因此,以下代码逻辑是无效的:
DMap GL地图JSAPI(dmapgl)开发指南。用于编写、审查、调试 DMap 地图 API 代码,涵盖地图初始化、覆盖物渲染、图层管理、事件处理、控件交互等任务。当用户提及 dmap、dmapgl、jsapi‑gl 或相关地图开发需求时自动触发。
gcc.fillText("Hello World", canvas.width/2, canvas.height/2); // ✅ 先画文字
page.render({ canvasContext: gcc, viewport }); // ❌ 后续渲染彻底覆盖!✅ 正确方案:在 render() 完成后二次绘制
必须等待 PDF 内容渲染完成(即 render() 返回的 Promise 解析后),再执行文本绘制。pdf.js 提供了 render() 的 Promise 返回值,这是关键切入点:
function renderPage(pageNumber, canvas) {
thePdf.getPage(pageNumber).then(function(page) {
const viewport = page.getViewport({ scale: scale });
canvas.height = viewport.height;
canvas.width = viewport.width;
const gcc = canvas.getContext('2d');
// Step 1: 渲染 PDF 原始内容
const renderTask = page.render({
canvasContext: gcc,
viewport: viewport
});
// Step 2: 在渲染完成后叠加文本(关键!)
renderTask.promise.then(() => {
gcc.font = "30px 'Comic Sans MS', sans-serif";
gcc.fillStyle = "#2563eb"; // 推荐深蓝色,比绿色更易读
gcc.textAlign = "center";
gcc.textBaseline = "middle"; // 避免垂直偏移计算误差
gcc.fillText(`Page ${pageNumber}`, canvas.width / 2, canvas.height / 2);
// ✅ 可扩展:添加旋转水印
gcc.save();
gcc.translate(canvas.width / 2, canvas.height / 2);
gcc.rotate(-Math.PI / 6); // -30度
gcc.font = "bold 48px sans-serif";
gcc.fillStyle = "rgba(0, 0, 0, 0.1)";
gcc.textAlign = "center";
gcc.textBaseline = "middle";
gcc.fillText("CONFIDENTIAL", 0, 0);
gcc.restore();
});
});
}⚠️ 注意事项与最佳实践
- 不要省略 renderTask.promise:直接在 page.render() 后同步调用 fillText() 仍会失败,必须使用 .then() 确保时序。
- 字体加载风险:若使用自定义字体(如 'Noto Sans CJK'),需确保字体已加载完成,否则可能回退为默认字体或渲染空白。建议优先使用系统安全字体栈。
- Canvas DPI 适配:pdf.js 默认按 CSS 像素渲染,若页面启用了高 DPI 缩放(如 Retina 屏),需通过 devicePixelRatio 手动调整 Canvas 内部分辨率,否则文本可能模糊。进阶方案可参考 getViewport({ scale: scale, intent: 'display' })。
- 性能考量:对长文档(>50页),避免在 renderPage 中执行复杂计算;水印类文本建议复用预渲染的 ImageBitmap 或离屏 Canvas。
-
替代方案(HTML 层叠):若仅需静态文本(如固定页眉),更推荐用绝对定位的 <div> 覆盖在 Canvas 上(需同步 Canvas 尺寸与 CSS transform):
<div class="pdf-page-overlay" style="position:absolute; top:10px; left:50%; transform:translateX(-50%); font-weight:bold; color:#dc2626;"> [DRAFT] — Page 1 </div>
✅ 总结
向 pdf.js 的 Canvas 添加文本的核心原则只有一条:文本绘制必须发生在 page.render().promise 解析之后。掌握这一时序逻辑,即可稳定实现水印、页码、调试标签等增强功能。文中示例已通过 pdf.js v2.16+ 实测验证,可直接集成至现有渲染流程,无需修改 pdf.js 源码或引入额外依赖。


















