PdfPecker 1.25.0

dotnet add package PdfPecker --version 1.25.0
                    
NuGet\Install-Package PdfPecker -Version 1.25.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="PdfPecker" Version="1.25.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="PdfPecker" Version="1.25.0" />
                    
Directory.Packages.props
<PackageReference Include="PdfPecker" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add PdfPecker --version 1.25.0
                    
#r "nuget: PdfPecker, 1.25.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package PdfPecker@1.25.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=PdfPecker&version=1.25.0
                    
Install as a Cake Addin
#tool nuget:?package=PdfPecker&version=1.25.0
                    
Install as a Cake Tool

PdfPecker

PdfPecker

NuGet

Fast HTML and Markdown to PDF conversion, PDF to PNG, JPEG, WebP and TIFF rendering, and PDF to HTML and Markdown, for .NET, with no dependencies.

PdfPecker has its own HTML parser, Markdown parser, CSS engine, layout engine and PDF writer, and its own PDF renderer. It does not start a browser, ship native binaries or pull in third-party packages; the NuGet package depends only on the .NET base class library.

using PdfPecker;

byte[] pdf = HtmlToPdf.Convert("<h1>Hello</h1><p>World</p>");
File.WriteAllBytes("hello.pdf", pdf);

Documentation: pdfpecker.net/docs · HTML to PDF in C#: a step-by-step guide

Install

dotnet add package PdfPecker

The package targets net8.0, net9.0, net10.0 and net11.0, so it works on .NET 8 and later, on Windows, Linux and macOS.

1.25.0 is the last release for .NET 8 and 9. Microsoft ended support for both on 10 November 2026. The next release targets .NET 10 and later only; 1.25.0 and earlier releases keep working on .NET 8 and 9. This applies to PdfPecker.Fluent, PdfPecker.Testing, PdfPecker.Razor and the pdfpecker tool too.

Quick start

// HTML string to PDF bytes, or file to file (relative images and stylesheets resolve against the HTML file's folder)
byte[] pdf = HtmlToPdf.Convert(html);
HtmlToPdf.ConvertFile("invoice.html", "invoice.pdf");

// ASP.NET Core
app.MapGet("/invoice", () => Results.File(HtmlToPdf.Convert(html), "application/pdf", "invoice.pdf"));

// Page size, margins, footer and fonts
var options = new PdfOptions
{
    PageSize = PageSize.Letter,
    Margins = PageMargins.FromMillimeters(20, 15, 20, 15),
    FooterText = "Page {page} of {pages}",
};
options.Fonts.AddFile("fonts/OpenSans-Regular.ttf");
byte[] letter = HtmlToPdf.Convert(html, options);

// A copy of shared options with one setting changed, leaving the shared ones as they are
var draft = options.Clone();
draft.HeaderText = "Draft";
byte[] draftLetter = HtmlToPdf.Convert(html, draft);

// Markdown to PDF or HTML, merging, PDF pages to PNG, and a whole PDF to one black and white fax TIFF
byte[] guide = MarkdownToPdf.Convert(File.ReadAllText("README.md"));
string guideHtml = MarkdownToHtml.Convert(File.ReadAllText("README.md"));
byte[] merged = PdfMerger.Merge(invoicePdf, termsPdf);
byte[][] pngs = PdfToImage.Convert(pdf, new ImageOptions { Dpi = 150 });
byte[] tiff = PdfToImage.ConvertToTiff(pdf, new ImageOptions { Dpi = 200, TiffColorMode = TiffColorMode.BlackAndWhite });

// Make any existing PDF smaller: losslessly, or with images downsampled to 150 dpi and recompressed as JPEG
byte[] smaller = PdfCompactor.Compact(File.ReadAllBytes("scan.pdf"));
byte[] smallest = PdfCompactor.Compact(pdf, new PdfCompactOptions { MaxImageDpi = 150, ImageQuality = 75 });

// Strip JavaScript, launch and submit actions and other active content from a PDF someone sent you
byte[] safe = PdfSanitizer.RemoveActiveContent(File.ReadAllBytes("received.pdf"));

// Redact: take every match out of the file itself (text, image pixels, shapes, fields), not just cover it
byte[] redacted = PdfRedactor.RedactText(File.ReadAllBytes("contract.pdf"), new Regex(@"\b\d{6}-\d{4}\b"));
byte[] cut = PdfRedactor.Redact(pdf, new[] { new PdfRedactionArea(pageNumber: 1, x: 72, y: 100, width: 200, height: 40) });

// Turn any existing PDF into PDF/A-2b for archiving, with a list of what changed and of anything left to fix
var archived = PdfToPdfA.Convert(File.ReadAllBytes("received.pdf"));
if (!archived.IsConformant) Console.WriteLine(string.Join("\n", archived.Problems));

