使用iTextSharp为PDF所有页面加边框时遇异常问题求助
看起来你在用iTextSharp给PDF添加边框的时候踩了几个常见的坑,我来帮你逐个分析问题根源,并给出靠谱的解决方案:
1. 第一个方法的「文档无页面」错误
你创建了Document和PdfWriter,但完全没有把源PDF的任何页面导入到新文档里,相当于你创建了一个空文档,调用document.Close()时自然会抛出「文档无页面」的异常。要生成带边框的新PDF,核心是把源PDF的内容复制过来,再加上边框。
2. 尝试1的页面变黑问题
你设置了rectangle.BackgroundColor = BaseColor.BLACK,这会让矩形填充黑色;而且你只调用了cb.Rectangle(rectangle),没有明确指定只绘制边框(描边),iText默认会执行「填充+描边」操作,所以整个页面被黑色填满了。需要去掉背景色设置,改为指定描边颜色,再调用Stroke()单独绘制边框。
3. 尝试2的ObjectDisposedException错误
你同时用PdfWriter和PdfStamper操作同一个FileStream,这两个组件都会管理流的生命周期,导致流被提前关闭,抛出「无法访问已关闭的文件」的异常。而且这种用法逻辑混乱——PdfStamper是用来直接修改现有PDF的,根本不需要搭配Document和PdfWriter。
方案一:用PdfStamper(推荐,简洁高效)
PdfStamper可以直接在现有PDF的页面上叠加内容,不需要重新构建文档,是处理这类需求的最佳方式:
private string PutBorderToPdfPages(string pdfFilePath) { string newPdf = @"C:\Output.pdf"; using (var reader = new PdfReader(pdfFilePath)) { using (var fileStream = new FileStream(newPdf, FileMode.Create, FileAccess.Write)) using (var pdfStamper = new PdfStamper(reader, fileStream)) { int pageCount = reader.NumberOfPages; for (int p = 1; p <= pageCount; p++) { // 获取当前页面的上层内容(边框会显示在页面内容之上) PdfContentByte cb = pdfStamper.GetOverContent(p); // 获取当前页面的真实尺寸(包含旋转信息) iTextSharp.text.Rectangle pageSize = reader.GetPageSizeWithRotation(p); // 设置边框颜色为黑色 cb.SetColorStroke(BaseColor.BLACK); // 绘制边框:参数为左下角X、左下角Y、宽度、高度 cb.Rectangle(pageSize.Left, pageSize.Bottom, pageSize.Width, pageSize.Height); // 执行描边操作(只画边框,不填充) cb.Stroke(); } } } return newPdf; }
如果不想让边框贴紧页面边缘,可以手动调整边距:
// 边框与页面边缘保留20单位的距离 float margin = 20f; float x = pageSize.Left + margin; float y = pageSize.Bottom + margin; float width = pageSize.Width - 2 * margin; float height = pageSize.Height - 2 * margin; cb.Rectangle(x, y, width, height);
方案二:用Document + PdfWriter + PdfImportedPage(保留你的PageEvent思路)
如果你坚持想用PdfPageEventHelper的方式,必须把源PDF的页面导入到新文档中,避免空文档报错:
private string PutBorderToPdfPages(string pdfFilePath) { string newPdf = @"C:\Output.pdf"; using (var reader = new PdfReader(pdfFilePath)) { using (var fileStream = new FileStream(newPdf, FileMode.Create, FileAccess.Write)) { // 用第一页的尺寸初始化文档 iTextSharp.text.Document document = new iTextSharp.text.Document(reader.GetPageSizeWithRotation(1)); PdfWriter writer = PdfWriter.GetInstance(document, fileStream); // 绑定页面事件(负责添加边框) PdfEventHelper pdfEvent = new PdfEventHelper(); writer.PageEvent = pdfEvent; document.Open(); PdfContentByte content = writer.DirectContent; // 遍历所有页面,导入到新文档 for (int p = 1; p <= reader.NumberOfPages; p++) { // 切换当前页面的尺寸(适配源PDF的页面大小/旋转) document.SetPageSize(reader.GetPageSizeWithRotation(p)); document.NewPage(); // 导入源PDF的页面 PdfImportedPage importedPage = writer.GetImportedPage(reader, p); // 根据页面旋转角度,正确绘制导入的内容 int rotation = reader.GetPageRotation(p); if (rotation == 90 || rotation == 270) { content.AddTemplate(importedPage, 0, -1f, 1f, 0, 0, reader.GetPageSizeWithRotation(p).Height); } else { content.AddTemplate(importedPage, 1f, 0, 0, 1f, 0, 0); } } document.Close(); writer.Close(); } } return newPdf; } // 你的PdfEventHelper类可以保持不变 public class PdfEventHelper : PdfPageEventHelper { public override void OnEndPage(PdfWriter writer, iTextSharp.text.Document document) { PdfContentByte content = writer.DirectContent; iTextSharp.text.Rectangle rectangle = new iTextSharp.text.Rectangle(document.PageSize); // 让边框和文档边距对齐 rectangle.Left += document.LeftMargin; rectangle.Right -= document.RightMargin; rectangle.Top -= document.TopMargin; rectangle.Bottom += document.BottomMargin; content.SetColorStroke(BaseColor.BLACK); content.Rectangle(rectangle.Left, rectangle.Bottom, rectangle.Width, rectangle.Height); content.Stroke(); } }
这个方案需要注意处理页面旋转,确保导入的页面显示方向正确,同时因为添加了源PDF的内容,文档不再是空的,document.Close()就不会报错了。
内容的提问来源于stack exchange,提问作者Willy

