Bhowra.Ink 1.0.11

dotnet add package Bhowra.Ink --version 1.0.11
                    
NuGet\Install-Package Bhowra.Ink -Version 1.0.11
                    
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="Bhowra.Ink" Version="1.0.11" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Bhowra.Ink" Version="1.0.11" />
                    
Directory.Packages.props
<PackageReference Include="Bhowra.Ink" />
                    
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 Bhowra.Ink --version 1.0.11
                    
#r "nuget: Bhowra.Ink, 1.0.11"
                    
#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 Bhowra.Ink@1.0.11
                    
#: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=Bhowra.Ink&version=1.0.11
                    
Install as a Cake Addin
#tool nuget:?package=Bhowra.Ink&version=1.0.11
                    
Install as a Cake Tool

Bhowra.Ink — Serverless HTML to PDF for .NET

NuGet NuGet Downloads

Serverless HTML-to-PDF for .NET — lightweight, no browser, modern CSS rendering. Bhowra.Ink is a zero-dependency, pure-C# engine in one DLL (just over 1 MB) that runs in-process on Azure Functions (including the Consumption plan), AWS Lambda and containers — no Chromium, no headless browser, no native binaries, no browser cold start. It renders modern HTML5 + CSS (Grid, flexbox, multi-column, var()/calc(), gradients, border-radius, box-shadow, transforms incl. 3D, SVG) to spec.

A fully managed alternative to headless-browser (Chromium-based) converters and AGPL-licensed PDF libraries. No AGPL. No cold start. Zero third-party dependencies.

Use it for HTML-to-PDF in C#, ASP.NET Core, Blazor, Azure Functions, and AWS Lambda — invoices, hybrid e-invoices (Factur-X / ZUGFeRD, ZATCA), payslips, statements, reports, and contracts — where launching a headless browser per request is too slow, too heavy, or simply not allowed.


Install

dotnet add package Bhowra.Ink

Targets .NET 8. One managed DLL (just over 1 MB, fonts embedded). Works on Windows and Linux, including Azure Functions / AWS Lambda / containers.

Quickstart

using Bhowra.Ink;

string html = "<h1 style='color:teal'>Hello, PDF</h1><p>Rendered in pure .NET.</p>";

// Simplest: straight to a file
HtmlConverter.ConvertToFile(html, "out.pdf");

// Or get bytes + a result you can inspect (Convert never throws — check Success)
ConversionResult result = HtmlConverter.Convert(html, new ConverterProperties { Title = "Demo" });
if (!result.Success) throw new InvalidOperationException(result.ErrorMessage);
Console.WriteLine($"{result.PageCount} page(s), {result.SizeBytes} bytes, {result.ConversionTimeMs} ms");
result.SaveToFile("out.pdf");

