Morph.PDFium
July 17, 2026 ยท View on GitHub
A .NET wrapper over PDFium, using the prebuilt native binaries from pdfium-binaries (Windows, Linux, and macOS). No image library dependency: PNG encoding is built in.
It renders pages to PNG and also covers text extraction and search, navigation (bookmarks, destinations, links), annotations, AcroForm fields, page manipulation (import/merge, rotate, insert, delete, flatten), content editing, save, attachments and signatures. See native API coverage for the full wrapped surface.
See Milestones for release notes.
Open Source Maintenance Fee
This project participates in the Open Source Maintenance Fee. The source code is freely available under the terms of the license. To support sustainable maintenance, use of the project's official binary releases in revenue-generating activities and all government agencies requires adherence to the Open Source Maintenance Fee EULA. The fee is paid by sponsoring Papyrine.
This project uses SponsorCheck to surface a build-time reminder in consuming projects that are not yet sponsoring.
NuGet package
Native binaries
Morph.PDFium references all three pdfium-binaries packages (bblanchon.PDFium.Win32, bblanchon.PDFium.Linux and bblanchon.PDFium.macOS), so it runs on Windows, Linux and macOS with no extra setup. The trade-off is that a restore pulls all three (~50 MB), and a runtime-agnostic build copies every runtime into bin (~110 MB across 12 RIDs).
None of that reaches a published app. NuGet copies only the binary matching the target runtime, so dotnet publish -r win-x64 emits a single pdfium.dll.
To trim the build output as well, set UseCurrentRuntimeIdentifier in an app or test project:
<PropertyGroup>
<UseCurrentRuntimeIdentifier>true</UseCurrentRuntimeIdentifier>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
</PropertyGroup>
UseCurrentRuntimeIdentifier resolves to the runtime of the machine doing the build (win-x64 locally, linux-x64 on CI, and so on), so only the matching pdfium.dll is copied. AppendRuntimeIdentifierToOutputPath keeps output at bin/<configuration>/<tfm>/, which the runtime identifier would otherwise push down to bin/<configuration>/<tfm>/<rid>/.
Both affect build output only: a packed library is keyed by target framework rather than runtime, so setting them in one changes nothing about the resulting package.
Usage
Render a page
using var document = PdfiumDocument.Load("sample.pdf");
var png = document.RenderPage(0, dpi: 96);
Render all pages
using var document = PdfiumDocument.Load("multi-page.pdf");
List<byte[]> pages = document.RenderPages();
Document metadata
using var document = PdfiumDocument.Load("multi-page.pdf");
Console.WriteLine(document.PageCount);
Console.WriteLine(document.GetPageSizes());
Console.WriteLine(document.GetProperties());
Grayscale and other render options
RenderOptions controls dpi, grayscale, anti-aliasing, print optimization and background.
using var document = PdfiumDocument.Load("sample.pdf");
var png = document.RenderPage(0, new RenderOptions
{
Grayscale = true,
Dpi = 150
});
Render a region (clip / tile)
Render a single rectangle of a page (in page points, origin bottom-left), scaled to the dpi.
using var document = PdfiumDocument.Load("sample.pdf");
// A clip rectangle in page points (origin bottom-left): the top-left quadrant.
var clip = new PdfRectangle(0, 396, 306, 792);
var png = document.RenderRegion(
0,
clip,
new()
{
Dpi = 96
});
Extract text
using var document = PdfiumDocument.Load("sample.pdf");
using var page = document.LoadPage(0);
var text = page.GetText();
Search within a page
using var document = PdfiumDocument.Load("sample.pdf");
using var page = document.LoadPage(0);
var matches = page.Search("paragraph");
// Map a match back to rectangles on the page (e.g. for highlighting).
var first = matches[0];
var rectangles = page.GetTextRectangles(first.CharIndex, first.CharCount);
Read bookmarks (outline)
using var document = PdfiumDocument.Load("multi-page.pdf");
foreach (var bookmark in document.GetBookmarks())
{
Console.WriteLine($"{bookmark.Title} -> page {bookmark.Destination?.PageIndex}");
}
Read annotations
using var document = PdfiumDocument.Load("sample.pdf");
using var page = document.LoadPage(0);
foreach (var annotation in page.GetAnnotations())
{
Console.WriteLine($"{annotation.Type}: {annotation.Contents}");
}
Read AcroForm fields
using var document = PdfiumDocument.Load("sample.pdf");
using var form = document.LoadForm();
using var page = document.LoadPage(0);
foreach (var field in form.GetFields(page))
{
Console.WriteLine($"{field.Name} ({field.Type}) = {field.Value}");
}
Merge / import pages
using var merged = PdfiumDocument.CreateNew();
using (var first = PdfiumDocument.Load("sample.pdf"))
using (var second = PdfiumDocument.Load("multi-page.pdf"))
{
merged.ImportPages(first);
merged.ImportPages(second, "1-2");
}
var bytes = merged.Save();
Edit and save
Add content (text, rectangles), then serialize with Save.
using var document = PdfiumDocument.Load("sample.pdf");
using (var page = document.LoadPage(0))
{
page.AddRectangle(new(40, 700, 240, 760), fill: new(220, 230, 250, 255));
page.AddText("Stamped", 50, 720, fontSize: 24);
}
var stamped = document.Save();
Embedded file attachments
using var document = PdfiumDocument.Load("sample.pdf");
document.AddAttachment("notes.txt", [.. "embedded data"u8]);
var withAttachment = document.Save();
For the full list of native PDFium entry points that are wrapped (and those intentionally left out), see native API coverage.
Behavior notes
- Pages render with a white background and annotations included.
- Output dimensions are
pageSizeInPoints / 72 * dpi, rounded to the nearest pixel. The default 96 dpi renders an A4 page at 794 x 1123 and a Letter page at 816 x 1056. - The PNG includes a
pHYschunk recording the render dpi. - PDFium is not thread safe, so all native calls are serialized on a process-wide lock.
PdfiumDocumentinstances can safely be used from multiple threads, but rendering does not parallelize. - Rendering is deterministic for a given Morph.PDFium version: the same input produces byte-identical PNGs on every machine and OS, which makes the output suitable for snapshot testing. Verify.PDFium builds on this.
Icon
PDF designed by Meilia from The Noun Project.