← dompdf.js Studio

PDF 水印添加指南:dompdf.js 文字与图片水印全攻略

公司内部文档、合同扫描件、设计稿预览,几乎都需要加水印:标注机密等级、防止截图外泄、宣示版权归属。传统做法要么用付费工具一张张处理,要么在服务端用重型的 PDF 库,既慢又贵,还增加基础设施负担。dompdf.js 在浏览器端即可完成水印渲染,文字水印、图片水印、平铺水印都能通过 CSS 轻松实现,配合自动分页,每一页都会带上统一水印。本文从水印的应用场景讲起,覆盖实现原理、完整代码、多页一致性处理与设计注意事项,最后给出常见问题的排查清单,帮助你用纯前端方案把水印功能做扎实,不再依赖第三方工具,也不用再手动给每份文档重复加水印。

水印的应用场景与需求分析

水印的典型场景有三类:机密文档标注(内部资料、仅限传阅)、版权防伪(设计稿、样机图、电子合同)、客户化标识(在预览版 PDF 上打上客户名与日期,防止免费版被商用)。每一类的诉求各不相同,实现时也要区别对待。

不同场景对水印的要求不同:机密标注要醒目但不遮挡正文,版权防伪要难以去除、覆盖全面,客户化标识则往往需要动态生成,每个用户的 PDF 水印内容都不一样,必须在渲染时注入,无法预先生成好模板。

水印还分为单页水印与多页统一水印。多页文档如果只在第一页加水印,后面的页面等于裸奔,截图的人只截后几页就能绕开保护;dompdf.js 的自动分页配合固定定位层,可以保证每一页都有水印,不留下任何缺口,防护无死角。

在需求评审阶段就把水印规则说清楚很重要:谁的水印、什么内容、多深的透明度、是否随用户动态变化。规则越明确,实现越简单,也避免后期反复返工调整水印样式,浪费开发资源。

文字水印的实现思路

dompdf.js 直接渲染 DOM,因此水印本质上是普通 HTML 元素:用绝对定位把水印文字放到页面中间,配合 transform 旋转 45 度形成经典的对角水印效果,横跨整页、难以裁切,也符合人们对正式文档水印的认知。

