如何在 C# 和 .NET 中截图并生成 PDF

发布: (2026年2月26日 GMT+8 14:08)
8 分钟阅读
原文: Dev.to

Source: Dev.to

如何在 C# 与 .NET 中截取屏幕截图并生成 PDF

在许多桌面和 Web 应用程序中,用户经常需要将当前视图保存为图像或 PDF 文档。本文将展示两种常见的实现方式:

  1. 使用 System.Drawing(适用于 Windows 桌面)来捕获屏幕截图。
  2. 使用 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.WidthPdfPage.Height,或使用 img.ScaleToFit(iTextSharp)来适配页面。
在非 Windows 系统上运行System.Drawing 在 .NET Core/5+ 上对 Linux/macOS 的支持有限,建议使用跨平台库如 SkiaSharpImageSharp 来捕获屏幕。
PDF 文件太大使用 JPEG 而非 PNG 保存截图,或在生成 PDF 前对图像进行压缩(ImageCodecInfo + EncoderParameters)。

小结

  • System.Drawing 提供了快速捕获屏幕的方式,适用于 Windows 桌面应用。
  • PdfSharpiTextSharp 都是成熟的 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;
}

作者

Custodia-Admin

所有代码片段均可直接放入 .NET 项目中使用。无需外部浏览器。

无需浏览器二进制文件。无需文件系统写入。适用于 Consumption 计划。

2 分钟快速入门 – 免费每月 100 次请求,无需信用卡

0 浏览
Back to Blog

相关文章

阅读更多 »

我构建的每个服务都会死

这正是重点。我是 Ontime Payments 的高级软件工程师,这是一家 fintech 初创公司,提供直接从工资中支付账单的服务。我们有意……