基于C# WinForm在PDF上绘制带编号圆形的技术求助
Hey there! I’ve built similar WinForm PDF tools before, so let’s walk through how to implement your requirements step by step. We’ll use iText 7 for .NET (for core PDF manipulation) and PdfiumViewer (for WinForm PDF display) since they’re reliable and easy to integrate into your project.
Install these required NuGet packages via your project’s NuGet Package Manager:
itext7(handles PDF editing, flattening, and drawing)PdfiumViewer(provides a WinForm-compatible PDF viewer control)
Flattening layers merges all visible layer content into the main page so layers can’t be toggled anymore. Here’s how to do it when opening a PDF:
using iText.Kernel.Pdf; using iText.Kernel.Utils; using System.IO; private PdfDocument _processedPdf; private string _originalPdfPath; // Call this from your "Open PDF" button click event private void LoadAndFlattenPdf(string pdfPath) { _originalPdfPath = pdfPath; // Create a temporary in-memory stream for the flattened PDF using (var tempStream = new MemoryStream()) { var reader = new PdfReader(pdfPath); var writer = new PdfWriter(tempStream); _processedPdf = new PdfDocument(reader, writer); // Flatten all layers in the PDF var flattener = new PdfFlattener(); flattener.FlattenLayers(_processedPdf); // Reset stream position to load into the viewer tempStream.Position = 0; // Assuming you have a PdfViewer control named "pdfViewer" on your form pdfViewer.LoadDocument(tempStream); } }
Add a PdfViewer control to your WinForm (name it pdfViewer), then track the current number and handle mouse clicks to draw circles with incrementing labels:
private int _currentLabel = 1; private void pdfViewer_MouseClick(object sender, MouseEventArgs e) { if (_processedPdf == null) return; // Get the clicked page (iText uses 1-based page indices) var pageIndex = pdfViewer.CurrentPageIndex; var targetPage = _processedPdf.GetPage(pageIndex + 1); var pageSize = targetPage.GetPageSize(); // Convert WinForm screen coordinates to PDF page coordinates var pdfPoint = pdfViewer.PointToPdf(pageIndex, e.Location); // Flip Y-axis: PDF uses bottom-left origin, WinForms uses top-left var pdfY = pageSize.GetTop() - pdfPoint.Y; // Create a canvas to draw on the page var canvas = new PdfCanvas(targetPage.NewContentStreamBefore(), targetPage.GetResources(), _processedPdf); // Draw a black circle (radius 10 points) canvas.SetStrokeColor(iText.Kernel.Colors.ColorConstants.BLACK) .SetLineWidth(1) .Circle(pdfPoint.X, pdfY, 10) .Stroke(); // Draw the centered number label var font = iText.Kernel.Font.PdfFontFactory.CreateFont(iText.IO.Font.Constants.StandardFonts.HELVETICA_BOLD); var fontSize = 12; var labelText = _currentLabel.ToString(); var textWidth = font.GetWidth(labelText, fontSize); var textHeight = font.GetAscent(fontSize) - font.GetDescent(fontSize); canvas.SetFillColor(iText.Kernel.Colors.ColorConstants.BLACK) .BeginText() .SetFontAndSize(font, fontSize) .MoveText(pdfPoint.X - textWidth / 2, pdfY - textHeight / 2) .ShowText(labelText) .EndText(); // Increment the label for next click _currentLabel++; // Refresh the viewer to show the new drawing pdfViewer.Reload(); }
Save Functionality
Add a "Save" button and use this code to save your edited PDF:
private void btnSave_Click(object sender, EventArgs e) { if (_processedPdf == null) return; using (var saveDialog = new SaveFileDialog()) { saveDialog.Filter = "PDF Files (*.pdf)|*.pdf|All Files (*.*)|*.*"; saveDialog.FileName = Path.GetFileNameWithoutExtension(_originalPdfPath) + "_edited.pdf"; if (saveDialog.ShowDialog() == DialogResult.OK) { using (var fileStream = new FileStream(saveDialog.FileName, FileMode.Create)) { _processedPdf.CopyTo(new PdfWriter(fileStream)); } MessageBox.Show("PDF saved successfully!"); } } }
Print Functionality
Use PdfiumViewer’s built-in print support to integrate with the system print dialog:
private void btnPrint_Click(object sender, EventArgs e) { if (pdfViewer.Document == null) return; using (var printDialog = new PrintDialog()) { printDialog.AllowCurrentPage = true; printDialog.AllowSomePages = true; if (printDialog.ShowDialog() == DialogResult.OK) { pdfViewer.Print(printDialog); } } }
- Performance: If you’re drawing 15000+ circles, consider disabling viewer refresh during bulk operations (but since you’re doing one click at a time, this shouldn’t be an issue).
- Coordinate Accuracy: Always test the coordinate conversion—double-check the Y-axis flip to ensure circles draw exactly where you click.
- Custom Fonts: Replace the Helvetica Bold font with your preferred font using
PdfFontFactory.CreateFont("path/to/your/font.ttf").
内容的提问来源于stack exchange,提问作者TodSme

