最稳妥方案是用iText7将文字水印绘制在PdfPage的OverContent层,需动态获取页面尺寸、使用SaveState/RestoreState处理旋转、嵌入字体并设置合适大小与透明度。

用 iTextSharp 或 iText7 添加文字水印最稳妥
直接用原生 .NET API 无法操作 PDF 内容,必须依赖第三方库。目前生产环境最成熟的是 iText7(商业授权宽松,社区版够用),iTextSharp(v5)已停止维护,但老项目还在用——别新起项目选它。
关键不是“能不能加”,而是“加在哪一层、是否覆盖正文、是否随缩放缩放”。文字水印必须写在 PDF 的 OverContent(上层内容流)里,否则会被原文本遮盖或错位。
- 用
PdfCanvas操作OverContent,不是直接改PdfPage的文本流 - 字体必须嵌入(
font.SetSubset(true)),否则别人打开可能显示方块 - 坐标原点在左下角,不是左上角;Y 轴向上增长,别按屏幕直觉写
y = 100 -
iText7需要显式调用canvas.Release(),漏掉会导致水印不渲染
DrawString 文字水印的旋转和居中容易偏移
常见现象是水印歪斜、只显示半截、或者全挤在左下角——根本原因是没换算好旋转中心点和页面尺寸。
PDF 页面宽高单位是点(point),1 point = 1/72 英寸。A4 是 595 x 842,但实际可用区域还要减去页边距。直接写死 x=300, y=400 必然错位。
- 先用
page.GetPageSize().GetWidth()和.GetHeight()动态读尺寸 - 旋转前用
canvas.SaveState(),旋转后canvas.RestoreState() - 绕中心旋转:先
canvas.Translate(xCenter, yCenter),再canvas.RotateRadians(Math.PI / 4),最后canvas.BeginText() - 字体大小建议 ≥ 60,太小在打印时不可见;透明度用
canvas.SetFillColor(new DeviceRgb(0.8f, 0.8f, 0.8f))控制灰度,别用 Alpha 混合(PDF 不保证支持)
图片水印要用 PdfFormXObject 而非直接 DrawImage
直接调 canvas.AddImageFittedIntoRectangle 很容易拉伸变形、分辨率崩坏,或者只在第一页出现。真正稳定的方案是把图片转成 PDF 表单对象(PdfFormXObject),再平铺或居中绘制。
这是因为 PDF 图片不是像素图,而是矢量描述;原图 PNG/JPG 必须先封装成 PDF 兼容的 XObject,才能正确缩放、旋转、复用。
- 加载图片:用
ImageDataFactory.Create("logo.png"),路径必须存在且有读权限 - 创建表单对象:
var form = new PdfFormXObject(new Rectangle(0, 0, width, height)),尺寸按需设(比如 200×100) - 在 form 上画图:
new Canvas(form, pdfDoc).AddImage(img, 0, 0, true),第三个参数true表示保持宽高比 - 最后在每页
OverContent上绘制该 form:canvas.AddXObjectAt(form, x, y) - 注意:大图(>2MB)会显著拖慢生成速度,建议预压缩到 800×400 以内
批量处理多页 PDF 时,CopyPagesTo 和 CopyPage 行为完全不同
想给一个 100 页 PDF 每页都加水印?别用 pdfDoc.CopyPagesTo(1, n, newPdfDoc) 后再统一加——它复制的是原始页面对象,OverContent 是空的,水印不会自动带过去。
必须逐页操作:打开源 PDF → 获取每页 → 创建新页 → 把原页内容拷贝进新页底层(UnderContent)→ 在新页上层(OverContent)画水印 → 写入目标文件。
- 用
sourceReader.GetPage(pageIndex)读原页,别用pdfDoc.GetPage(pageIndex)(那是编辑中的页) - 新页的
UnderContent用newPage.GetPageSize()初始化,否则尺寸错乱 - 拷贝原页内容:用
newPage.CopyContentFrom(sourcePage),不是CopyPage(后者只复制引用) - 水印逻辑必须放在
CopyContentFrom之后,否则被覆盖 - 内存敏感场景记得及时
sourceReader.Close(),iText7 不自动释放原文件句柄
最易忽略的一点:水印的 Z-order(层叠顺序)完全由绘制顺序决定。先画文字、再画图片,图片就会盖住文字;反过来,文字就可能被图片半透明区域吃掉。没有“图层管理”,只有“谁后画谁在上”。



