// PDF/X-4 for a print shop, for the press of the ICC profile it gives you, with a 3 mm bleed and crop marks from @page
byte[] print = HtmlToPdf.Convert(flyerHtml, new PdfOptions { PdfX = new PdfXOptions(File.ReadAllBytes("PSOcoated_v3.icc"), "FOGRA51") });

// Fast Web View: the first page shows while the rest of a large PDF is still downloading
byte[] web = HtmlToPdf.Convert(html, new PdfOptions { Linearize = true });
byte[] mergedForWeb = PdfLinearizer.Linearize(PdfMerger.Merge(invoicePdf, termsPdf));
byte[] protectedForWeb = PdfLinearizer.Linearize(mergedForWeb, new PdfSecurity { UserPassword = "secret" }, password: null);

// Title, author, PDF version, page sizes, encryption and PDF/A claim of any PDF
var properties = PdfDocumentProperties.Read(File.ReadAllBytes("received.pdf"));
Console.WriteLine($"{properties.Title} by {properties.Author}: PDF {properties.Version}, {properties.PageCount} pages, {properties.PdfAConformance}");

// Any PDF as one self-contained HTML page: page images with selectable, searchable text and working links
string html = PdfToHtml.Convert(File.ReadAllBytes("statement.pdf"));

// Or its text as headings, paragraphs, lists and real <table> elements in reading order, for search and language models
string structured = PdfToHtml.Convert(File.ReadAllBytes("statement.pdf"), new PdfHtmlOptions { Mode = PdfHtmlMode.Structured });

// The same structure with the PDF's fonts, colours, backgrounds, borders and pictures
string styled = PdfToHtml.Convert(File.ReadAllBytes("statement.pdf"),
    new PdfHtmlOptions { Mode = PdfHtmlMode.Structured, IncludeStyles = true, IncludeImages = true });

// Or as Markdown: headings, lists, bold and italic, links and pipe tables, for language models and search indexes
string markdown = PdfToMarkdown.Convert(File.ReadAllBytes("statement.pdf"), new PdfMarkdownOptions { IncludePageComments = true });

// Open a PDF once and render its pages one at a time, or in parallel
var renderer = PdfRenderer.Open(pdf);
renderer.RenderPage(0, new ImageOptions { Dpi = 150 }).Save("page1.png");

The conversion methods are static and thread-safe, so you can convert many documents in parallel. The few instance types are FontCollection and PdfRenderer, which can be shared by parallel work, and StreamingPdfMerger, which is not thread-safe: it builds one output and is used from one thread at a time.

Fluent API

PdfPecker.Fluent builds the same PDFs from C# code instead of HTML, in the style of QuestPDF, so most QuestPDF documents port by changing the using lines. It is a separate package from this repository, released with every PdfPecker release under the same version, and its only dependency is PdfPecker.

dotnet add package PdfPecker.Fluent
using PdfPecker.Fluent;

Document.Create(container => container.Page(page =>
{
    page.Size(PageSizes.A4);
    page.Margin(2, Unit.Centimetre);
    page.Header().Text("Invoice 2026-104").FontSize(20).Bold();
    page.Content().Table(table =>
    {
        table.ColumnsDefinition(columns => { columns.RelativeColumn(3); columns.RelativeColumn(); });
        table.Cell().Text("Consulting");
        table.Cell().AlignRight().Text("1,200.00");
    });
    page.Footer().AlignCenter().Text(x => { x.CurrentPageNumber(); x.Span(" / "); x.TotalPages(); });
}))
.GeneratePdf("invoice.pdf");

It writes compact HTML and CSS and renders it with PdfPecker, so every PdfOptions feature (PDF/A, PDF/UA, signatures, e-invoices) works with it. Its DocumentOperation takes QuestPDF's calls for existing PDFs (choosing and merging pages, overlays and underlays, attachments, encryption, linearization) and runs them on PdfPecker's merger, stamper and protector. What it covers and how it differs from QuestPDF is in its README.

Command line

PdfPecker.Cli is the same library as a pdfpecker command, for scripts, build pipelines and designing a template. It is a dotnet tool released with every PdfPecker release under the same version, and like the library it needs no browser, no JavaScript and no network.

dotnet tool install -g PdfPecker.Cli

pdfpecker convert invoice.html                       # writes invoice.pdf; .md files are Markdown
pdfpecker watch invoice.html                         # converts again on every save, for a live preview in your PDF viewer
pdfpecker merge cover.pdf report.pdf:2-9 -o all.pdf  # also split, extract, remove, rotate, stamp, compact, linearize
pdfpecker sign all.pdf signed.pdf --cert me.pfx      # also verify, encrypt, decrypt, sanitize
pdfpecker to-image all.pdf pages --format webp       # also to-html, text, info

