如何在 C# 和 .NET 中截图并生成 PDF
Source: Dev.to
如何在 C# 与 .NET 中截取屏幕截图并生成 PDF
在许多桌面和 Web 应用程序中,用户经常需要将当前视图保存为图像或 PDF 文档。本文将展示两种常见的实现方式:
- 使用 System.Drawing(适用于 Windows 桌面)来捕获屏幕截图。
- 使用 PdfSharp(或 iTextSharp)将图像转换为 PDF。
注意:下面的代码示例仅用于演示目的,实际项目中请根据需求进行错误处理和资源释放。
1. 捕获屏幕截图
代码示例
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;
public class ScreenshotHelper
{
public static Bitmap CaptureScreen()
{
// 获取主屏幕的尺寸
Rectangle bounds = Screen.PrimaryScreen.Bounds;
Bitmap screenshot = new Bitmap(bounds.Width, bounds.Height, PixelFormat.Format32bppArgb);
// 使用 Graphics 对象将屏幕内容拷贝到位图
using (Graphics g = Graphics.FromImage(screenshot))
{
g.CopyFromScreen(bounds.X, bounds.Y, 0, 0, bounds.Size, CopyPixelOperation.SourceCopy);
}
return screenshot;
}
public static void SaveScreenshot(string filePath, ImageFormat format = null)
{
using (Bitmap bmp = CaptureScreen())
{
// 如果未指定格式,默认使用 PNG
format ??= ImageFormat.Png;
bmp.Save(filePath, format);
}
}
}
解释
Screen.PrimaryScreen.Bounds返回主显示器的像素范围。Graphics.CopyFromScreen将屏幕像素复制到Bitmap对象中。ImageFormat可选Png、Jpeg、Bmp等,默认使用 PNG 以获得无损压缩。
2. 将截图转换为 PDF
使用 PdfSharp
using PdfSharp.Pdf;
using PdfSharp.Drawing;
using System.Drawing.Imaging;
public class PdfGenerator
{
public static void CreatePdfFromImage(string imagePath, string pdfPath)
{
// 创建一个新的 PDF 文档
PdfDocument document = new PdfDocument();
document.Info.Title = "Screenshot PDF";
// 添加页面并获取 XGraphics 对象
PdfPage page = document.AddPage();
XGraphics gfx = XGraphics.FromPdfPage(page);
// 加载图像
XImage img = XImage.FromFile(imagePath);
// 调整页面大小以匹配图像尺寸(可选)
page.Width = img.PixelWidth * 72 / img.HorizontalResolution;
page.Height = img.PixelHeight * 72 / img.VerticalResolution;
// 将图像绘制到页面上
gfx.DrawImage(img, 0, 0, page.Width, page.Height);
// 保存 PDF
document.Save(pdfPath);
}
}
使用 iTextSharp(如果你更喜欢 iText)
using iTextSharp.text;
using iTextSharp.text.pdf;
using System.IO;
public class ITextPdfGenerator
{
public static void CreatePdf(string imagePath, string pdfPath)
{
using (FileStream fs = new FileStream(pdfPath, FileMode.Create, FileAccess.Write, FileShare.None))
{
// 创建文档对象,页面大小自动匹配图像
Document doc = new Document();
PdfWriter.GetInstance(doc, fs);
doc.Open();
// 将图像添加到文档中
iTextSharp.text.Image img = iTextSharp.text.Image.GetInstance(imagePath);
img.ScaleToFit(doc.PageSize.Width, doc.PageSize.Height);
img.Alignment = Element.ALIGN_CENTER;
doc.Add(img);
doc.Close();
}
}
}
3. 完整工作流示例
下面的示例演示了如何一次性完成 截图 → 保存为 PNG → 转换为 PDF 的全部步骤:
string screenshotPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "screenshot.png");
string pdfPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "screenshot.pdf");
// 1. 捕获并保存屏幕截图
ScreenshotHelper.SaveScreenshot(screenshotPath, ImageFormat.Png);
// 2. 使用 PdfSharp 将 PNG 转换为 PDF
PdfGenerator.CreatePdfFromImage(screenshotPath, pdfPath);
// 可选:删除临时 PNG 文件
File.Delete(screenshotPath);
常见问题
| 问题 | 解决方案 |
|---|---|
| 截图为空白 | 确认应用拥有对屏幕的读取权限(在 Windows 10/11 需要以管理员身份运行或在 UAC 对话框中授予权限)。 |
| 生成的 PDF 页面尺寸不正确 | 手动设置 PdfPage.Width 与 PdfPage.Height,或使用 img.ScaleToFit(iTextSharp)来适配页面。 |
| 在非 Windows 系统上运行 | System.Drawing 在 .NET Core/5+ 上对 Linux/macOS 的支持有限,建议使用跨平台库如 SkiaSharp 或 ImageSharp 来捕获屏幕。 |
| PDF 文件太大 | 使用 JPEG 而非 PNG 保存截图,或在生成 PDF 前对图像进行压缩(ImageCodecInfo + EncoderParameters)。 |
小结
- System.Drawing 提供了快速捕获屏幕的方式,适用于 Windows 桌面应用。
- PdfSharp 与 iTextSharp 都是成熟的 PDF 生成库,选择哪一个取决于项目的许可证要求和个人偏好。
- 通过上述代码,你可以轻松实现“一键截图 → PDF 导出”的功能,进一步提升用户体验。
如果你在实现过程中遇到其他问题,欢迎在评论区留言,我会尽快回复。祝编码愉快!
从 URL 截图
using System.Net.Http;
using System.Text;
using System.Text.Json;
public class PageBoltClient
{
private static readonly HttpClient _http = new();
private readonly string _apiKey = Environment.GetEnvironmentVariable("PAGEBOLT_API_KEY")!;
private const string BaseUrl = "https://pagebolt.dev/api/v1";
public async Task<byte[]> ScreenshotAsync(string url)
{
var body = JsonSerializer.Serialize(new
{
url,
fullPage = true,
blockBanners = true
});
using var request = new HttpRequestMessage(HttpMethod.Post, $"{BaseUrl}/screenshot");
request.Headers.Add("x-api-key", _apiKey);
request.Content = new StringContent(body, Encoding.UTF8, "application/json");
using var response = await _http.SendAsync(request);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsByteArrayAsync();
}
}
从 URL 获取 PDF
public async Task<byte[]> PdfFromUrlAsync(string url)
{
var body = JsonSerializer.Serialize(new { url, blockBanners = true });
using var request = new HttpRequestMessage(HttpMethod.Post, $"{BaseUrl}/pdf");
request.Headers.Add("x-api-key", _apiKey);
request.Content = new StringContent(body, Encoding.UTF8, "application/json");
using var response = await _http.SendAsync(request);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsByteArrayAsync();
}
PDF 从 HTML(替代 PuppeteerSharp / wkhtmltopdf)
public async Task<byte[]> PdfFromHtmlAsync(string html)
{
var body = JsonSerializer.Serialize(new { html });
using var request = new HttpRequestMessage(HttpMethod.Post, $"{BaseUrl}/pdf");
request.Headers.Add("x-api-key", _apiKey);
request.Content = new StringContent(body, Encoding.UTF8, "application/json");
using var response = await _http.SendAsync(request);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsByteArrayAsync();
}
System.Text.Json.JsonSerializer.Serialize 处理所有转义——无需手动构建字符串。
在 DI 中注册为单例(推荐)
// Program.cs
builder.Services.AddHttpClient();
builder.Services.AddSingleton<PageBoltClient>();
// PageBoltClient.cs — 构造函数注入版本
public class PageBoltClient
{
private readonly HttpClient _http;
private readonly string _apiKey;
private const string BaseUrl = "https://pagebolt.dev/api/v1";
public PageBoltClient(HttpClient http, IConfiguration config)
{
_http = http;
_apiKey = config["PageBolt:ApiKey"]
?? throw new InvalidOperationException("PageBolt:ApiKey not configured");
}
public async Task<byte[]> ScreenshotAsync(string url) { /* … */ }
public async Task<byte[]> PdfFromHtmlAsync(string html) { /* … */ }
}
ASP.NET Core 控制器
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("api/invoices")]
public class InvoiceController : ControllerBase
{
private readonly PageBoltClient _pagebolt;
private readonly IInvoiceService _invoices;
private readonly IRazorViewRenderer _renderer;
public InvoiceController(
PageBoltClient pagebolt,
IInvoiceService invoices,
IRazorViewRenderer renderer)
{
_pagebolt = pagebolt;
_invoices = invoices;
_renderer = renderer;
}
[HttpGet("{id}/pdf")]
public async Task<IActionResult> DownloadPdf(int id)
{
var invoice = await _invoices.GetByIdAsync(id);
var html = await _renderer.RenderAsync("Invoice", invoice);
var pdf = await _pagebolt.PdfFromHtmlAsync(html);
return File(pdf, "application/pdf", $"invoice-{id}.pdf");
}
[HttpGet("{id}/screenshot")]
public async Task<IActionResult> GetScreenshot(int id)
{
var invoice = await _invoices.GetByIdAsync(id);
var image = await _pagebolt.ScreenshotAsync($"https://yourapp.com/invoices/{id}");
return File(image, "image/png", $"invoice-{id}.png");
}
}
Azure Functions (Consumption Plan)
在这里,无浏览器方法大放异彩。由于 Azure Functions 消费计划没有可写入的目录来下载 Chromium 二进制文件,PuppeteerSharp 和 Playwright 会失败。使用 HTTP 调用则可以正常工作:
[Function("GenerateInvoicePdf")]
public async Task<HttpResponseData> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", Route = "invoices/{id}/pdf")] HttpRequestData req,
int id)
{
var html = await _renderer.RenderAsync("Invoice", id);
var pdf = await _pagebolt.PdfFromHtmlAsync(html);
var response = req.CreateResponse(HttpStatusCode.OK);
response.Headers.Add("Content-Type", "application/pdf");
response.Headers.Add("Content-Disposition", $"attachment; filename=\"invoice-{id}.pdf\"");
await response.WriteBytesAsync(pdf);
return response;
}
作者
所有代码片段均可直接放入 .NET 项目中使用。无需外部浏览器。
无需浏览器二进制文件。无需文件系统写入。适用于 Consumption 计划。
