WinForms中如何将图片精准添加到PDF指定位置?
PDFiumViewer添加图片后保存到PDF的坐标计算问题
在WinForms项目中,使用PDFiumViewer查看PDF文件,iText7执行保存操作。软件支持打开任意PDF,通过“添加”按钮用PictureBox插入图片,用户可拖拽调整图片位置与尺寸,但保存PDF时,因PDF尺寸、窗体尺寸、PDFiumViewer尺寸的换算错误,导致图片位置偏移到边界外。
以下是原btnSavePdf_Click方法代码:
private void btnSavePdf_Click(object sender, EventArgs e) { var saveFileDialog = new SaveFileDialog(); saveFileDialog.Filter = "PDF Files (*.pdf)|*.pdf|All Files (*.*)|*.*"; if (saveFileDialog.ShowDialog() == DialogResult.OK) { string outputPdfPath = saveFileDialog.FileName; var pdfWriter = new iText.Kernel.Pdf.PdfWriter(outputPdfPath); var pdfReader = new iText.Kernel.Pdf.PdfReader(loadedPdfFilename); using (var pdfDoc = new iText.Kernel.Pdf.PdfDocument(pdfReader,pdfWriter)) { int currentPageNumber = pdfViewer1.Renderer.Page; var pageNum = pdfDoc.GetPage(currentPageNumber + 1); Document document = new Document(pdfDoc); foreach (Control control in pdfViewer1.Controls) { if (control is NewPictureBox newPictureBox) { System.Drawing.Image image = newPictureBox.Image; ImageData imageData = ImageDataFactory.Create(ImageToByteArray(image)); iText.Layout.Element.Image pdfImage = new iText.Layout.Element.Image(imageData) .ScaleAbsolute(newPictureBox.Width * newPictureBox.ZoomFactor, newPictureBox.Height * newPictureBox.ZoomFactor); float yPosition = pageNum.GetPageSize().GetHeight() - ((newPictureBox.Location.Y + pdfImage.GetImageScaledHeight()) * newPictureBox.ZoomFactor); float xPosition = pageNum.GetPageSize().GetWidth() - ((newPictureBox.Location.X + pdfImage.GetImageScaledWidth()) * newPictureBox.ZoomFactor); pdfImage.SetFixedPosition(xPosition, yPosition); document.Add(pdfImage); } } document.Close(); } MessageBox.Show("PDF saved successfully."); } } // Helper method to convert System.Drawing.Image to byte array private byte[] ImageToByteArray(System.Drawing.Image image) { using (MemoryStream memoryStream = new MemoryStream()) { image.Save(memoryStream, System.Drawing.Imaging.ImageFormat.Png); return memoryStream.ToArray(); } }
问题根源与修正方案
核心问题是屏幕控件坐标到PDF页面坐标的映射逻辑错误,需要解决三个关键差异:
- PDF坐标系以左下角为原点,WinForms控件坐标系以左上角为原点
- PDF页面的实际尺寸(单位:点,1点=1/72英寸)与PDFiumViewer的显示尺寸存在缩放比例
- 需排除PDFiumViewer的滚动偏移影响
修正后的代码
private void btnSavePdf_Click(object sender, EventArgs e) { var saveFileDialog = new SaveFileDialog(); saveFileDialog.Filter = "PDF Files (*.pdf)|*.pdf|All Files (*.*)|*.*"; if (saveFileDialog.ShowDialog() == DialogResult.OK) { string outputPdfPath = saveFileDialog.FileName; var pdfWriter = new iText.Kernel.Pdf.PdfWriter(outputPdfPath); var pdfReader = new iText.Kernel.Pdf.PdfReader(loadedPdfFilename); using (var pdfDoc = new iText.Kernel.Pdf.PdfDocument(pdfReader, pdfWriter)) { // PDFium页码从0开始,iText页码从1开始 int currentPageIndex = pdfViewer1.Renderer.Page; var pdfPage = pdfDoc.GetPage(currentPageIndex + 1); var pageSize = pdfPage.GetPageSize(); // 获取当前页面在PDFViewer中的显示区域 var pageDisplayRect = pdfViewer1.Renderer.GetPageRect(currentPageIndex); // 计算PDF实际尺寸与显示尺寸的缩放比例 float scaleX = pageSize.GetWidth() / pageDisplayRect.Width; float scaleY = pageSize.GetHeight() / pageDisplayRect.Height; // 获取PDFViewer的滚动偏移(AutoScrollPosition为负数,需取反) var scrollOffset = new Point(pdfViewer1.AutoScrollPosition.X * -1, pdfViewer1.AutoScrollPosition.Y * -1); Document document = new Document(pdfDoc); foreach (Control control in pdfViewer1.Controls) { if (control is NewPictureBox newPictureBox) { // 1. 转换控件坐标到页面渲染区域的相对坐标(减去滚动偏移) float relativeX = newPictureBox.Location.X - pageDisplayRect.X + scrollOffset.X; float relativeY = newPictureBox.Location.Y - pageDisplayRect.Y + scrollOffset.Y; // 2. 计算图片在PDF中的实际尺寸 float imgPdfWidth = newPictureBox.Width * scaleX; float imgPdfHeight = newPictureBox.Height * scaleY; // 3. 转换为PDF坐标系的绝对坐标(Y轴反转) float pdfX = relativeX * scaleX; float pdfY = pageSize.GetHeight() - (relativeY * scaleY + imgPdfHeight); // 加载图片并设置属性 ImageData imageData = ImageDataFactory.Create(ImageToByteArray(newPictureBox.Image)); iText.Layout.Element.Image pdfImage = new iText.Layout.Element.Image(imageData) .ScaleAbsolute(imgPdfWidth, imgPdfHeight) .SetFixedPosition(pdfX, pdfY); document.Add(pdfImage); } } document.Close(); } MessageBox.Show("PDF saved successfully."); } } private byte[] ImageToByteArray(System.Drawing.Image image) { using (MemoryStream memoryStream = new MemoryStream()) { image.Save(memoryStream, System.Drawing.Imaging.ImageFormat.Png); return memoryStream.ToArray(); } }
关键修正点说明
- 页码转换:PDFium的
Page属性从0开始计数,iText的页码从1开始,需做+1转换 - 缩放比例计算:通过
GetPageRect获取页面显示区域,用PDF实际尺寸除以显示尺寸得到准确的缩放比例,避免非等比例显示时的变形 - 滚动偏移处理:如果用户滚动了PDFViewer,必须减去滚动偏移量才能得到图片相对于页面的正确位置
- 坐标系反转:PDF的Y轴原点在左下角,需用页面高度减去(相对Y坐标缩放后的值+图片高度)得到正确的Y坐标
内容的提问来源于stack exchange,提问作者Yunus Derici
相关产品推荐
相关产品推荐

