第一页无页码因OnEndPage事件在首页初始化前触发,需确保PageEvent正确设置、用DirectContent绘制、处理加密权限、适配坐标系与CropBox、延迟计算页数、禁用冗余字体嵌入。

用 iTextSharp 给 PDF 插页码,为什么第一页总是没编号?
因为默认从第一页开始写入内容时,页码逻辑常被套在 OnEndPage 事件里,但第一页的 OnEndPage 触发前,页眉页脚区域可能还没初始化完成——尤其当原 PDF 是扫描件或无内容流时,iTextSharp 会跳过首次渲染准备。
- 必须在
Open()后、Close()前调用Writer.PageEvent = new PageNumberEventHandler(),且该 handler 的OnEndPage方法里用writer.DirectContent而非cb(后者在空页上不可靠) - 页码坐标建议用
document.PageSize.GetLeft(20)+document.PageSize.GetBottom(20),避免硬写36, 36导致中文 PDF 下沉或裁切 - 如果原 PDF 有加密(比如禁止修改),
PdfStamper构造时需传入 owner password,否则静默失败,页码根本不会出现
用 PdfSharp 修改已有 PDF 时,页码位置偏移或重叠文字
PdfSharp 的坐标系 Y 轴向上为正,而多数人习惯向下为正——这是位置错乱最常见原因。它不自动处理原 PDF 的 CropBox 或 Rotate 属性,直接按 PageSize 计算会导致页码贴到页面外或盖住正文。
- 取实际可绘制区域要用
page.CropBox ?? page.MediaBox,再通过page.Rotate判断是否需要交换宽高值 - 写文本必须用
gfx.DrawString("1", font, XBrushes.Black, new XRect(x, y, width, height), XStringFormats.TopLeft),不能只传XPoint,否则多行内容会堆叠 - 中文字体必须显式注册:
XFont font = new XFont("SimSun", 10, XFontStyle.Regular, new XPdfFontOptions(PdfFontEncoding.Unicode)),否则显示方块或空白
用 QuestPDF 生成带页码的新 PDF,为什么总显示“Page 1 of 1”?
QuestPDF 的 PageNumber() 和 PageCount() 是延迟计算的,只有整个文档完全构建并渲染完毕后才准确。若在 Page() 内部直接拼接字符串,PageCount() 还未就绪,返回默认值 1。
- 页码必须放在
FooterSection()或PageNumber()组件内,不要自己写"Page " + pageNumber + " of " + pageCount - 如果用了
.PageBreak()手动分页,需确保每个Page()都调用PageNumber(),否则后续页的计数器不递增 - 导出时用
document.GeneratePdf(stream),别用GenerateHtml()中转,HTML 渲染不触发页数统计逻辑
为什么用 Spire.PDF 添加页码后,文件体积暴涨 3 倍?
Spire.PDF 默认启用字体子集嵌入(font subsetting),对中文字体尤其激进——哪怕只用一个“第”字,也会把整个 GB2312 字库拆解后嵌入 PDF。再加上它内部缓存大量 PdfImage 对象用于渲染水印式页码,内存压力直接反映在输出体积上。
- 关掉字体嵌入:
pdfDocument.SaveOptions.EmbedFonts = false(前提是目标环境已安装对应字体) - 页码用纯矢量文字而非图片:
page.Canvas.DrawString()替代page.Canvas.DrawImage() - 批量处理时复用
PdfFont实例,避免每页新建,否则 GC 来不及回收导致内存堆积
页码这事看着简单,但 PDF 格式本身对“页”的定义就很松散——有些文件用 Pages 目录,有些靠 PageTree,还有些靠 AcroForm 动态生成。选哪个库不重要,关键是先确认你的源 PDF 是哪种结构,再决定页码是覆盖写还是追加写。



