透明度用 opacity 控制,建议 0.1-0.2 之间:太浅起不到警示作用,太深则影响正文阅读。水印文字建议使用对比度低的灰色(如 #999),既不抢眼又能被清晰识别,达到警示与美观的平衡,兼顾阅读体验与防泄漏效果。

需要注意,文字水印会随正文一起被渲染为矢量文字,是可选中、可复制的。这既是优势(清晰度高、可溯源)也是局限(技术高手可以提取水印文字),对防泄漏要求极高的场景应结合服务端加密等其他手段,前端方案更适合防视觉外泄。

除了旋转与透明度,水印还可以组合多个图层:比如文字加日期加用户名的三行水印,或者文字叠加细边框,既美观又增加伪造难度。元素组合的自由度与普通 HTML 完全一致,想怎么排都行。

代码示例:平铺水印与对角水印

import { DomPDF } from 'dompdf.js';

const watermarkHTML = `
  <style>
    @page { margin: 20mm; }
    .watermark {
      position: fixed;
      top: 45%; left: 0; right: 0;
      text-align: center;
      font-size: 48px;
      color: #999;
      opacity: 0.12;
      transform: rotate(-45deg);
      pointer-events: none;
    }
    .tiled {
      position: fixed;
      top: 0; left: 0; right: 0; bottom: 0;
      background: repeating-linear-gradient(
        -45deg,
        transparent 0 60px,
        rgba(153,153,153,0.10) 60px 120px
      );
    }
  </style>
  <!-- 对角文字水印 + 平铺斜纹水印,双层防护 -->
  <div class="watermark">机密 · 内部资料</div>
  <div class="tiled"></div>
  <h1>产品需求文档 v2.3</h1>
  <p>本文档仅限项目组内部传阅,禁止外发……</p>`;

const pdf = new DomPDF();
pdf.addPage(watermarkHTML, { format: 'A4' });
pdf.save('prd-watermarked.pdf');

图片水印与多页统一水印

图片水印适合放 Logo 或防伪图章:把透明背景的 PNG 用 <img> 或 background-image 放进绝对定位层,设置合适透明度即可,做法与文字水印完全一致,只是把文字元素换成图片元素,上手零门槛。

position: fixed 让水印层固定在页面视口,dompdf.js 分页时该层会在每一页重复出现,天然实现多页统一水印,不需要为每页单独插入元素,也不用担心页数变化后水印缺失,逻辑简单可靠。

如果文档是多次 addPage 拼成的多页文档,也可以在每次 addPage 的 HTML 中复用同一个水印片段,比如封装成一个水印模板字符串拼接进每页内容,保证所有页风格统一、维护起来也方便,一处修改全局生效。

如果水印需要跟随正文而不是固定居中,比如只在标题区域显示,也可以把水印元素放进对应的容器内,配合定位属性实现局部水印,灵活度很高,能满足更多定制化需求。

如果水印需要在导出时动态生成(比如每次导出带上当前时间戳),只需在拼接 HTML 时注入变量即可,模板字符串天然支持,不用修改任何渲染逻辑,实现成本几乎为零。

水印设计的注意事项

水印元素一定要设置 pointer-events: none,避免影响正文内容的交互与布局;同时用 z-index 确保水印位于正文之上、又因为低透明度不遮挡阅读,两层配合才能既醒目又不碍事,是水印功能的基本功。

旋转角度建议 30-45 度,兼顾辨识度与美观;平铺水印的间距要均匀,文字水印可以同时加日期与用户名,让每个 PDF 可溯源到具体责任人,形成威慑,也方便事后追责。

注意水印不要与页眉页脚、页码区域重叠,避免视觉混乱;导出前建议在 A4 纸面上目测一遍透明度与位置,不同显示器对透明度的感知差异较大,打印样张验证最可靠,能避免交付后才发现效果不佳。

水印与安全策略要配套设计:水印负责事后溯源与事前威慑,权限控制负责事前拦截,两者结合才能构成完整的安全闭环,单靠水印防泄漏并不现实,这一点要跟业务方讲清楚。

还要提醒一点:水印效果一定要在真实业务数据上验证,不要只用演示内容测试。长标题、长表格、图片密集的页面会让水印位置和视觉密度发生变化,提前用真实数据跑一遍能避免交付后返工。

常见问题

Q: 水印只出现在第一页?A: 检查水印层是否使用了 position: fixed;若放在普通流中会被分页截断,改成 fixed 后每页都会重复渲染,一劳永逸,这是最高频的水印问题。

Q: 水印文字在 PDF 里能复制出来?A: 可以,因为 dompdf.js 输出矢量文字。若需防复制,可结合 PDF 权限设置或服务端加密,前端方案更适合防视觉外泄,定位要清晰。

Q: 水印太淡或太浓?A: 调整 opacity 与颜色即可,建议 0.1-0.15 起步,在真实打印样张上验证效果后再定稿,不要只凭屏幕观感判断,屏幕与纸面的观感差异很大。

Q: 图片水印背景发白?A: 使用带透明通道的 PNG 图片,避免使用 JPG 作为水印素材,JPG 没有透明通道,会渲染成实心方块,破坏整体效果。

Q: 多页文档每页水印位置略有偏移?A: 检查水印层是否依赖了随内容变化的高度属性,建议用固定尺寸与固定 top/left 值定位,分页后位置即可保持一致,排版整齐。

Q: 水印影响 PDF 文件体积吗?A: 文字水印是矢量文本,几乎不增加体积;平铺背景也只是几条渐变指令。只有高分辨率图片水印会带来少量体积增长,整体影响可以忽略。

⚡ 现场演示(点击生成 PDF)

下面的按钮用 dompdf.js 在浏览器端实时生成 PDF,无需后端:

Hello from dompdf.js!

这是由 dompdf.js 渲染的示例 PDF 内容。