PDF 版式固定、便于分发,但其内容一经生成便不易在正文中修改。对于合同、报表、公告等文档,通常需要在不影响正文阅读的前提下标识机密等级、版权归属或“草稿”“样例”等使用状态。除了上一类常见的文字水印,使用公司 logo、印章或警示图片制作的图片水印也颇为常见:它以半透明的图片浮于内容之上,既能清晰传达品牌与状态信息,又不会破坏原文的可读性。
Spire.PDF for JavaScript 基于 WebAssembly 在浏览器端直接完成 PDF 的加载、绘制与保存,通过虚拟文件系统(VFS)管理输入输出文件,无需后端服务支持。添加图片水印通常有两种形态:一种是在页面指定位置(如页面中央)放置单张图片水印,可通过 PdfImage.FromFile 加载图片,并结合页面画布的透明度设置与 DrawImage 方法直接实现;另一种是让图片在整页重复平铺,可借助 PdfTilingBrush 平铺画刷完成。
本文介绍两个核心功能点:
有关安装和项目配置,请参考 React 项目中集成 Spire.PDF for JavaScript。以下示例默认已安装 Spire.PDF 并完成 WebAssembly 模块初始化。
将单个图片水印添加到 PDF
单张图片水印会在页面的指定位置(本示例为每页中央)放置一枚半透明的图片,适用于在文档醒目位置放置公司 logo 或警示标识。做法是:用 PdfImage.FromFile 从文件加载图片,随后在每页的画布上以 Save 保存状态、用 SetTransparency 设置透明度与混合方式,通过 DrawImage 将图片绘制到居中位置,最后用 Restore 恢复画布状态。
function App() {
const addSingleImageWatermark = 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/`);
// 将水印图片载入 VFS
const inputImageName = 'logo.png';
await window.spire.FetchFileToVFS(inputImageName, "", `${process.env.PUBLIC_URL}/data/`);
// 创建 PdfDocument 对象并加载 PDF 文档
let doc = new pdfModule.PdfDocument();
doc.LoadFromFile(inputFileName);
// 从文件加载水印图片
let image = pdfModule.PdfImage.FromFile(inputImageName);
// 遍历文档中的所有页面
for (let i = 0; i < doc.Pages.Count; i++) {
// 获取指定页面
let page = doc.Pages.get_Item(i);
// 保存画布状态,并设置半透明(透明度 0.5)与正片叠底混合模式
page.Canvas.Save();
page.Canvas.SetTransparency({ alphaPen: 0.5, alphaBrush: 0.5, blendMode: pdfModule.PdfBlendMode.Multiply });
// 计算居中绘制位置:用页面尺寸减去图片尺寸后取一半
let position = new pdfModule.PointF(
(page.Canvas.Size.Width - image.Width) / 2,
(page.Canvas.Size.Height - image.Height) / 2
);
// 在页面中央绘制水印图片
page.Canvas.DrawImage({ image: image, point: position });
// 恢复画布之前的状态
page.Canvas.Restore();
}
// 定义输出文件名并保存文档
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>Add Single Image Watermark To PDF</h1>
<button onClick={addSingleImageWatermark}>
Generate
</button>
</div>
);
}
export default App;
添加单个图片水印后的 PDF 文档

将平铺图片水印添加到 PDF
当需要让水印铺满整页、形成浅淡的背景纹理时,可使用平铺图片水印。做法是:用 PdfImage.FromFile 加载图片,按页面尺寸通过 PdfTilingBrush 划分平铺单元,在画刷的图形上下文中以 SetTransparency 降低透明度并调用 DrawImage 在单元内绘制图片,最后以 DrawRectangle 用该画刷填充整页,即可让图片按单元行列重复、平铺覆盖整个页面。
function App() {
const addTiledImageWatermark = 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/`);
// 将水印图片载入 VFS
const inputImageName = 'logo.png';
await window.spire.FetchFileToVFS(inputImageName, "", `${process.env.PUBLIC_URL}/data/`);
// 创建 PdfDocument 对象并加载 PDF 文档
let doc = new pdfModule.PdfDocument();
doc.LoadFromFile(inputFileName);
// 从文件加载水印图片
let image = pdfModule.PdfImage.FromFile(inputImageName);
// 遍历文档中的所有页面
for (let i = 0; i < doc.Pages.Count; i++) {
// 获取指定页面
let page = doc.Pages.get_Item(i);
// 创建平铺画刷:以页面宽度的三分之一、高度的五分之一作为平铺单元
let size = new pdfModule.SizeF({
width: page.Canvas.Size.Width / 3,
height: page.Canvas.Size.Height / 5
});
let brush = new pdfModule.PdfTilingBrush({ size: size });
// 设置水印透明度为 30%
brush.Graphics.SetTransparency({ alpha: 0.3 });
// 在平铺单元中央绘制水印图片
let point = new pdfModule.PointF(
(brush.Size.Width - image.Width) / 2,
(brush.Size.Height - image.Height) / 2
);
brush.Graphics.DrawImage({ image: image, point: point });
// 用平铺画刷填充整页矩形,使图片水印平铺覆盖整个页面
let rect = new pdfModule.RectangleF({
location: new pdfModule.PointF(0, 0),
size: page.Canvas.Size
});
page.Canvas.DrawRectangle({ brush: brush, rectangle: rect });
}
// 定义输出文件名并保存文档
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>Add Tiled Image Watermark To PDF</h1>
<button onClick={addTiledImageWatermark}>
Generate
</button>
</div>
);
}
export default App;
添加平铺图片水印后的 PDF 文档

