NkkinPdf 2.0.0

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

NkkinPdf

Tiny, fast, dependency-free PDF writer for .NET 10 and .NET MAUI 10 (Android, iOS, Windows).

Version 2.0 targets net10.0 only. Projects still on .NET 8 can stay on NkkinPdf 1.0.1.

  • Unicode text with subset-embedded TrueType fonts: only the glyphs you use are embedded, and text stays copyable and searchable. Pair kerning from the font.
  • Arabic and RTL: letter joining, lam-alef ligatures, the Unicode bidi algorithm for mixed Arabic/Latin/number lines (paired brackets, LRM/RLM), mirrored brackets, kashida justification.
  • Two fonts in one line: PdfFontSet draws Arabic with your Arabic font and Latin with your Latin font.
  • Layout: alignment (Left/Center/Right/Start/End/Justify), word wrap, maxLines with ellipsis, paragraphs that flow onto new pages, tables with automatic page breaks, underline and strikethrough.
  • Graphics: lines (solid or dashed), rectangles (rounded), circles, ellipses, polygons, any path, clipping, opacity.
  • Images: PNG (with transparency) and JPEG; camera photos are rotated by their EXIF orientation. Opaque PNGs are embedded without decoding.
  • Document: links (web and to other pages), bookmarks, page numbers/headers/footers, metadata, language, right-to-left reading order.
  • Safe for Android release builds: no reflection, trimming- and AOT-compatible, culture-safe number output.

Quick start

using NkkinPdf;

var latin  = TrueTypeFont.Load(await FileSystem.OpenAppPackageFileAsync("Fonts/OpenSans-Regular.ttf"));
var bold   = TrueTypeFont.Load(await FileSystem.OpenAppPackageFileAsync("Fonts/OpenSans-Semibold.ttf"));
var arabic = TrueTypeFont.Load(await FileSystem.OpenAppPackageFileAsync("Fonts/Cairo-Regular.ttf"));
var text   = new PdfFontSet(latin, rtl: arabic);
var title  = new PdfFontSet(bold, rtl: arabic);

var doc  = new PdfDocument { Title = "Earnings report", Author = "Nkkinsoft", Language = "en" };
var page = doc.AddPage(PdfPageSize.A4);                 // coordinates: points, origin top-left

page.DrawText("Earnings report — تقرير الأرباح", title, 18, 36, 36, PdfColor.FromHex("#2563EB"));
page.DrawParagraph("Long text wraps inside the width. النص العربي يلتف تلقائياً أيضاً.", text, 11, 36, 70, page.Width - 72);
page.DrawImage(logoPngBytes, page.Width - 100, 30, 64, 64);   // PNG or JPEG, aspect ratio kept
page.AddLink(36, 120, 120, 14, "https://example.com");

doc.Save(path);   // or doc.ToArray() / await doc.SaveAsync(stream)

Load each font once and reuse it (fonts can be shared across threads; a PdfDocument is built on one thread). A plain TrueTypeFont can be passed anywhere a PdfFontSet is expected.

Text

page.DrawText("Total", bold, 12, x, y);                                        // one line, y = top of the line
page.DrawText("4.37 $", text, 10, x, y, width: 80, PdfTextAlign.End);          // aligned in a box
page.DrawText("Sale", text, 10, x, y, decoration: PdfTextDecoration.Strikethrough);

var height = page.DrawParagraph(body, text, 11, x, y, width, PdfTextAlign.Justify);   // returns the height used
page.DrawParagraph(summary, text, 10, x, y, width, maxLines: 3);                       // cut with "…"

// Long text that continues on new pages; `page` becomes the last page used.
y = doc.DrawParagraph(ref page, article, text, 11, 36, y, page.Width - 72, PdfTextAlign.Justify);

TextLayout.Wrap and TextLayout.Ellipsize give you the lines without drawing; PdfFontSet.MeasureWidth measures. Set Kerning = false on a PdfFontSet to turn kerning off.

Tables

var table = new PdfTable(text, 10) { HeaderFont = title, ShowVerticalLines = true, ShowOuterBorder = true };
table.AddColumn("Date", 80)
     .AddColumn("App")                                  // width 0 = share the remaining width
     .AddColumn("Earnings", 70, PdfTextAlign.End);
foreach (var r in rows) table.AddRow(r.Date, r.App, r.Earnings.ToString("F2"));
table.AddRow(new PdfCell("Best day", background: PdfColor.FromHex("#DCFCE7")), "Hi Koora", new PdfCell("9.99", textColor: PdfColor.Green));
table.AddFooterRow("Total", "", total.ToString("F2"));

var y = table.Draw(doc, ref page, x: 36, y: 140, width: page.Width - 72);   // adds pages as needed

Cells wrap, the header repeats on each new page, rows are zebra-striped, rows taller than a page are split, and RightToLeft = true lays the columns out from the right for Arabic tables. VerticalAlign positions short cells.

Graphics and images

