C# iText生成/修改PDF:坐标转换与内存流操作技术问询
嘿,我看你在用iTextSharp处理PDF操作,正好能帮你解决这几个问题,咱们一步步来:
1. 厘米与磅的坐标转换
PDF的默认单位是磅(Point),咱们常用的厘米和磅的转换关系很清晰:
- 1英寸 = 72磅
- 1厘米 = 2.54英寸
我帮你写两个静态工具方法,方便随时调用转换:
// 厘米转磅 public static float CmToPoints(float cm) { return cm * 72 / 2.54f; } // 磅转厘米(需要反向转换时用) public static float PointsToCm(float points) { return points * 2.54f / 72; }
举个实际例子:
- 3厘米转磅:
CmToPoints(3)≈ 85.04磅 - 10厘米转磅:
CmToPoints(10)≈ 283.47磅
另外要注意一个容易踩的坑:PDF的坐标原点在页面的左下角,所以“距顶部10厘米”的位置,得用页面高度减去转换后的磅值,比如A4页面高度是841.89磅,那对应的y坐标就是 841.89 - CmToPoints(10)。
2. 创建新PDF或加载已有PDF修改
创建新PDF
你原代码里的SetSimpleColumn参数顺序有点问题,这个方法的参数是x1(左下角x)、y1(左下角y)、x2(右上角x)、y2(右上角y),我已经在下面的完整代码里修正了这部分,还结合坐标转换方法帮你设置了指定位置的文本。
加载已有PDF修改
要修改已有PDF,咱们得用PdfReader读取原文件,再用PdfStamper来添加内容,这样既能保留原PDF的所有内容,又能在指定位置添加新文本。
3. 用内存流修改PDF并覆盖原文件
直接同时读写同一个文件会报错,所以咱们用内存流中转:
- 先把原文件读入内存流
- 基于内存流创建
PdfReader进行修改 - 将修改后的内容写入新的内存流
- 最后把新内存流的内容覆盖回原文件
下面是整合了所有需求的完整代码,每一步都加了注释:
using System; using System.IO; using iTextSharp.text; using iTextSharp.text.pdf; namespace CreateChangePdf { class Program { static void Main(string[] args) { string fileName = @"c:\data\pdftest.pdf"; string textToAdd = @"New text"; // 示例:文本距左侧3厘米、距顶部10厘米 float leftDistanceCm = 3; float topDistanceCm = 10; if (File.Exists(fileName)) { // 用内存流修改已有PDF并覆盖原文件 ModifyPdfWithMemoryStream(fileName, textToAdd, leftDistanceCm, topDistanceCm); } else { // 创建新PDF并添加指定位置的文本 CreateNewPdf(fileName, textToAdd, leftDistanceCm, topDistanceCm); } } // 厘米转磅的工具方法 public static float CmToPoints(float cm) { return cm * 72 / 2.54f; } // 创建新PDF的方法 private static void CreateNewPdf(string filePath, string text, float leftCm, float topCm) { using (var outputStream = new FileStream(filePath, FileMode.Create)) { var document = new Document(PageSize.A4); var writer = PdfWriter.GetInstance(document, outputStream); document.Open(); var contentByte = writer.DirectContent; var columnText = new ColumnText(contentByte); // 获取A4页面的高度(也可以用document.PageSize.Height) float pageHeight = PageSize.A4.Height; // 计算坐标:x是左侧距离转磅,y是页面高度减去顶部距离转磅(因为原点在左下角) float xPos = CmToPoints(leftCm); float yPos = pageHeight - CmToPoints(topCm); // 设置文本显示的区域:从(xPos,yPos)开始,宽度200磅,行高15磅 columnText.SetSimpleColumn( new Phrase(text, FontFactory.GetFont(FontFactory.COURIER, 12, Font.NORMAL)), xPos, yPos, xPos + 200, yPos + 15, 0, Element.ALIGN_LEFT ); columnText.Go(); document.Close(); } } // 用内存流修改PDF并覆盖原文件的方法 private static void ModifyPdfWithMemoryStream(string filePath, string text, float leftCm, float topCm) { byte[] originalPdfBytes; // 第一步:把原文件读入内存 using (var originalFileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read)) using (var memoryStream = new MemoryStream()) { originalFileStream.CopyTo(memoryStream); originalPdfBytes = memoryStream.ToArray(); } // 第二步:修改PDF内容到新的内存流 using (var modifiedMemoryStream = new MemoryStream()) { var pdfReader = new PdfReader(originalPdfBytes); var pdfStamper = new PdfStamper(pdfReader, modifiedMemoryStream); // 获取第一页的内容层(GetOverContent是在原内容上方添加,GetUnderContent是下方) var contentByte = pdfStamper.GetOverContent(1); var columnText = new ColumnText(contentByte); float pageHeight = pdfReader.GetPageSize(1).Height; float xPos = CmToPoints(leftCm); float yPos = pageHeight - CmToPoints(topCm); columnText.SetSimpleColumn( new Phrase(text, FontFactory.GetFont(FontFactory.COURIER, 12, Font.NORMAL)), xPos, yPos, xPos + 200, yPos + 15, 0, Element.ALIGN_LEFT ); columnText.Go(); // 关闭资源 pdfStamper.Close(); pdfReader.Close(); // 第三步:把修改后的内容覆盖回原文件 using (var outputFileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write)) { modifiedMemoryStream.Seek(0, SeekOrigin.Begin); modifiedMemoryStream.CopyTo(outputFileStream); } } } } }
额外提醒
- 确保你已经通过NuGet安装了iTextSharp包(可以在NuGet包管理器里搜索iTextSharp并安装)
- 如果需要修改PDF的所有页面,可以循环
pdfReader.NumberOfPages,对每一页执行添加文本的逻辑 - 如果要真正“覆盖”原有内容(而不是添加在上方),需要先清除对应区域的内容,这个操作比较复杂,一般用
GetOverContent添加新内容就能满足需求
内容的提问来源于stack exchange,提问作者user3507211
相关产品推荐
相关产品推荐