Output options: ConvertToFile, ConvertToBytes, ConvertToBytesAsync (fetches remote images concurrently), ConvertToStream, ConvertFileToFile (uses the HTML file's folder as BaseUri), and Convert. The Convert*To* methods throw on failure; Convert instead returns a ConversionResult with Success/ErrorMessage, the bytes, page count, timing, and three diagnostic lists — FontDiagnostics, ImageDiagnostics, and LayoutDiagnostics (structural recoveries such as an inconsistent table grid or nesting-depth truncation). Forward all three to your logger.

Convert HTML to PDF in Azure Functions (serverless)

Because Bhowra.Ink runs in-process with no Chromium and no native binaries, it works on Azure Functions, AWS Lambda, and Linux containers with no cold-start browser launch and nothing to install on the host — just add the package and call HtmlConverter:

[Function("InvoicePdf")]
public async Task<HttpResponseData> Run(
    [HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequestData req)
{
    string html = await new StreamReader(req.Body).ReadToEndAsync();
    byte[] pdf   = HtmlConverter.ConvertToBytes(html);

    var res = req.CreateResponse(System.Net.HttpStatusCode.OK);
    res.Headers.Add("Content-Type", "application/pdf");
    await res.Body.WriteAsync(pdf);
    return res;
}

The same code runs unchanged on Windows Consumption plans, Linux, AWS Lambda, and Docker — there are no per-platform native builds to manage.


Why Bhowra.Ink? (vs headless-browser converters & AGPL/commercial PDF libraries)

Concern Chromium / headless-browser converters AGPL or commercial PDF libraries Bhowra.Ink
Cold start (serverless) 5–15 s to launch a browser varies in-process, no browser launch
Dependencies Chromium + native binaries AGPL or per-seat commercial zero — pure .NET 8 BCL
Deployment size 150–400 MB tens of MB just over 1 MB, one DLL
Linux / Windows often native-only varies cross-platform managed
Modern CSS (Grid, gradients, SVG) yes frequently weak yes
License — often AGPL or costly commercial; watermark-on-trial

What it renders

CSS & layout

  • Full CSS cascade — specificity, inheritance, shorthand expansion, !important, inline styles
  • External stylesheets — <link rel="stylesheet"> and @import resolved into the cascade in document order (secure-by-default; see External stylesheets)
  • Selectors: type, class, id, attribute, descendant/child/adjacent/sibling, :nth-child, :nth-of-type, :first-/:last-/:only-child, :first-/:last-of-type, :empty, :root, :not, :is, :where, ::before/::after (incl. counters), ::marker
  • Flexbox and CSS Grid (incl. repeat(), minmax(), auto-fit/auto-fill); display: inline-block on any element
  • var(), calc(), position (relative/absolute/fixed + z-index), float
  • Multi-column — columns, column-width, column-count, column-gap, column-fill, and column-rule, paginated across pages
  • Gradients (linear / radial / conic) on boxes and as the page background (body/html), border-radius, box-shadow, filter: blur, transforms (rotate/scale/translate, incl. calc() and % arguments), opacity & alpha compositing
  • Tables (colspan, rowspan, border-collapse, border-spacing, empty-cells, caption-side, repeating headers), lists and nested markers (list-style-position, list-style-image)
  • background-size / background-repeat, flex-flow, place-content, and logical properties (margin-inline-*, padding-inline-*, border-inline-*) that follow direction
  • Pair kerning from the font's OpenType GPOS table, plus letter-spacing / word-spacing, for embedded fonts

Documents & paging

  • Automatic pagination, page-break-* / break-before / break-after (incl. avoid), break-inside: avoid / avoid-page
  • @page size/margins, including named pages (@page wide { size: A4 landscape } + page: wide) — mixed page sizes in one PDF
  • Wide content is shrunk to fit the page by default (PageScaling.ShrinkToFit); set PageScaling.None to lay out strictly at the page width
  • Running headers/footers (HeaderHtml/FooterHtml with {{page}} / {{pages}})
  • Bookmarks/outlines from headings, internal links, link annotations
  • Interactive AcroForm fields and embedded file attachments

Graphics & fonts

  • SVG: paths, shapes, gradients, clipPath, <use>/<symbol>, transforms, and <text> (shaped, bidi-aware, with font fallback)
  • Images: PNG (incl. alpha/soft-mask + 16-bit), JPEG (incl. CMYK + Exif orientation), GIF, BMP, WebP (lossless), data: URIs
  • Base-14 fonts (no files needed) + TrueType/OpenType embedding (Identity-H), WOFF/WOFF2, subsetting, @font-face
  • Right-to-left & complex scripts — Arabic (OpenType cursive joining + GPOS harakat) and Hebrew with bidirectional (mixed LTR/RTL) text, plus Devanagari and Thai — bundled Noto fonts, no setup

Output targets

  • Encryption: passwords with RC4-128 or AES-256
  • PDF/A-2b and PDF/A-3b (archival — all fonts embedded automatically), PDF/UA-1 + Tagged PDF (accessibility), XMP metadata
  • Hybrid e-invoices — PDF/A-3 with embedded associated files (/AFRelationship), EU Factur-X / ZUGFeRD, and a built-in QR-code generator (see E-invoicing)
  • Barcodes — 2D QR Code, PDF417, Aztec, and Data Matrix, and linear Code 128, GS1-128, EAN-13, EAN-8, and UPC-A, any number per document, placed by point coordinates or an HTML element anchor, plus an IATA BCBP boarding-pass builder (see Barcodes & boarding passes)
  • Configurable diagonal watermark

E-invoicing — Factur-X / ZUGFeRD, ZATCA hybrid, and QR

Produce the hybrid invoice PDF that modern e-invoicing flows expect: a human-readable PDF/A-3 with the structured XML embedded inside it, plus a scannable QR — all in pure .NET, no extra packages.

PDF/A-3 with an embedded associated file

using Bhowra.Ink;

byte[] xml = File.ReadAllBytes("invoice.xml");

var props = new ConverterProperties { PdfA = PdfAConformance.PdfA3B }
    .AttachFile("invoice.xml", xml, "application/xml", afRelationship: "Alternative");

byte[] pdf = HtmlConverter.ConvertToBytes(invoiceHtml, props);

EU Factur-X / ZUGFeRD — SetFacturX embeds the Cross-Industry Invoice XML under the mandated filename (factur-x.xml, or xrechnung.xml for XRECHNUNG), writes the fx: XMP schema with its required PDF/A extension declaration, and applies a profile-correct /AFRelationship:

byte[] cii = File.ReadAllBytes("factur-x.xml");

var props = new ConverterProperties { PdfA = PdfAConformance.PdfA3B }
    .SetFacturX(cii, FacturXProfile.En16931);

byte[] pdf = HtmlConverter.ConvertToBytes(invoiceHtml, props);

Profiles: Minimum, BasicWL, Basic, En16931, Extended, XRechnung. SetFacturX requires PdfA = PdfAConformance.PdfA3B and fails before fetching external CSS or images when another PDF/A mode is selected.

This fits enterprise EDI, ERP, and SAP S/4HANA flows that need a PDF/A-3 hybrid invoice carrier: Bhowra.Ink builds the document layer (rendered invoice PDF + embedded CII XML + Factur-X/XRechnung metadata). It is not an EDI network, validation service, or SAP connector; generate and validate the business XML in your platform, then pass it to SetFacturX.

Built-in QR — a zero-dependency ISO/IEC 18004 generator (byte mode, versions 1–40, error-correction L/M/Q/H), drawn as a crisp vector with the mandatory quiet zone — sized for payloads up to the ~700-character range (e.g. a Saudi TLV string).

For Saudi invoice QR data, ZatcaTlv builds the TLV payload: typed helpers for tags 1-5, and raw caller-supplied bytes for Phase 2 tags 6-9 (invoice hash, signature, public key, stamp signature). It does not generate, sign, stamp, validate, or submit the invoice XML.

using Bhowra.Ink;
using Bhowra.Ink.QrCode;

byte[] tlvBytes = ZatcaTlv.BuildPhase1Bytes(
    seller: "Najd Trading Company",
    vatNo: "311111111100003",
    ts: DateTimeOffset.Parse("2026-06-28T10:15:00Z"),
    total: "2472.50",
    vat: "322.50");

var props = new ConverterProperties
{
    QrCode =
    {
        Data             = tlvBytes,       // any bytes; null/empty = no QR (output unchanged)
        X                = 60f,            // points from the page's left edge
        Y                = 70f,            // points from the page's bottom edge
        Size             = 120f,           // points (includes the quiet zone)
        Page             = 1,              // 1-based; 0 = every page
        EcLevel          = QrEcLevel.M,    // L / M / Q / H
        QuietZoneModules = 4,              // ISO minimum
    },
};

Barcodes & boarding passes

Beyond the single invoice QR above, ConverterProperties.Barcodes draws any number of barcodes on a document, mixing symbologies freely — 2D QR Code (ISO/IEC 18004), PDF417 (ISO/IEC 15438), Aztec (ISO/IEC 24778), and Data Matrix ECC 200 (ISO/IEC 16022), and linear Code 128 (ISO/IEC 15417), GS1-128 (Code 128 with FNC1, for SSCC / GTIN logistics labels), EAN-13, EAN-8, and UPC-A (ISO/IEC 15420). Each is a crisp vector Form XObject — no raster, no fonts — so it stays sharp at any scale and is PDF/A-safe. All encoders are pure .NET, zero third-party packages.

You supply the payload bytes; the engine renders them. Place each barcode either by explicit point coordinates or by pointing AnchorId at an HTML element, letting the document's layout own placement:

using Bhowra.Ink;
using Bhowra.Ink.Barcodes;
using Bhowra.Ink.QrCode;

var props = new ConverterProperties();

// Placed by absolute point coordinates.
props.Barcodes.Add(new BarcodeOptions
{
    Symbology = BarcodeSymbology.Pdf417,
    Data      = System.Text.Encoding.ASCII.GetBytes(bcbpString),
    X = 40f, Y = 40f, Width = 220f, Height = 70f,   // points; Height 0 = natural aspect
    Page = 1,                                       // 1-based; 0 = every page
    Pdf417ErrorLevel = 5,                           // 0-8 (PDF417 only)
});

// Placed by an HTML element — the laid-out box of #boarding-code supplies X/Y/Width/Height.
props.Barcodes.Add(new BarcodeOptions
{
    Symbology = BarcodeSymbology.Aztec,
    Data      = System.Text.Encoding.ASCII.GetBytes(bcbpString),
    AnchorId  = "boarding-code",                    // QR/Aztec/DataMatrix anchors must be square
    AztecErrorPercent = 23,                         // ISO recommendation (Aztec only)
});

// Linear: a retail EAN-13 (12 digits = check digit computed; 13 = check digit verified).
// Linear anchors may be any aspect; the box's width and height set the symbol size.
props.Barcodes.Add(new BarcodeOptions
{
    Symbology = BarcodeSymbology.Ean13,
    Data      = System.Text.Encoding.ASCII.GetBytes("978020137962"),
    AnchorId  = "product-code",
});

Per-symbology knobs (QrEcLevel, Pdf417ErrorLevel, Pdf417Columns, AztecErrorPercent) are ignored by the others, and QuietZoneModules defaults to each spec's minimum (QR 4, PDF417 6, Data Matrix 1, Aztec 0, Code 128 / GS1-128 10, EAN-13 11, UPC-A 9, EAN-8 7). An entry with no Data is skipped, so the output is unchanged. Linear symbols draw bars only: put the human-readable digits in the HTML beneath the anchored element, so they use your document's font and styling.

IATA boarding passes (BCBP) — BcbpPass builds the IATA Resolution 792 payload string for a mobile or paper boarding pass, including multi-leg itineraries, ready to hand to any symbology above (Aztec and QR for mobile, PDF417 for paper):

using Bhowra.Ink.Barcodes;

var pass = new BcbpPass { PassengerName = "DESMARAIS/LUC" };
pass.Legs.Add(new BcbpLeg
{
    Pnr = "ABC123", From = "YUL", To = "FRA", Carrier = "AC", FlightNumber = "0834",
    FlightDate = new DateOnly(2026, 6, 28), Cabin = 'J', Seat = "003A", Sequence = "0012",
});

byte[] payload = pass.BuildBytes();     // ASCII BCBP bytes → BarcodeOptions.Data

The library builds the document and barcode layer; it does not issue, validate, or check-in the pass against a departure-control system — supply the itinerary data your platform already holds.


Configuration

ConverterProperties is the single config object (every member is documented for IntelliSense):

var props = new ConverterProperties
{
    Title  = "Invoice 2026-001",
    Author = "Acme Corp",

    WatermarkText    = "CONFIDENTIAL",   // diagonal; always honoured, licensed or not
    WatermarkOpacity = 0.15f,
    WatermarkColor   = "#BFBFBF",

    UserPassword = "secret",             // encrypts the PDF
    Aes256       = true,                 // AES-256 instead of the default RC4-128

    PdfA   = PdfAConformance.PdfA2B,     // archival conformance (fonts auto-embedded)
    Tagged = true,                       // accessible structure (PDF/UA)

    MissingGlyphBehavior = MissingGlyphBehavior.Diagnostic, // never silently drop glyphs
}
.SetPageA4()        // or SetPageLetter() / SetPageLegal() / SetLandscape()
.SetMargins(36f);   // points (1 pt = 1/72")

byte[] pdf = HtmlConverter.ConvertToBytes(html, props);

Page size/margins can also be set explicitly via PageWidth/PageHeight and MarginTop/Right/Bottom/Left (all in points). Defaults: A4 portrait, 36 pt margins.

Other commonly used options:

Option Default Purpose
PageScaling ShrinkToFit scale a too-wide layout down to the page; None lays out strictly at page width
HeaderHtml / FooterHtml — running header/footer HTML; {{page}} / {{pages}} are substituted
OwnerPassword user password full-permission password when UserPassword encrypts
WatermarkOnTop / WatermarkFontSize false / auto paint the watermark over content; force its size in points
Subject / Keywords / CreatedOn — / now document info metadata
PdfUA false PDF/UA-1: forces Tagged and writes the PDF/UA identification
Language en-US catalog /Lang for tagged output
XmpMetadata false write an XMP packet (implied by PdfA / PdfUA)
ValidateContent false check every visible HTML word reached the PDF; result in ConversionResult.ContentValidation
EnableCompression true Flate-compress content streams (disable only for debugging)

The engine resolves external CSS — <link rel="stylesheet"> and the @imports those sheets (and inline <style> blocks) contain — and cascades it with embedded styles in correct document order. It is secure-by-default: local stylesheets under BaseUri resolve out of the box; remote (http/https) fetching is opt-in.

// Local stylesheets (the common case) — resolved relative to BaseUri, no extra setup:
var props = new ConverterProperties { BaseUri = templateDir };   // <link href="css/site.css"> just works

// Opt in to remote stylesheets (CDN), keeping every guard on:
props.ExternalCss.EnableRemote();          // or .Mode = ExternalCssMode.LocalAndRemote

props.ExternalCss (an ExternalCssOptions) carries the policy and limits:

Setting Default Purpose
Mode LocalOnly Disabled / LocalOnly / LocalAndRemote
AllowHttp false allow plain http:// (otherwise HTTPS-only)
AllowPrivateNetwork false the SSRF guard — blocks private/loopback/link-local IPs and the cloud metadata endpoint
MaxBytesPerFile / MaxBytesTotal 2 MiB / 8 MiB size caps (DoS guard)
MaxStylesheets / MaxImportDepth 30 / 5 count + @import recursion caps
TimeoutSeconds / MaxRedirects 10 / 5 per-fetch timeout; each redirect is SSRF-re-validated
FailOnError false fail-open (skip a bad sheet + record a diagnostic) vs. fail-closed
StrictContentType false accept only text/css / text/plain responses (default still rejects text/html and known binaries)
AllowedHosts / CssFetcher — optional host allow-list; optional custom fetcher (e.g. Managed Identity)

Behaviour matches how a strict print renderer treats CSS: the print media is the target, so media="screen" links are ignored and media="print"/all apply; @import cycles are detected (ancestor-chain) and depth-bounded; charset follows BOM → HTTP charset → @charset → UTF-8. Skipped or blocked stylesheets are reported in ConversionResult.ImageDiagnostics.


Images: remote & local sources (secure-by-default)

Out of the box an <img> is embedded only from a data: URI. Both remote and local file sources are opt-in, so converting HTML you don't fully trust makes no outbound network calls and reads no local files — no SSRF and no local-file disclosure by default.

<img src> Default Enable
data: URI embedded —
Remote http(s):// not fetched props.AllowRemoteImages = true (or supply an ImageFetcher). Fetches then pass the SSRF guard — private/loopback/link-local IPs and the cloud metadata endpoint blocked, redirect-to-internal and DNS-rebind defeated, 64 MiB response cap.
Local file path not read props.AllowLocalFiles = true — only for trusted HTML. Off by default so an untrusted <img src="/etc/passwd"> can't embed an arbitrary local file.
// Trusted template with local logo assets + CDN images:
var props = new ConverterProperties { AllowLocalFiles = true, AllowRemoteImages = true };

Skipped or blocked images are reported in ConversionResult.ImageDiagnostics.


Image → PDF

Turn one or more images into a PDF — one image per page — with ImageToPdf. It routes through the same renderer, so it inherits the full image stack: JPEG embedded losslessly (DCTDecode passthrough), PNG/WebP/BMP/GIF decoding, RGB/Gray/CMYK color spaces, EXIF auto-orientation, alpha (/SMask), and image de-duplication.

// One image, fit to A4 (default), preserving aspect ratio:
byte[] pdf = ImageToPdf.ToBytes("scan.jpg");

// Several images → multi-page, page sized to each image (no margins):
ImageToPdf.ToFile(
    new[] { ImageInput.FromFile("p1.png"), ImageInput.FromBytes(jpegBytes) },
    "album.pdf",
    new ImageToPdfOptions { PageSize = ImagePageSize.FitToImage });

// A4 with margins, centered, cropped to fill, at print resolution:
var opts = new ImageToPdfOptions
{
    PageSize  = ImagePageSize.A4,     // or Letter / Legal / Custom / FitToImage
    Fit       = ImageFit.Cover,       // Contain (default) / Cover / Stretch / None
    MarginPt  = 36,
    Dpi       = 300,                  // px→pt for FitToImage (300 = print scans)
    Background = "#ffffff",
};
byte[] album = ImageToPdf.ToBytes(new[] { ImageInput.FromFile("photo.jpg") }, opts);

Inputs are files, byte[], or Stream (ImageInput.FromFile/FromBytes/FromStream). ImagePageSize.FitToImage sizes the page to the image; with multiple differently-sized images the page is sized to the largest and the rest are centered (one PDF uses a single page size).


Fonts & non-Latin text

props.RegisterFont("Inter", File.ReadAllBytes("Inter.ttf"));   // embed a custom family

Bundled fonts ship inside the DLL — Noto Sans Arabic, Hebrew, Thai, Devanagari, Symbols and Symbols 2, plus Roboto (Latin) — so common non-Latin scripts and symbols (✓ ✗ ★ → ✈) render the same on every host with no extra setup. Anything a registered or bundled font can't draw is reported — never silently dropped:

var result = HtmlConverter.Convert(html, props);
foreach (var d in result.FontDiagnostics)
    Console.WriteLine(d);   // e.g. characters with no available glyph + how to fix

Licensing & trial

Bhowra.Ink is commercial software that runs fully without a key — it never expires, never imposes a page limit, and never throws based on license state. Unlicensed output simply carries a horizontal "TRIAL VERSION" mark (plus a clickable licensing banner). Activating a valid license removes that trial mark; any caller-configured watermark is independent and unaffected. A free, time-limited evaluation license for clean output is available on request.

Activating a license — pick one:

// 1. In code, once at startup (online activation; the signed token is cached and re-verified offline):
HtmlConverter.Initialize("YOUR-LICENSE-KEY");          // or await HtmlConverter.InitializeAsync(...)
  1. App setting / environment variable BHOWRA_LICENSE_KEY — activates automatically on first conversion, no code change (the documented Azure App Service / Functions path).
  2. Offline token — for hosts that must make no outbound calls: set the two environment variables issued with the token, BHOWRA_INSTALL_ID and BHOWRA_LICENSE_JWT. Verified locally, no network.

Licenses are bound to an installation ID. Read it with HtmlConverter.GetInstallId() (also on every ConversionResult.InstallId) and send it with your order; on Azure it is derived from the site identity, so all scaled-out instances of one app share it. Only the activation call itself can throw (LicenseActivationException); conversion never throws on license state.

See LICENSE.txt for the full agreement. Licensing & sales: sales@bhowra.com.


Architecture (in brief)

HtmlConverter → HtmlParser (DOM) + CssParser/CssCascade (computed styles) → LayoutTreeBuilder (typed layout tree) → PdfWriter (paginate, render, emit PDF bytes). Everything — the HTML parser, CSS engine, font/image decoders, and the PDF writer — is implemented from scratch on the .NET 8 base class library, with no third-party packages.


Known limitations

Area Status
PDF/A (2b / 3b) + watermark / form fields / password Not combinable — a caller watermark and form fields draw with non-embeddable standard-14 fonts, and PDF/A forbids encryption, so PDF/A throws a clear error. Plain text, registered fonts and the trial mark embed automatically, so unlicensed builds produce PDF/A too.
Persian/Urdu extended Arabic letters Beyond Modern Standard Arabic — not yet cursively joined
CJK scripts Require a font via RegisterFont(...) or @font-face (not bundled)
JavaScript / SPAs / live web pages Out of scope — renders HTML/CSS, does not execute scripts
Remote @font-face URLs Not fetched (local files and data: URIs supported)
TIFF / JPEG-2000 images No decoder — surfaced via result.ImageDiagnostics (never silently dropped, no black box)
Lossy / animated WebP Only lossless (VP8L) WebP is decoded
border-collapse shared-border dedup Borders drawn per cell (no dedup yet)
Cross-host font fallback Text in base-14 and the bundled Noto/Roboto fonts (Latin, Arabic, Hebrew, Thai, Devanagari) renders identically everywhere; glyphs outside that set fall back to host system fonts, so their metrics can differ across environments (e.g. Windows Arial vs Linux DejaVu). Bundle or RegisterFont(...) a font for byte-identical output on every host.
Gradient page background position A body/html gradient paints over every page, positioned against the page box rather than the root element's box (identical for a one-page 100vh body)
backdrop-filter Not applied — the blur of content behind a translucent box is a no-op (the box's own background, borders and shadows still render)

License

Commercial license — see LICENSE.txt. The bundled Noto and Roboto fonts are licensed under the SIL Open Font License 1.1 (see Fonts/Bundled/OFL.txt in the package) and remain governed by the OFL.

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 was computed.  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 was computed.  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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net8.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.11 92 10/17/2026
1.0.10 97 10/14/2026
1.0.9 553 8/20/2026
1.0.8 198 7/19/2026
1.0.7 140 7/11/2026
1.0.6 152 7/7/2026
1.0.5 140 7/5/2026
1.0.4 206 7/4/2026
1.0.3 153 7/3/2026
1.0.2 151 6/28/2026
1.0.1 141 6/27/2026
1.0.0 176 6/25/2026

1.0.11 - CSS 3D transforms, multi-column and footnote layout, typography and layout-correctness fixes.

NEW: CSS 3D transforms (perspective, rotateX/Y/Z, translateZ, matrix3d, transform-style: preserve-3d, backface-visibility) as vector output. Footnotes (float: footnote) reserve a footnote area, so body text no longer runs under the notes. Inline sub/sup and vertical-align (sub, super, lengths, percentages, middle, text-top, text-bottom) on text. font-weight matching by number (CSS Fonts 4), synthetic oblique, Times and Courier italics, bolder/lighter against the parent. text-align-last, line-clamp, text-orientation, inline-size/block-size and their min/max forms.

FIXES: Multi-column: decorated paragraphs and block boxes split across columns with box-decoration-break slice/clone, bare text and inline content flow across columns, column-gap: normal is 1em. Parent/child margin collapsing, auto table column distribution, float shrink-to-fit insets and max-height on auto-height blocks follow CSS 2.1. Tall lines in a paragraph that crosses a page no longer run past the page bottom. Bidi brackets are paired for the whole paragraph. Link annotations are no longer recorded twice for table cells.

BEHAVIOUR CHANGES vs 1.0.10 - re-render and compare existing documents before upgrading: margins now collapse through a parent and its first/last child; sub/sup text is raised and smaller; column-gap: normal is 1em instead of 11.25pt; footnote areas shrink the page's body area; font-weight 500 is no longer bold.

1.0.10 - CSS correctness and linear barcodes.

NEW: Linear barcodes on ConverterProperties.Barcodes - Code 128, GS1-128, EAN-13, EAN-8 and UPC-A, alongside QR Code, PDF417, Aztec and Data Matrix (bars only; put the human-readable digits in your HTML). CSS that was previously parsed and silently ignored now renders: border-collapse, border-spacing, empty-cells, caption-side, list-style-position and flex-flow, plus background-size, background-repeat, place-content, list-style-image, and logical inline properties (margin/padding/border-inline-*) that follow direction. CSS-wide keywords inherit / initial / unset. GPOS pair kerning and letter-/word-spacing for embedded fonts. SVG text elements are drawn through the HTML text path (shaping, bidi, font fallback, rotation). A gradient set on body/html now paints as the page background. display:inline-block works on block elements (div, section, p), so they sit side by side. Transforms resolve calc(), percentages and font-relative lengths. LayoutDiagnostics now reports an inconsistent table column structure.

FIXES: List markers are sized from the widest marker the list draws, so item 10 and beyond no longer overlap their text, and a marker is never stranded at the foot of a page without its item. Floats that do not fit move to the next page; fixed-height boxes end on the right page; a forced break after empty content is honoured; flex lines paginate. Opacity applies to an element as one transparency group. Grid template-less and fit-content track sizing.

BEHAVIOUR CHANGES vs 1.0.9 - re-render and compare existing documents before upgrading: (1) Tables default to border-collapse:separate with 2px border-spacing (the CSS 2.1 initial value; 1.0.9 defaulted to collapse) - declare border-collapse:collapse to keep the previous look. (2) Line baselines use the font's ascent/descent with half-leading (CSS 2.1 10.8.1), so text sits about 0.13em lower. (3) Automatic shrink-to-fit never scales a page below 1/1.5 (about 67%). (4) img is an atomic inline-level box. (5) body margin/padding/border form a real box that offsets the content, and the html background paints the page canvas; the default body margin stays 0. (6) display:inline-block on block elements now lays out inline.

Also shipped in 1.0.9 but not listed in its notes: multi-column pagination with column-width, column-fill and column-rule; named @page sizes and margins; ::marker styling; break-inside:avoid-page and break-after:avoid; box-shadow fixes for #RGBA colours, mm/cm/in lengths and rgba() alpha; a width:100% table no longer collapses after a fixed-width sibling.

Previous 1.0.x notes remain in git history.