Microsoft Word 自带的水印功能一次只能放置一个水印,其位置与数量由内置版式决定,无法将水印以多行多列的方式铺满整页。对于合同、内部资料、演示稿件这类需要整页覆盖的水印,可以通过在页眉中添加形状或图片,再按行列复制的方式实现。
本文用 Spire.Doc for JavaScript 在浏览器中为 Word 文档添加多行多列文字水印与图片水印。处理过程在 WebAssembly 中完成,不依赖 Office,也不需要后端服务,文件始终留在用户的设备上。
本文介绍三个功能点:
两类水印的实现方式对比如下:
| 水印类型 | 承载对象 | 创建方式 | 关键属性 |
|---|---|---|---|
| 文字水印 | 艺术字形状 | new ShapeObject(doc, ShapeType.TextPlainText) |
WordArt.Text、WordArt.FontFamily、Rotation、FillColor |
| 图片水印 | 图片对象 | new DocPicture(doc) 配合 LoadImage() |
TextWrappingStyle、Width、Height |
有关安装和项目配置,请参考 React 项目中集成 Spire.Doc for JavaScript。下面的示例假定 Spire.Doc 已安装、WebAssembly 模块已初始化。示例从项目的 public/data 目录读取 Sample.docx 与 logo.png,从 public/font 目录读取字体文件,运行时请将对应文件放入这两个目录。
添加多行多列文字水印
添加多行多列文字水印,是在页眉中创建一个艺术字形状,再按行、列循环复制,并为每一份副本设置不同的垂直与水平坐标。水印对象位于页眉中,会随页眉在每一页重复显示,因此不需要逐页添加。
添加多行多列文字水印涉及以下成员:
| 成员 | 作用 |
|---|---|
ShapeObject |
承载水印文字的形状对象 |
ShapeType.TextPlainText |
指定形状为普通艺术字 |
ShapeObject.Width、Height |
形状的宽高,单位为磅 |
ShapeObject.VerticalPosition、HorizontalPosition |
形状在页面中的垂直与水平坐标 |
ShapeObject.Rotation |
旋转角度,单位为度 |
WordArt.Text、WordArt.FontFamily |
艺术字内容与字体 |
ShapeObject.FillColor、StrokeColor |
文字填充色与边框色 |
ShapeObject.Clone() |
复制形状,供平铺使用 |
HeaderFooter.AddParagraph() |
在页眉中添加承载水印的段落 |
Paragraph.ChildObjects.Add() |
将形状加入页眉段落 |
具体操作步骤如下:
- 获取 Spire.Doc 的 WASM 模块,确认已就绪。
- 将示例文档与字体文件载入虚拟文件系统(VFS),创建
Document对象并加载文档。 - 创建
ShapeObject对象,将形状类型设为ShapeType.TextPlainText,设置宽高、旋转角度、艺术字内容、字体、填充色与边框色。 - 遍历文档中的每一节,通过
HeadersFooters.Header取得页眉,并用AddParagraph()添加段落。 - 按行、列循环调用
Clone()复制形状,为每份副本设置独立的VerticalPosition与HorizontalPosition,再加入页眉段落的ChildObjects。 - 使用
SaveToFile()保存结果文件,从 VFS 读出并触发浏览器下载。
添加多行多列文字水印的代码如下:
function App() {
const addTextWatermark = async () => {
// 获取 Spire.Doc 的 WASM 模块
const wasmModule = window.wasmModule?.spiredoc;
if (!wasmModule) {
alert('Spire.Doc is not ready yet');
return;
}
// 载入示例文档与字体
await window.spire.FetchFileToVFS('示例文档.docx', '', `${process.env.PUBLIC_URL}/data/`);
await window.spire.FetchFileToVFS('simsun.ttc', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
// 创建 Document 实例并加载文档
const doc = new wasmModule.Document();
doc.LoadFromFile('Sample.docx');
// 创建艺术字形状,并设置大小、位置、旋转角度与样式
let shape = new wasmModule.ShapeObject(doc, wasmModule.ShapeType.TextPlainText);
shape.Width = 60;
shape.Height = 15;
shape.VerticalPosition = 25;
shape.HorizontalPosition = 20;
shape.Rotation = 320;
shape.WordArt.Text = '内部使用';
shape.WordArt.FontFamily = '宋体';
shape.FillColor = wasmModule.Color.FromArgb(255, 255, 0, 0);
shape.StrokeColor = wasmModule.Color.FromArgb(255, 255, 0, 0);
// 遍历所有节,将水印加入每一节的页眉
for (let n = 0; n < doc.Sections.Count; n++) {
const section = doc.Sections.get_Item(n);
const header = section.HeadersFooters.Header;
const paragraph = header.AddParagraph();
// 5 行 6 列:复制形状并分别设置坐标
for (let i = 0; i < 5; i++) {
for (let j = 0; j < 6; j++) {
shape = shape.Clone();
shape.VerticalPosition = 50 + 150 * i;
shape.HorizontalPosition = 20 + 85 * j;
paragraph.ChildObjects.Add(shape);
}
}
}
// 保存结果文件
const outputFileName = '添加多行多列文字水印.docx';
doc.SaveToFile({ fileName: outputFileName, fileFormat: wasmModule.FileFormat.Docx2013 });
doc.Dispose();
// 从 VFS 读取生成的文件,触发下载
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' });
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={addTextWatermark}>生成文档</button>
</div>
);
}
export default App;
添加多行多列文字水印后的文档:

添加多行多列图片水印
添加多行多列图片水印,是在页眉中载入一张图片,再按行、列复制并调整坐标。与文字水印相比,图片水印的样式由图片本身决定,代码中只需要控制图片的环绕方式与排列位置。
添加多行多列图片水印涉及以下成员:
| 成员 | 作用 |
|---|---|
DocPicture |
承载水印图片的对象 |
DocPicture.LoadImage(path) |
从 VFS 载入水印图片 |
TextWrappingStyle.Behind |
将图片置于正文文字之后 |
DocPicture.VerticalPosition、HorizontalPosition |
每份图片的垂直与水平坐标 |
DocPicture.Clone() |
复制图片对象,供平铺使用 |
HeaderFooter.AddParagraph() |
在页眉中添加承载水印的段落 |
Paragraph.ChildObjects.Add() |
将图片加入页眉段落 |
具体操作步骤如下:
- 获取 Spire.Doc 的 WASM 模块,确认已就绪。
- 将示例文档与水印图片载入虚拟文件系统(VFS),创建
Document对象并加载文档。 - 遍历文档中的每一节,通过
HeadersFooters.Header取得页眉,并用AddParagraph()添加段落。 - 创建
DocPicture对象,调用LoadImage()载入水印图片,并将TextWrappingStyle设为TextWrappingStyle.Behind。 - 按行、列循环调用
Clone()复制图片,为每份副本设置独立的VerticalPosition与HorizontalPosition,再加入页眉段落的ChildObjects。 - 使用
SaveToFile()保存结果文件,从 VFS 读出并触发浏览器下载。
添加多行多列图片水印的代码如下:
function App() {
const addImageWatermark = async () => {
// 获取 Spire.Doc 的 WASM 模块
const wasmModule = window.wasmModule?.spiredoc;
if (!wasmModule) {
alert('Spire.Doc is not ready yet');
return;
}
// 载入示例文档与水印图片
await window.spire.FetchFileToVFS('示例文档.docx', '', `${process.env.PUBLIC_URL}/data/`);
await window.spire.FetchFileToVFS('logo.png', '', `${process.env.PUBLIC_URL}/data/`);
// 创建 Document 实例并加载文档
const doc = new wasmModule.Document();
doc.LoadFromFile('Sample.docx');
// 遍历所有节,将水印加入每一节的页眉
for (let n = 0; n < doc.Sections.Count; n++) {
const section = doc.Sections.get_Item(n);
const header = section.HeadersFooters.Header;
const paragraph = header.AddParagraph();
// 创建图片对象并载入水印图片
const picture = new wasmModule.DocPicture(doc);
picture.LoadImage('logo.png');
picture.TextWrappingStyle = wasmModule.TextWrappingStyle.Behind;
// 5 行 3 列:复制图片并分别设置坐标
for (let p = 0; p < 5; p++) {
for (let q = 0; q < 3; q++) {
const pic = picture.Clone();
pic.VerticalPosition = 50 + 150 * p;
pic.HorizontalPosition = 10 + 150 * q;
paragraph.ChildObjects.Add(pic);
}
}
}
// 保存结果文件
const outputFileName = '添加多行多列图片水印.docx';
doc.SaveToFile({ fileName: outputFileName, fileFormat: wasmModule.FileFormat.Docx2013 });
doc.Dispose();
// 从 VFS 读取生成的文件,触发下载
const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([fileArray], { type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' });
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={addImageWatermark}>生成文档</button>
</div>
);
}
export default App;
添加多行多列图片水印后的文档:

调整水印的平铺行列与间距
调整水印的平铺行列与间距,是修改循环次数与坐标公式中的两个系数:行数、列数决定水印的数量,行距、列距决定相邻水印之间的间隔。两者的组合决定了水印在页面上的疏密程度。
平铺使用的坐标公式如下:
| 参数 | 示例取值 | 作用 |
|---|---|---|
行数 rows |
4 | 纵向平铺的数量 |
列数 cols |
5 | 横向平铺的数量 |
行距 rowGap |
180 | 相邻两行的垂直间隔,单位为磅 |
列距 colGap |
100 | 相邻两列的横向间隔,单位为磅 |
| 起始坐标 | 40、20 | 第一行、第一列的垂直与水平坐标 |
每个水印的坐标按以下公式计算:
VerticalPosition = 起始垂直坐标 + 行距 × 行号HorizontalPosition = 起始水平坐标 + 列距 × 列号
具体操作步骤如下:
- 根据页面尺寸与页边距,估算可放置水印的宽度与高度。
- 确定行数、列数,以及第一行、第一列的起始坐标。
- 修改循环次数与坐标公式中的行距、列距,并按需调整形状的宽高。
- 保存文件并预览,根据实际效果微调间距。
调整平铺行列与间距的代码如下。示例使用 4 行 5 列、行距 180 磅、列距 100 磅,并将水印改为深灰色:
// 行数、列数与间距
const rows = 4;
const cols = 5;
const rowGap = 180;
const colGap = 100;
// 创建基础形状,后续通过 Clone() 复制
const base = new wasmModule.ShapeObject(doc, wasmModule.ShapeType.TextPlainText);
base.Width = 80;
base.Height = 20;
base.Rotation = 315;
base.WordArt.Text = '内部资料';
base.WordArt.FontFamily = '宋体';
base.FillColor = wasmModule.Color.FromArgb(255, 70, 70, 70);
base.StrokeColor = wasmModule.Color.FromArgb(255, 70, 70, 70);
for (let n = 0; n < doc.Sections.Count; n++) {
const header = doc.Sections.get_Item(n).HeadersFooters.Header;
const paragraph = header.AddParagraph();
for (let i = 0; i < rows; i++) {
for (let j = 0; j < cols; j++) {
const shape = base.Clone();
shape.VerticalPosition = 40 + rowGap * i;
shape.HorizontalPosition = 20 + colGap * j;
paragraph.ChildObjects.Add(shape);
}
}
}
调整行列数与间距后的文字水印:

常见问题
水印为什么没有出现在首页
页眉按节设置。示例写入的是 HeadersFooters.Header,它作用于该节的普通页。文档启用“首页不同”时,首页使用 HeadersFooters.FirstPageHeader,需要向该页眉另外添加水印;启用“奇偶页不同”时,偶数页使用 HeadersFooters.EvenHeader。示例中的文档未启用这两项,因此每一页都会显示水印。
为什么靠右的几列水印会落到页面之外
HorizontalPosition 以磅为单位,并且从页面的左页边距起算。文字水印的宽度为 60 磅,列距为 85 磅、共 6 列时,最右一列的右侧边缘约为 20 + 85 × 5 + 60 = 505 磅,加上左页边距后已接近页面右边缘;如果将列距或列数继续调大,靠右的对象就会超出页面。调整前可按“起始坐标 + 间隔 ×(数量 − 1) + 对象宽度”估算总宽度,再与页面宽度比较。
为什么设置的位置与页面上的实际位置有偏差
页眉中的形状采用绝对定位,横向以左侧页边距为原点,纵向以页眉区域顶部为原点。示例文档的左页边距为 90 磅、页眉距页面顶端为 36 磅,因此设置 HorizontalPosition = 20 时,水印在页面上的实际横坐标约为 110 磅。更换模板或调整页边距后,起始坐标需要重新估算。
复制多份图片水印会显著增大文件体积吗
通常不会显著增大。副本共享同一张图片:Clone() 复制的是图片对象与排版属性,图片本身在文档中只保存一份。示例生成 15 份图片水印,docx 的 word/media/ 目录中仍然只有一个图片文件,其余副本通过引用使用它。文件体积的增长主要来自形状与定位信息,而不是图片数据。
图片水印盖住正文怎么办
示例通过 TextWrappingStyle.Behind 将图片置于正文文字之后,生成的文档中图片的层级为负值,正文保持在上层。若水印仍然过于醒目,可以在制作图片时降低不透明度或改用浅色版本;示例使用的就是半透明浅色 PNG。
获取免费许可证
如果您希望删除结果文档中的评估消息,或者摆脱功能限制,请该Email地址已收到反垃圾邮件插件保护。要显示它您需要在浏览器中启用JavaScript。获取有效期 30 天的临时许可证。