常见问题
如何控制图片水印的透明度
原因:图片水印以图片形式叠加在正文之上,若不降低透明度会遮挡正文内容。
解决:绘制前用 Save 保存画布状态,再用 SetTransparency 设置透明度:其透明度由 alphaPen 与 alphaBrush 指定,取值范围为 0(完全透明)到 1(不透明);如需让水印与底色自然融合,还可通过 blendMode 指定混合模式,如 PdfBlendMode.Multiply(正片叠底)。绘制完成后用 Restore 恢复画布状态,避免影响后续绘制:
// 保存画布状态,设置半透明与正片叠底混合模式后绘制水印图片
page.Canvas.Save();
page.Canvas.SetTransparency({ alphaPen: 0.5, alphaBrush: 0.5, blendMode: pdfModule.PdfBlendMode.Multiply });
page.Canvas.DrawImage({ image: image, point: position });
page.Canvas.Restore();
如何指定图片水印的位置
原因:DrawImage 若不传入位置,会按默认坐标绘制图片,无法精确控制水印落点。
解决:DrawImage 支持传入 PointF 位置或 x、y 坐标。想让图片居中时,用页面尺寸减去图片尺寸后取一半即可得到居中坐标:
// 计算居中位置:页面尺寸减去图片尺寸后取一半
let point = new pdfModule.PointF(
(page.Canvas.Size.Width - image.Width) / 2,
(page.Canvas.Size.Height - image.Height) / 2
);
// 在指定位置绘制水印图片
page.Canvas.DrawImage({ image: image, point: point });
如何调整平铺图片水印的疏密
原因:平铺图片水印的疏密由平铺画刷 PdfTilingBrush 的 size(平铺单元尺寸)决定。
解决:平铺单元越小,图片重复越密集;单元越大则越稀疏。把 size 设为页面宽高的一定比例即可按行列平铺,例如以页面宽度的三分之一、高度的五分之一为一个单元,得到间隔适中的水印纹理。想更稀疏可调大分母(如 Width / 4),想更密集则调小分母:
// 创建平铺画刷:以页面宽度的三分之一、高度的五分之一作为平铺单元
let size = new pdfModule.SizeF({
width: page.Canvas.Size.Width / 3,
height: page.Canvas.Size.Height / 5
});
let brush = new pdfModule.PdfTilingBrush({ size: size });
获取免费许可证
如果您希望删除结果文档中的评估消息,或者摆脱功能限制,请该Email地址已收到反垃圾邮件插件保护。要显示它您需要在浏览器中启用JavaScript。获取有效期 30 天的临时许可证。









