Table of Contents

Render PDF Pages to Images

LM-Kit.NET ships two complementary APIs for turning PDF pages into raster images. They share the same underlying renderer; they differ in surface area and ergonomics. This guide explains the decision tree, then walks through the production-grade PdfRenderer workflow end to end.


🎯 Pick the right API

Need Use
Drop-in convenience, one or two lines of code, file paths only PdfToImage
Full format coverage (PNG / JPEG / WebP / BMP / TIFF / TGA / PNM), async, CancellationToken, progress, Stream and byte[] input/output, encrypted PDFs PdfRenderer
Pack every rendered page into one multi-page TIFF PdfRenderer.SavePagesAsMultipageTiff
The image data, but no file on disk PdfRenderer.RenderPageAs{Format}Bytes

If you are unsure, start with PdfRenderer. Its defaults match PdfToImage for the common case, and you get the production knobs for free as your needs grow.


⚡ The 30-second version (PdfToImage)

PdfToImage is the quick-start API. PNG, JPEG, BMP only. Synchronous. No cancellation. No progress.

using LMKit.Document.Conversion;

// Render one page to a PNG file
PdfToImage.RenderToFile("report.pdf", pageIndex: 0, "page-1.png");

// Render every page; outputs are named report-001.png, report-002.png, ...
PdfToImage.RenderToFiles("report.pdf", "thumbs/");

That is the whole API. When you need more — Async, a different format, a Stream output, an encrypted PDF, a clean cancellation — switch to PdfRenderer.


🏭 The production version (PdfRenderer)

PdfRenderer is a static class in LMKit.Document.Pdf. Every method comes in sync and async flavours, takes a CancellationToken, and lets you pick path / Stream / byte[] for input. Single-page methods write to a file, a Stream, or return a byte[]. Multi-page methods write one file per page or pack everything into a single multi-page TIFF.

Encoder knobs (PNG compression level, JPEG / WebP quality, BMP / TGA RLE) live on the method signature; render-universal knobs (zoom, pixel format, orientation, annotations, page range, password) live on a PdfRenderOptions instance shared by every overload.


✨ Format matrix

Format Single page → file Single page → Stream Single page → byte[] Many pages → directory Many pages → one file
PNG SavePageAsPng SavePageAsPng(... Stream) RenderPageAsPngBytes SavePagesAsPngs —
JPEG SavePageAsJpeg SavePageAsJpeg(... Stream) RenderPageAsJpegBytes SavePagesAsJpegs —
WebP SavePageAsWebp SavePageAsWebp(... Stream) RenderPageAsWebpBytes SavePagesAsWebps —
BMP SavePageAsBmp SavePageAsBmp(... Stream) RenderPageAsBmpBytes SavePagesAsBmps —
TIFF SavePageAsTiff SavePageAsTiff(... Stream) RenderPageAsTiffBytes SavePagesAsTiffs SavePagesAsMultipageTiff
TGA SavePageAsTga SavePageAsTga(... Stream) RenderPageAsTgaBytes SavePagesAsTgas —
PNM SavePageAsPnm SavePageAsPnm(... Stream) RenderPageAsPnmBytes SavePagesAsPnms —

Every method also has an Async variant. Cell entries marked — are not supported (no native multi-page container for that format).


🚀 Common recipes

Render every page to PNG files in a directory

using LMKit.Document.Pdf;

// Streams pages one at a time; memory stays flat regardless of page count
IReadOnlyList<string> files = PdfRenderer.SavePagesAsPngs(
    "report.pdf",
    outputDirectory: "thumbs/",
    options: new PdfRenderOptions { Zoom = 2.0 });

Stream a single page directly into an HTTP response

using LMKit.Document.Pdf;
using Microsoft.AspNetCore.Mvc;

[HttpGet("/preview/{pdfId}/{pageIndex:int}")]
public IActionResult Preview(string pdfId, int pageIndex)
{
    byte[] bytes = PdfRenderer.RenderPageAsJpegBytes(
        LookupPdfPath(pdfId), pageIndex, quality: 85);
    return File(bytes, "image/jpeg");
}

