Zpdf 0.3.1

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

zpdf

zpdf writes PDFs and reads the text back out of them. It's written in Zig, and one C ABI serves the browser (WebAssembly), C and .NET.

What it does

Writing:

  • Real fonts. TrueType and OpenType, shaped with kb_text_shape, so Arabic, Hebrew, Devanagari and other complex scripts are shaped too. Fonts get embedded as subsets, and the text stays copyable and searchable.
  • The 14 standard PDF fonts, when you don't want to embed anything.
  • Lines, rectangles, and text you fill in later, like "Page 3 of 12".
  • Pages go out as they end. Memory follows one page, not the whole document.

Reading:

  • Page count, page boxes and rotation, and the text each page draws as positioned runs, one page at a time. Encrypted files are refused.

It's early (0.x). The C ABI has a version number, and it changes whenever the ABI does.

.NET

dotnet add package Zpdf

.NET 10 on linux-x64, linux-arm64, osx-arm64 and win-x64. The native library for each one ships in the package.

Write a PDF into any Stream, like a file or an HTTP response body. zpdf writes each page straight into the pipe's memory, so there's no extra copy, and the flush after each page is where the I/O waits:

var pipe = PipeWriter.Create(output);
using (var pdf = PdfWriter.Create(pipe, compress: true))
{
    var helvetica = pdf.AddStandardFont(StandardFont.Helvetica);
    var page = pdf.BeginPage(612, 792);
    pdf.DrawText(page, helvetica, 24, 72, 720, PdfColor.Black, "Hello from zpdf", TextDirection.FromText);
    pdf.EndPage(page);
    await PdfWriter.FlushAsync(pipe, CancellationToken.None);
    pdf.Finish();
    await PdfWriter.FlushAsync(pipe, CancellationToken.None);
}
await pipe.CompleteAsync();

Read it back:

using var reader = PdfReader.Open(bytes);
for (var i = 0; i < reader.PageCount; i++)
{
    var box = reader.GetPage(i).MediaBox;
    Console.WriteLine($"page {i + 1}: {box.Width} x {box.Height} pt");
    foreach (var run in reader.ReadText(i).Runs)
    {
        Console.WriteLine($"  at ({run.X}, {run.Y}), {run.Font} {run.Size} pt: {run.Text}");
    }
}

Measure and wrap text with the same widths the writer draws. Wrap breaks at spaces and around CJK, keeps line breaks, and with breakWords splits a word that is too long for the line. Lines come back as offsets into your string, in a list you can reuse:

using var helvetica = FontMetrics.Open(StandardFont.Helvetica);
var cell = "Cost of goods on hand, by location";
var lines = new List<TextLine>();
helvetica.Wrap(cell, 10, 100, breakWords: true, TextDirection.FromText, lines);
foreach (var line in lines)
{
    Console.WriteLine($"{cell.Substring(line.Start, line.Length)} ({line.Width:0.##} pt)");
}

Units are points (1/72 inch), and y goes up from the bottom of the page. PdfWriter takes any IBufferWriter<byte>, and AddFont embeds a font file. The source in dotnet/Zpdf documents both.

Zig

zig fetch --save git+https://github.com/dimenus/zpdf

That pins the current commit in your build.zig.zon. Then import the module in build.zig:

const zpdf = b.dependency("zpdf", .{ .target = target, .optimize = optimize });
const exe = b.addExecutable(.{ .name = "hello", .root_module = b.createModule(.{
    .root_source_file = b.path("src/main.zig"),
    .target = target,
    .optimize = optimize,
    .imports = &.{.{ .name = "zpdf", .module = zpdf.module("zpdf") }},
}) });

And write a PDF with it:

var out: std.Io.Writer.Allocating = .init(gpa);
defer out.deinit();

var doc = try zpdf.Document.begin(gpa, &out.writer, .{ .compress = true });
defer doc.deinit();
const helvetica = try doc.addStandardFont(.helvetica);
const page = try doc.beginPage(612, 792);
try doc.drawText(page, helvetica, 24, 72, 720, .black, "Hello from zpdf", .{});
try doc.endPage(page);
try doc.finish();

Document writes into any std.Io.Writer, so a file or a socket works as well as memory. You get just the library: zpdf's tests and tools stay out of your build.

C

tools/zig build abi

You get the shared library (zig-out/lib, or zig-out/bin on Windows) and zpdf.h in zig-out/include. The comments in the header are the protocol: contexts, handles, buffers and error codes.

Browser

tools/zig build wasm

That builds zig-out/bin/zpdf.wasm. web/zpdf.js is a thin wrapper around it.

Building

Use the Zig version in build.zig.zon. tools/zig picks it for you through mise; if that version is already on your PATH, plain zig is fine.

tools/zig build test          # unit tests, golden PDFs, the C ABI, the wasm build
tools/zig build dotnet-test   # the .NET library (needs the .NET 10 SDK)

AGENTS.md has the rest: valgrind, benchmarks, browser tests and mutation testing.

License

zlib, see LICENSE. kb_text_shape is zlib too. The library also holds Adobe's standard-font metrics, glyph names and CJK character maps; their notices are in THIRD-PARTY-NOTICES.md, which ships in every package.

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
0.3.1 517 10/17/2026
0.3.0 97 10/17/2026
0.2.0 87 10/17/2026
0.1.1 156 10/17/2026
0.1.0 88 10/17/2026