NkkinPdf 2.0.0
dotnet add package NkkinPdf --version 2.0.0
NuGet\Install-Package NkkinPdf -Version 2.0.0
<PackageReference Include="NkkinPdf" Version="2.0.0" />
<PackageVersion Include="NkkinPdf" Version="2.0.0" />
<PackageReference Include="NkkinPdf" />
paket add NkkinPdf --version 2.0.0
#r "nuget: NkkinPdf, 2.0.0"
#:package NkkinPdf@2.0.0
#addin nuget:?package=NkkinPdf&version=2.0.0
#tool nuget:?package=NkkinPdf&version=2.0.0
NkkinPdf
Tiny, fast, dependency-free PDF writer for .NET 10 and .NET MAUI 10 (Android, iOS, Windows).
Version 2.0 targets
net10.0only. 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:
PdfFontSetdraws Arabic with your Arabic font and Latin with your Latin font. - Layout: alignment (Left/Center/Right/Start/End/Justify), word wrap,
maxLineswith 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.
Pages, links and bookmarks
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,glyfoutlines) fonts are supported. CFF-based.otffonts and.ttccollections are not. Corrupt files throwInvalidDataException. PdfFontSet(latin, rtl, params fallbacks): Arabic/Hebrew letters usertl, other letters uselatin; digits, spaces and punctuation follow the surrounding text (setDigitsUseLatinFont = trueto 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 | Versions 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. |
-
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.
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.