On a .NET 10 or 11 SDK the tool installs as a Native AOT executable for Windows, macOS and Linux (x64 and Arm64, glibc and musl), so a command starts in milliseconds instead of the third of a second the JIT needs: converting the sample invoice took 17 ms in all instead of 355 ms on a 4-core Linux VM. Other platforms and .NET 8 and 9 SDKs get the framework-dependent tool, as before.

A call without a command word is read like wkhtmltopdf's, so a script can switch by changing the program name: pdfpecker -s A4 -T 15mm --footer-center "[page] / [topage]" in.html out.pdf. Page size, orientation, margins, headers and footers with their tokens, zoom, style sheets and quiet mode work as they do there; options that exist because wkhtmltopdf runs a browser (--javascript-delay, --cookie, --header-html, ...) are ignored with a warning. Web addresses are refused. Its README is here.

Testing PDFs

PdfPecker.Testing adds snapshot tests for the PDFs your code makes. PdfAssert.MatchesSnapshot compares a PDF with a stored one, leaving out dates, file IDs and stream compression, and when they differ it writes the new PDF and page images with the changed pixels marked. It works with any test framework, because a failed check throws. It is released with every PdfPecker release under the same version, and its only dependency is PdfPecker.

dotnet add package PdfPecker.Testing
using PdfPecker.Testing;

byte[] pdf = HtmlToPdf.Convert(invoiceHtml);
PdfAssert.MatchesSnapshot(pdf, "invoice");   // Snapshots/invoice.pdf next to the test file
PdfAssert.ContainsText(pdf, "Total due: 1,500.00");
PdfAssert.HasPageCount(pdf, 1);

How snapshots are created and accepted, and the appearance comparison for PDFs from other producers, are in its README.

Razor components

PdfPecker.Razor turns Razor components (.razor files) into PDFs, so a template gets typed parameters, compile-time checks, child components, layouts and dependency injection. It renders the component to HTML with Microsoft's HtmlRenderer, without an ASP.NET Core host, and converts the HTML with PdfPecker. It is released with every PdfPecker release under the same version, and it is the one package here with a dependency besides PdfPecker: Microsoft's Microsoft.AspNetCore.Components.Web, which holds the renderer.

dotnet add package PdfPecker.Razor
using PdfPecker.Razor;

// Invoice.razor has [Parameter] public InvoiceModel Data { get; set; }
byte[] pdf = await RazorToPdf.ConvertAsync<Invoice>(new Dictionary<string, object?> { ["Data"] = invoice });

