用 iTextSharp 添加文字水印时,PageStamp
和 ContentByte
的调用时机很关键
直接在
OnEndPage里写水印,但没调用
cb.SaveState()和
cb.RestoreState(),会导致后续内容(比如页眉页脚)被旋转或透明度污染。文字水印必须在页面内容绘制前叠加,且需独立坐标系。 先调用
cb.SaveState(),再设置旋转、字体、颜色、透明度 用
cb.BeginText()+
cb.ShowTextAligned()写文字,别用
ColumnText文字位置建议用
document.PageSize.Width / 2和
document.PageSize.Height / 2居中,再手动平移避免裁剪 如果水印文字被原PDF内容盖住,说明你写在了
DirectContent而不是
DirectContentUnder
iTextSharp v5 中图片水印必须预加载为 PdfTemplate
或 Jpeg
对象
不能直接传文件路径字符串给
cb.AddImage();否则会抛出
NullReferenceException或静默失败。图片尺寸、DPI、色彩模式也会影响渲染效果——常见问题是 PNG 透明通道丢失或 JPG 拉伸变形。 用
Jpeg.GetInstance(imagePath)加载 JPG,
Png.GetInstance(imagePath)加载 PNG(注意后者需启用 alpha 支持) 缩放图片推荐用
img.ScaleAbsolute(200f, 100f),别依赖原始尺寸;太大易撑破页面,太小看不清 叠加前务必调用
img.SetAbsolutePosition(x, y),x/y 是左下角坐标,不是中心点 若 PDF 原始页面是 A4 但旋转了(如 landscape),要先读
page.PageSize.Rotate再算坐标
替换现有 PDF 时,PdfStamper
的构造参数决定水印是否可编辑
用
new PdfStamper(reader, output, '<p>用 <code>new PdfStamper(reader, output, '\0', true)的第四个参数设为
true,才能启用“提升权限”模式,否则添加的水印图层可能被 Acrobat 识别为普通内容而允许删除。但这也意味着输出 PDF 会带修改密码保护标记(即使没设密码),部分下游系统会报“文档已加密”警告。', true) 的第四个参数设为
true,才能启用“提升权限”模式,否则添加的水印图层可能被 Acrobat 识别为普通内容而允许删除。但这也意味着输出 PDF 会带修改密码保护标记(即使没设密码),部分下游系统会报“文档已加密”警告。 不加权限提升:水印能被轻易删掉,适合内部草稿 加权限提升:水印图层锁定,但需确认接收方 PDF 阅读器兼容性(尤其国产阅读器常忽略该标志)
PdfStamper.FormFlattening = true可防止表单域遮挡水印,但会丢弃所有交互能力
.NET Core 项目里引用 iTextSharp 会遇到签名冲突和 GDI+ 依赖问题
iTextSharp v5.5.x 不支持 .NET Standard,强行 NuGet 安装后编译通过,运行时却在
PdfContentByte.ShowTextAligned()报
System.TypeInitializationException—— 根源是其内部用了
System.Drawing.Common,而 .NET Core 3.1+ 默认不启用 GDI+。 方案一:降级用 iText7(
iText7.PdfCore),API 完全重写,但支持 .NET 6+,水印用
Canvas+
Div构建 方案二:保留 iTextSharp,项目文件加
<usewpf>true</usewpf>或
<usewindowsforms>true</usewindowsforms>方案三:改用
QuestPDF生成带水印的新 PDF,而非修改旧文件——适合水印内容固定、无需保留原文档结构的场景
真正麻烦的不是加水印这一步,而是判断原 PDF 是否含加密、是否有 XFA 表单、是否用了非标准字体嵌入——这些都会让水印位置偏移或文字乱码。动手前先用
PdfReader.IsEncrypted和
reader.Catalog.GetAsDict(PdfName.ACROFORM)探查文档元信息。