Render an encrypted PDF

using LMKit.Document.Pdf;

var options = new PdfRenderOptions { Password = "s3cret!" };
PdfRenderer.SavePageAsPng("locked.pdf", 0, "page-1.png", options);

Render with progress and cancellation

using System.Threading;
using LMKit.Document.Pdf;

using var cts = new CancellationTokenSource();
var progress = new Progress<PdfRenderProgressEventArgs>(e =>
{
    Console.WriteLine($"[{(e.PageIndex + 1)}/{e.TotalPages}] -> {e.OutputPath}");
});

await PdfRenderer.SavePagesAsPngsAsync(
    "report.pdf",
    outputDirectory: "thumbs/",
    progress: progress,
    cancellationToken: cts.Token);

Pack every page into a single archival multi-page TIFF

using LMKit.Document.Pdf;
using LMKit.Media.Image;

var options = new PdfRenderOptions
{
    Zoom = 300.0 / 72.0,             // 300 DPI
    PixelFormat = ImagePixelFormat.GRAY8, // compact archival grayscale
};

PdfRenderer.SavePagesAsMultipageTiff("scan.pdf", "archive.tif", options);
Note

SavePagesAsMultipageTiff must hold every page in memory because the native TIFF writer emits a single shared header. For very large documents, prefer SavePagesAsTiffs (one file per page) plus an external assembly step.

Render a PDF that lives in a stream or byte array

using System.IO;
using LMKit.Document.Pdf;
using LMKit.Media.Image;

// From a Stream (HTTP response body, blob storage, etc.)
using FileStream pdfStream = File.OpenRead("report.pdf");
using ImageBuffer page = PdfRenderer.RenderPage(pdfStream, pageIndex: 0);
page.SaveAsPng("page-1.png");

// From a byte array
byte[] pdfBytes = File.ReadAllBytes("report.pdf");
foreach ((int pageIndex, ImageBuffer img) in PdfRenderer.RenderPages(pdfBytes))
{
    using (img)
    {
        img.SaveAsWebp($"page-{pageIndex + 1}.webp", quality: 80);
    }
}

Probe page dimensions before rendering

using LMKit.Document.Pdf;

int pages = PdfRenderer.GetPageCount("report.pdf");
(int w, int h) = PdfRenderer.GetRenderedPageSize(
    "report.pdf",
    pageIndex: 0,
    new PdfRenderOptions { Zoom = 2.0 });
Console.WriteLine($"Document has {pages} pages; first page renders to {w}x{h} px.");

🎚️ Tuning quality vs. size

The render pipeline applies in this order: rasterise → optionally convert format → encode. The biggest knobs are zoom, pixel format, and the per-format encoder quality.

Knob Where Effect
Zoom PdfRenderOptions 1.0 = 96 DPI (matches PdfToImage); 2.0 = 192 DPI; 300.0/72.0 ≈ 300 DPI archival.
PixelFormat PdfRenderOptions RGB24 (default) for color; GRAY8 for compact archival scans; RGBA32 for compositing.
compressionLevel Per-method (PNG) 0-9. Higher = smaller file, slower encode. Default 6 is a good baseline.
quality Per-method (JPEG / WebP) 1-100. 85 is a good default; 92+ for prints; 65-75 for thumbnails.
rle Per-method (BMP / TGA) true enables RLE compression for legacy pipelines.

🧪 Verifying renders without writing to disk

PdfRenderer.RenderPageAs*Bytes is useful in tests: the returned byte array can be parsed in-memory or compared to a golden snapshot without touching the filesystem.

byte[] png = PdfRenderer.RenderPageAsPngBytes("report.pdf", 0);
Assert.That(png[0..4], Is.EqualTo(new byte[] { 0x89, (byte)'P', (byte)'N', (byte)'G' }));

📚 Additional resources

Share