Razor encodes caller data, every PdfOptions setting works, and the package is trimmable and runs with Native AOT (Microsoft's components assembly reports trim warnings of its own, which its README explains). How to set up the Razor project, inject services and stream to an ASP.NET Core response is in its README.

Features

  • HTML and CSS: flexbox, grid and subgrid, CSS tables (collapsed borders also from col and colgroup; rows and columns with visibility: collapse leave), floats with shape-outside, clip-path shapes (also rect() and xywh()), sizes that fill the containing block (width: stretch and -webkit-fill-available, also for heights and their limits), percentage heights inside flex items resolved as in Chrome (stretched items, and items flexed from a length basis in a column whose container has only a min-height, as card and label layouts use), corner-shape (squircle, bevel, scoop, notch and superellipse() corners on borders, backgrounds, shadows, outlines and clips), overflow: clip with overflow-clip-margin, zoom (as Chrome computes it, for templates that shrink a page to fit), multi-column layout, gap decorations (column-rule and row-rule in grid and flex containers too, with lists and repeat(), rule-break, rule-inset, rule-visibility-items and rule-overlap, cut where a grid or flex container breaks across pages), positioning (also the legacy clip: rect(), and absolute boxes inside a positioned inline element, such as tooltips and badges), transforms, gradients (also in oklch and other colour spaces, and as list markers; translucent stops blend premultiplied, as in Chrome), contrast-color() (black or white text, whichever reads better on a brand colour, as in Chrome), fixed background images (placed on each page's paper, as Chrome prints them), filters, masks, media and container queries, @scope (scope roots, limits and proximity, as in Chrome), logical properties, writing-mode (with floats, sized boxes and vertical-lr line order; Chinese and Japanese characters stand upright with the font's vertical forms, as text-orientation says, text-combine-upright sets dates and numbers across the line, and images stay upright, as in Chrome), text-wrap and white-space-collapse, hanging indents (text-indent with hanging and each-line), emphasis marks (text-emphasis), ruby annotations such as Japanese furigana (<ruby>, <rt> and <rp>: the annotation at half size over its base, spread around it and over the kana beside it, in the line's leading where it fits, as in Chrome), trimming lines to the cap height and baseline (text-box, for optically centred buttons and badges), line-clamp with your own block-ellipsis, underlines from the font or below the descenders (text-underline-position), solid underlines, overlines and line-throughs as thick and placed as Chrome draws them, drop caps, ::first-line styles with a background, ::marker styles (a larger marker font or a tall marker image makes its line taller, content: none hides the marker, counter(list-item) numbers it and its own direction orders its text, as in Chrome), images and gradients in ::before and ::after content, inline SVG (with percentage coordinates and sizes, as in Bootstrap's placeholder images, cx, cy, r, rx, ry, x, y, width, height, d: path() and stop-color set from style sheets, HTML in <foreignObject>, and icons drawn with <use> from a sprite of symbols that is itself hidden, in the currentColor where they are used, as in Bootstrap's icon sprites; lengths in mm, cm, in, pt and em; nested <svg> and <symbol> viewports with preserveAspectRatio and clipping; also CSS transform with transform-box and transform-origin on SVG elements, such as a gauge needle turning about its own centre, and vector-effect: non-scaling-stroke for hairlines in a scaled chart, though markers still scale) and PNG, JPEG, GIF, WebP, BMP and TIFF images (also chosen by srcset, <picture> and image-set(), shown by <embed> and <object>, photos turned upright by their EXIF orientation, and image-rendering: pixelated or crisp-edges writing enlarged images, such as QR codes and pixel art, with hard pixel edges in every PDF viewer, and object-view-box cropping a raster image in CSS, as Chrome does), and CSS-only charts such as Charts.css (column, bar, area, line and pie charts), laid out as Chrome lays them out. Typed attr() works in any property, as in Chrome: width: attr(data-pct %), color: attr(data-color type(<color>), gray) or calc(attr(data-v number) * 1mm) draw bar charts and badges from data attributes, with fallbacks, ::before reading its element's attributes, and no attribute value ever reaching a url(). sibling-index() and sibling-count() work as in Chrome too, for staggered widths, colours that step through a list and tabs that share a row evenly. if() chooses a value from style() of a custom property, media() or supports(), such as border-color: if(style(--status: overdue): red; else: green), as in Chrome (whose media() in if() still asks about the screen when it prints). Older CSS works too: the -webkit- property names Chrome still takes (-webkit-transform, -webkit-flex, ...), the legacy display: -webkit-box and its -webkit-box- properties, and the -webkit-gradient() and prefixed gradient syntaxes. As in browsers, a length without a unit (width: 200) counts as pixels only in a page without a doctype; with <!DOCTYPE html> it is ignored. Without a doctype, lines also follow Chrome's quirks-mode line heights: a line of small <font> text, or an image alone in a table cell, is only as tall as its content. The old body attributes topmargin, leftmargin, marginheight and marginwidth set the page margins as they do in Chrome. More
  • Paged media: @page sizes (also a page as tall as its content, size: 80mm auto, for receipts on a roll), borders, padding and background images (which cover the whole sheet) and margin boxes (also with running elements, position: running() and element(), as in Prince and WeasyPrint), a bleed with crop marks and registration crosses for a print shop (bleed: 3mm; marks: crop cross, written as the PDF's trim and bleed boxes, as in WeasyPrint), CMYK colours for print with device-cmyk() of CSS Color 5 (color: device-cmyk(0 0 0 1) for text in black ink only, written as DeviceCMYK for text, backgrounds, borders, rules and SVG fills and strokes; gradients, shadows, filters and colour mixes use its plain sRGB conversion, as does PDF/A, whose output intent is sRGB), @page :nth(2n+1) and page groups, :nth(1 of chapter) (as in WeasyPrint; they style margin boxes and the page background, not the page size or margins), running headers and footers (also a first page of its own, even pages of their own for printing on both sides, and bands of their own for named pages), position: fixed boxes repeated on every page of their named page, also in the page margins with negative offsets, and over the running headers and footers unless their z-index is negative (for watermarks and page frames), footnotes, tables of contents with page numbers, the page count anywhere in the content (counter(pages), as in Prince), the first and last page of a section and page numbers counted within it (-pdfpecker-target-pages(), and {section-page:id} in running headers and footers), page numbers in running headers and footers in any counter style ({page:upper-roman}, {total-pages:lower-alpha}, {section-page:id:lower-roman}, or a zero-padded @counter-style of your own), break control (also table row groups and group header rows kept together, as reports keep a group on one page, and forced breaks between table rows and between flex lines), table rows that break inside at a page's end as in Chrome, unless a cell would have nothing before the break (also rows with cells that span rows, whose spanning cells go on below the repeated header), flex rows whose items break at a page's end as in Chrome (the row moves whole when an item is kept whole), table headers and footers repeated on every page (also around a row split across pages), boxes split across pages or columns that repeat their border, padding, rounded corners, shadow and background on each page with box-decoration-break: clone (also tables with separate borders), and bookmarks from the headings that bookmark-level, bookmark-label and bookmark-state can extend, rename or close. More
  • Content scaled to fit a box: -pdfpecker-scale-to-fit: shrink fits text of any length into a fixed space, for labels, badges and certificates. More
  • Content kept to one page or box: continue: discard (CSS Overflow 4) keeps the part of a box that fits on the page it starts on, or in its height or max-height, and leaves the rest out instead of continuing on the next page. Lines, images and table rows that would cross the end are left out whole.
  • Content repeated on each page: -pdfpecker-repeat: each-page draws a box again at the top of each later page its parent continues on, such as a label in a row beside a text several pages long.
  • Smaller images while converting: -pdfpecker-image-quality: 75 writes images as JPEGs at that quality, and -pdfpecker-image-max-resolution: 150dpi downsamples images drawn denser than that, judged by the size each is drawn at. Both are inherited, so body { ... } sets them for the document and none turns them off for one image; transparency is kept losslessly, and an image is only replaced when the result is smaller.
  • Markdown: CommonMark 0.31 and GitHub Flavored Markdown to PDF or HTML, with footnotes, definition lists and YAML front matter. More
  • Fonts: TrueType, OpenType, WOFF and WOFF2, subset and embedded, with kerning, ligatures, contextual alternates (as Chrome applies them, such as Fira Code's ligatures), variable fonts and OpenType features, and bundled fallback fonts for letters and symbols a document's fonts lack. COLR colour fonts, versions 0 and 1 (such as Noto Color Emoji), print in colour as Chrome prints them, in the default palette or the one font-palette picks: light, dark or an @font-palette-values rule with base-palette and override-colors. font-variant-alternates turns on historical forms and the stylistic sets, character variants, stylistic, swash, ornament and annotation alternates that @font-feature-values names for a family. Variation sequences pick the glyph the font gives them (cmap format 14), such as the forms of a CJK ideograph that ideographic variation selectors choose, and copy as written. @font-face takes size-adjust, ascent-override, descent-override and line-gap-override, so a fallback face can be sized to match a web font. FontCollection.AddDeferred registers a font that is read only when a document uses it, with a unicode-range so that fallback skips it for other characters, which suits large CJK fonts. More
  • Languages: Arabic and Hebrew with bidi and shaping, Indic scripts, Khmer, Myanmar, Sinhala, Tibetan, Thai and Lao (copied and extracted as written, with /ActualText where shaping reorders or shares glyphs), CJK line breaking and justification, text-autospace (an eighth of an em between Chinese, Japanese or Korean text and Latin letters or digits, as in Chrome), and hyphenation in eleven languages (with hyphenate-limit-lines), plus TeX patterns of your own, which HyphenationPatterns.Deferred reads only when a document uses their language. More
  • PDF/A, passwords and watermarks: PDF/A-2 and 3 (levels B, U and A, the accessible level, tagged from the HTML) and PDF/A-4, AES-256 encryption (passwords prepared with SASLprep, as PDF 2.0 requires), and text, image and SVG watermarks. More
  • PDF/X-4 for print shops: new PdfOptions { PdfX = new PdfXOptions(File.ReadAllBytes("PSOcoated_v3.icc"), "FOGRA51") } writes a PDF/X-4 file (ISO 15930-7) for the press whose ICC output profile you pass: PDF 1.6 with the output intent, a trim box on every page (the paper, inside the bleed box of @page { bleed: 3mm }), sRGB colours and images tagged with an sRGB profile for the print shop's colour conversion, CMYK JPEGs as DeviceCMYK, embedded fonts and the PDF/X metadata. PdfPecker bundles no press profile (the print shop, the ICC or the ECI provides one), does no trapping, and leaves links and fillable fields out, since PDF/X allows no annotations on the printed page. veraPDF has no PDF/X profile, so the files are checked by tests of each requirement and by rendering in Ghostscript and Poppler, not by a PDF/X validator.
  • Existing PDFs to PDF/A: PdfToPdfA converts PDFs from any tool to PDF/A-2b, 2u, 3b or 3u without rasterizing them. It embeds fonts that are not embedded (with the bundled glyphs, at the document's own widths), repairs embedded TrueType fonts whose glyphs, widths or character maps disagree with the PDF, adds the output intent and XMP metadata, removes scripts and other forbidden actions, gives annotations and form fields appearances, makes colours device independent, and keeps or converts attachments as the level allows. The result lists every change and every problem it could not fix, such as text in a font whose glyphs PdfPecker does not have (CJK fonts that are not embedded, for instance), and veraPDF checks the converted samples in CI. Encrypted and signed PDFs are refused.
  • Accessibility: tagged PDF, PDF/UA-1 and PDF/UA-2 built from your HTML. ARIA roles tag what HTML has no element for: role="math" (with aria-label) a formula, role="doc-toc" a table of contents whose list items are its entries, and role="doc-index" an index. data-pdf-list-label marks a list item's own label, such as a number in a flex row, when the item has no list marker. More
  • Forms: HTML inputs become fillable PDF fields; read, fill and flatten the fields of existing PDFs. Check boxes and radio buttons take accent-color, and appearance: none leaves them to the page's own CSS (:checked, ::before and ::after), as for toggle switches; such a control is drawn rather than made a field. An element with data-pdf-signature="Buyer" becomes an empty signature field over its box, which PdfSigner fills later with FieldName = "Buyer". More
  • Digital signatures: PAdES signing, certification signatures, timestamps, external signers, verification (SHA-1 signatures count as not valid unless you allow them) and long-term validation. More
  • E-invoices and attachments: Factur-X, ZUGFeRD, XRechnung and Peppol BIS 3.0 XML built from invoice data and checked against EN 16931, a ready-made invoice layout, and embedded files. More
  • Barcodes: QR codes, EPC payment QR codes, Swiss QR-bill codes (with the Swiss cross and a checked payload), Data Matrix and GS1 Data Matrix, Code 128 and EAN-13 as vector SVG, e.g. Barcode.QrCode("https://example.com") or Barcode.Gs1DataMatrix("(01)09506000134352(10)AB-123"). More
  • Reading barcodes: Barcode.Read(image) reads the QR, Data Matrix (including the rectangular DMRE sizes), Code 128 and EAN-13 codes in a PNG, JPEG, GIF, WebP, BMP or TIFF image, and PdfBarcodes.Find(pdf) those on PDF pages, whether drawn as vectors, embedded as images or scanned. Codes may be turned, tilted, mirrored or light on dark; each result has its text and bytes, its corners in pixels or page points, a GS1 flag, and the payment parsed from an EPC or Swiss QR-bill code. On the zxing-cpp test images it reads about 85 % of what zxing-cpp reads, with no wrong reads.
  • Existing PDFs: merge (also streamed), split, reorder, rotate, stamp, extract text with positions, extract images and read document properties. Text comes out in reading order: a tagged PDF's in the order of its tags, and columns side by side one after the other, also on pages turned sideways or upside down. More
  • Compacting and Fast Web View: PdfCompactor makes any PDF smaller without changing how it looks, and PdfOptions.Linearize or PdfLinearizer writes a linearized PDF whose first page shows before the rest arrives, also encrypted: PdfLinearizer opens a protected PDF with its password and encrypts the result in the same pass. More
  • Removing active content: PdfSanitizer takes JavaScript, actions that launch programs, open other files or submit form data, XFA and media annotations out of PDFs from others, together with links to anything but web, e-mail and phone addresses (network paths, smb: and other protocol handlers), and HasActiveContent tells whether a PDF has any. Pages, text, web links and form fields stay.
  • Redaction: PdfRedactor removes what pages draw in given areas, or wherever a text or regular expression matches, from the file itself: the glyphs leave the content streams (so text extraction and copying no longer find them, while the text around them keeps its place), image pixels under an area are overwritten, and shapes, links and form fields in an area go, before a black box (or none) is drawn over it. Forms shared with other pages are redacted in copies, the originals of redacted images and forms leave the file unless a page still shows them elsewhere, replacement texts that would still name what was there are removed, and earlier revisions do not survive.
  • Images to PDF: ImageToPdf puts scans and photos on pages of their own size or fitted to a page size, with photos turned upright by their EXIF orientation. More
  • Photo privacy: PdfOptions.StripImageMetadata removes EXIF (GPS location included), XMP, IPTC and comments from embedded JPEGs while keeping their pixels and colours.
  • PDF to image: renders any PDF page to PNG, JPEG or WebP, or a whole PDF to a multi-page TIFF, and HtmlToImage goes from HTML to images in one call. PdfRenderer.RenderPage(page, buffer, ImagePixelFormat.Gray) renders a page straight into a buffer of yours as grey, RGB or RGBA pixels, sized by GetPixelSize, for OCR and image processing. Comments that carry no appearance of their own (notes, highlights, underlines, strike-outs, squiggly lines, shapes, lines with their arrowheads, ink and free text) are drawn from their properties, as viewers draw them. More
  • Image decoding: ImageToPixels.Convert(image) reads a PNG, JPEG, GIF, WebP, BMP or TIFF file (any page of a multi-page TIFF) straight to grey, RGB or RGBA pixels, with the same colour conversions, EXIF orientation and size limits as ImageToPdf and without making a PDF of it: for OCR, image analysis and thumbnails.
  • PDF to HTML: PdfToHtml turns any PDF into one script-free HTML file that looks like the PDF, with text you can select, search and copy. PdfHtmlMode.Structured writes the text instead as flowing headings, paragraphs, lists and real <table> elements in reading order, with nothing placed at a fixed position, for search indexes and language models. A tagged PDF (PDF/UA, or any PDF with a structure tree) is read from its tags, which say exactly what each heading, list item and table cell is. Other PDFs are read from their layout: tables come from the rules and borders the PDF draws and from text that lines up in columns, with header rows, spanned cells and wrapped cells. Reading the layout can turn unusual layouts, floats and charts into plain paragraphs, and scanned pages have no text to give. IncludeStyles keeps how the page looks in that structure: fonts, sizes, colours, backgrounds, rounded boxes, table borders, column widths and alignment, as CSS classes. IncludeImages adds the page's pictures as <img> elements. More
  • PDF to Markdown: PdfToMarkdown writes the same structure as GitHub Flavored Markdown: headings, paragraphs, bulleted and numbered lists, bold and italic text, links, and pipe tables, with several header rows joined column by column and spanned cells in their first row and column. Line breaks the PDF made on purpose stay, and text that would read as Markdown is escaped. IncludePageComments marks where each page starts, so a passage can be traced to its page, and IncludeImages adds the pictures as data: images.
  • Viewer settings and page labels: the page, zoom and panel a viewer opens with, print settings, and page numbers such as "i" to "iv" for front matter. More
  • Reproducible output: a fixed CreationDate or SOURCE_DATE_EPOCH gives byte-identical PDFs, for build pipelines and output caches. More
  • Safe failures: errors share the base class PdfPeckerException, and the *File methods never leave a half-written output. More

PdfPecker is built for HTML templates you control: it runs no JavaScript, and conversion never fetches anything from the network (remote images and stylesheets load only through a ResourceResolver you supply; those in data: URLs need none). Only signing goes online, and only when you ask: a timestamp from the server you name in TimestampUrl, OCSP and CRL data for long-term validation when you turn on FetchRevocationData (from public addresses only, unless your UrlFilter allows others), and online revocation checks when verification uses X509RevocationMode.Online. See the limitations for what it does not do yet.

How it compares

The same three documents converted to A4 PDF by PdfPecker and six .NET alternatives, on one 4-core cloud VM (Intel Xeon @ 2.1 GHz, 260 MB L3, 16 GB, Linux, .NET 10.0.13). Times are medians after a 3-second warm-up, averaged over two runs; "first PDF" includes starting the browser where there is one. Parallel runs one conversion per core at once on the 5-page document. Peak memory is the proportional set size (PSS) of the whole process tree, so a browser's helper processes are included once and shared pages are not double-counted; .NET processes run with a 16 MB GC gen0 budget, as .NET would otherwise size it from the CPU cache.

PdfPecker is 50 to 200 times faster than the Chromium-based converters on the small documents and uses an eighth or less of their memory. How to read the table and how it was measured is in Performance; the benchmark itself, to run on your own machine, is in PdfPecker.Benchmarks.

Converter Engine To deploy First PDF Invoice, 1 page Feature tour, 5 pages Report, 30 pages Parallel Peak memory
PdfPecker own, managed 6.3 MB DLL 0.40 s 0.92 ms 2.9 ms 33 ms 1,132 docs/s 92 MB
HtmlRenderer.PdfSharp 1.6.1 own, managed (CSS 2) 5.0 MB of DLLs 0.54 s 4.9 ms 16 ms 360 ms 114 docs/s 255 MB
iText pdfHTML 6.3.4 own, managed 11 MB of DLLs 1.18 s 24 ms 132 ms 2.2 s 15 docs/s 707 MB
wkhtmltopdf 0.12.6 Qt WebKit, native about 90 MB with Qt 0.33 s 282 ms 309 ms 600 ms 12 docs/s 165 MB
PuppeteerSharp 25.13 Chromium 141 about 600 MB browser 1.15 s 106 ms 142 ms 496 ms 15 docs/s 813 MB
Microsoft.Playwright 1.63 Chromium 141 browser + 135 MB driver 0.98 s 131 ms 167 ms 517 ms 12 docs/s 1,027 MB
Syncfusion HTML to PDF 35.1.40 Chrome (Blink), bundled about 545 MB with Chrome 2.46 s 183 ms 236 ms 968 ms 3 docs/s 2,239 MB

Building

dotnet build
dotnet test
dotnet run --project samples/PdfPecker.Sample -c Release      # converts samples/PdfPecker.Sample/html/*.html and *.md into ./output
dotnet run --project samples/PdfPecker.Fluent.Sample -c Release   # writes the PdfPecker.Fluent examples into ./output
dotnet run --project src/PdfPecker.Cli -c Release -- convert in.html out.pdf   # the pdfpecker command-line tool
dotnet run --project samples/PdfPecker.Razor.Sample -c Release    # writes the PdfPecker.Razor invoice into ./output
dotnet run --project bench/PdfPecker.Benchmarks -c Release
dotnet run --project bench/PdfPecker.Benchmarks -c Release -- --requirements    # memory and CPU per conversion
bench/compare-versions.sh v1.24.0                              # this checkout against a release, in turns on this machine
dotnet pack src/PdfPecker -c Release -o ./nupkg                # also checks the public API against the last release
dotnet pack src/PdfPecker.Fluent -c Release -o ./nupkg         # the fluent package, with the same version
dotnet pack src/PdfPecker.Cli -c Release -o ./nupkg            # the pdfpecker dotnet tool, with the same version
dotnet pack src/PdfPecker.Testing -c Release -o ./nupkg        # the snapshot testing package, with the same version
dotnet pack src/PdfPecker.Razor -c Release -o ./nupkg          # the Razor package, with the same version

Requirements and details are in BUILD.md and TESTING.md; how to fuzz the parsers is in fuzz/README.md.

To contribute, see CONTRIBUTING.md. Report security problems privately as described in SECURITY.md.

License

MIT. The third-party fonts, font data and code built into the library are listed, with their licenses, in THIRD-PARTY-NOTICES.md.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed.  net11.0 is compatible. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

    • No dependencies.
  • net11.0

    • No dependencies.
  • net8.0

    • No dependencies.
  • net9.0

    • No dependencies.

NuGet packages (10)

Showing the top 5 NuGet packages that depend on PdfPecker:

Package Downloads
PdfPecker.Fluent

Fluent C# API for building PDFs with PdfPecker: pages, headers, footers, columns, rows, tables, text, images and barcodes in code. It writes HTML and CSS and renders them with PdfPecker, so it needs no browser and no native binaries.

PdfPecker.Testing

Snapshot tests for PDFs with PdfPecker: compare a generated PDF with a stored one after leaving out dates, file IDs and compression, and get page images with the changed pixels marked when they differ. Also checks for text and page counts. Works with xUnit, NUnit, MSTest or any other test framework, because a failed check throws.

PdfPecker.Razor

Razor components (.razor) to PDF with PdfPecker: typed templates with layouts, child components and dependency injection, rendered to HTML with Microsoft's HtmlRenderer and converted to PDF without a browser, an ASP.NET Core host or native binaries.

PdfPecker.Email

Convert e-mail (.eml, MIME, Outlook .msg and winmail.dat) to PDF and PDF/A-3 with PdfPecker: headers, HTML or plain text bodies, inline images and the attachments embedded in the PDF. Dependency-free apart from PdfPecker; HTML bodies are sanitised and nothing is fetched from the network.

PdfPecker.Ocr

Dependency-free OCR for .NET: reads text from scanned PDFs and images and makes scanned PDFs searchable. Its own neural network runtime in C#, no native binaries.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.25.0 162 11/12/2026
1.24.0 447 11/10/2026
1.23.0 696 11/4/2026
1.22.0 339 11/1/2026
1.21.0 230 10/28/2026
1.20.0 580 10/25/2026
1.19.0 206 10/22/2026
1.18.0 178 10/21/2026
1.17.0 131 10/21/2026
1.16.0 128 10/20/2026
1.15.0 120 10/19/2026
1.14.2 120 10/18/2026
1.14.1 103 10/18/2026
1.14.0 108 10/18/2026
1.13.0 2,549 10/18/2026
1.12.0 257 10/18/2026
1.11.0 491 10/18/2026
1.10.0 383 10/17/2026
1.9.0 693 10/16/2026
1.8.0 161 10/16/2026
Loading failed

What changed in each release: https://pdfpecker.net/docs/changelog/