page.DrawLine(36, 200, 560, 200, PdfColor.Gray, 1, dash: [6, 3]);
page.DrawRectangle(36, 220, 200, 80, fill: PdfColor.LightGray, stroke: PdfColor.Gray, cornerRadius: 8);
page.DrawCircle(300, 260, 40, fill: PdfColor.Blue.WithOpacity(0.5));
page.DrawPolygon([(400, 220), (460, 300), (340, 300)], fill: PdfColor.FromHex("#22C55E80"));   // #RRGGBBAA
page.DrawPath(new PdfPath().MoveTo(36, 320).CurveTo(100, 280, 200, 360, 260, 320), stroke: PdfColor.Red);

page.PushClip(new PdfPath().AddEllipse(36, 340, 120, 120));   // round avatar
page.DrawImage(photoJpeg, 36, 340, 120, 120, keepAspectRatio: false);
page.PopClip();

page.DrawImage(watermarkPng, 0, 0, page.Width, page.Height, opacity: 0.1);

Images: PNG (gray, RGB, palette, alpha, 1–16 bit, interlaced) and JPEG (gray, RGB, CMYK, progressive). The same bytes are embedded once per document. PNGs that need decoding (transparency or interlacing) are limited to 40 megapixels. Scale large camera photos down before adding them to keep files small.

var chapter = doc.AddBookmark("Results", page);          // sidebar outline
chapter.AddChild("October", page, y: 300);
page.AddLink(36, 80, 200, 14, otherPage, targetY: 100);   // jump inside the document

// Headers, footers and page numbers: call once after all pages are added.
doc.ForEachPage((p, number, count) =>
    p.DrawText($"{number} / {count}", text, 8, 0, p.Height - 24, p.Width, PdfTextAlign.Center, PdfColor.Gray));

Document options: Title, Author, Subject, Keywords, Creator, Language (e.g. "ar-MA"), RightToLeft (reading order), CreationDate (set it to get byte-identical output), Compress, StripFontHinting (default on; smaller files).

Fonts

  • TrueType (.ttf, glyf outlines) fonts are supported. CFF-based .otf fonts and .ttc collections are not. Corrupt files throw InvalidDataException.
  • PdfFontSet(latin, rtl, params fallbacks): Arabic/Hebrew letters use rtl, other letters use latin; digits, spaces and punctuation follow the surrounding text (set DigitsUseLatinFont = true to force 0-9 to the Latin font); characters neither font has go to the first fallback that has them.
  • The same font file loaded twice is embedded once per document.

Right-to-left text

Direction is detected from the first strong letter, or set it with PdfTextDirection.RightToLeft. PdfTextAlign.Start means right for RTL text and left for LTR text. Mixed lines are ordered with the Unicode Bidirectional Algorithm (implicit levels, paired brackets, LRM/RLM/ALM marks), so they match browsers and Word. For example الأرباح 4.37$ shows the $ to the left of the number, exactly as other apps do; put an LRM (‎) right before the number ("الأرباح ‎4.37$") to keep it as 4.37$. Copy and search return the text in reading order (/ActualText). Joining forms come from the font's OpenType GSUB table, or from Unicode presentation forms when the font has them.

Performance

500 pages of text (22,500 lines) in about 0.16 s and 350 KB; 100 pages of Arabic in about 0.13 s; a 2,000-row table in about 25 ms. Measured on a cloud container with .NET 10 (your device will differ).

Limitations

  • Arabic diacritics (harakat) use the font's default positions (no GPOS mark positioning).
  • Images: PNG and JPEG only.
  • Viewers that ignore /ActualText (some poppler-based ones) may copy Arabic in visual order.

Building

build-pack.cmd (Windows) runs the self-test, then packs to artifacts/. Elsewhere:

dotnet run --project tests/SelfTest -c Release -- artifacts
dotnet pack src/NkkinPdf/NkkinPdf.csproj -c Release -o artifacts

The self-test uses the bundled Noto fonts in tests/fixtures/fonts (SIL Open Font License, see OFL.txt).

License

MIT © Nkkinsoft

Product Compatible and additional computed target framework versions.
.NET 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.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
2.0.0 76 11/3/2026
1.0.0 90 11/1/2026

2.0.0 (breaking: .NET 10 only; DrawJpeg removed, use DrawImage)
- Text: full Unicode bidi algorithm (implicit levels, paired brackets, LRM/RLM/ALM), pair kerning (GPOS/kern), invisible format characters and tabs no longer draw boxes, underline/strikethrough, Justify alignment with Arabic kashida, paragraph maxLines with ellipsis, PdfDocument.DrawParagraph flows across pages.
- Graphics: dashed lines, ellipses, circles, polygons, PdfPath, clipping, opacity (PdfColor alpha, image opacity).
- Images: JPEG EXIF orientation, opaque PNGs embedded without decoding, size guard for decoding.
- Tables: per-cell styles (PdfCell), footer rows, vertical and outer borders, vertical alignment, rows taller than a page split across pages.
- Document: bookmarks, internal links, Keywords, Language, RightToLeft, CreationDate (byte-identical output), smaller font subsets.
- Faster: 2-14x less time and up to 75x fewer allocations; corrupt fonts/images throw InvalidDataException.