Zpdf 0.3.1
dotnet add package Zpdf --version 0.3.1
NuGet\Install-Package Zpdf -Version 0.3.1
<PackageReference Include="Zpdf" Version="0.3.1" />
<PackageVersion Include="Zpdf" Version="0.3.1" />
<PackageReference Include="Zpdf" />
paket add Zpdf --version 0.3.1
#r "nuget: Zpdf, 0.3.1"
#:package Zpdf@0.3.1
#addin nuget:?package=Zpdf&version=0.3.1
#tool nuget:?package=Zpdf&version=0.3.1
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 | 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.