-
Notifications
You must be signed in to change notification settings - Fork 0
Installation
-
.NET 10 (
net10.0). The library targets .NET 10 and uses nothing beyond the base class library —System.IO.CompressionandSystem.Xml.Linqare the whole of what it builds on. - Fonts. By default the converter discovers the platform's installed fonts (macOS, Windows and Linux font directories). For reproducible output regardless of what a machine has installed, register fonts explicitly instead — see below.
There is no native code, no external process, and no service to stand up. The assembly is self-contained by construction: the project fails its own build if a PackageReference is ever added (Promises).
NuGet.
dotnet add package n8PDFThe package carries its symbols and XML documentation, and its origin is verifiable — see Verifying the package.
From source (today).
git clone https://github.com/nathanpond/n8PDF.git
dotnet build n8PDF/src/n8PDF --configuration ReleaseThen either reference the project directly:
<ProjectReference Include="path/to/n8PDF/src/n8PDF/n8PDF.csproj" />or pack it locally and consume the .nupkg:
dotnet pack n8PDF/src/n8PDF -c ReleaseBuilds are deterministic — the same input builds the same assembly, whoever builds it.
n8PDF reads other people's files, so its releases are traceable to the CI that built them.
Build provenance. Every release attaches a signed SLSA build-provenance attestation that binds the .nupkg and .snupkg to the exact commit and workflow run that produced them. Verify it against the asset on the GitHub release — not the copy from nuget.org: nuget.org re-signs every package it accepts, which changes the bytes, so the attestation matches the release asset the workflow built, not the file you install.
gh attestation verify n8PDF.0.1.3.nupkg --repo nathanpond/n8PDF # the file from the GitHub releaseA pass means the file was built by n8PDF's own release.yml and has not been altered since. The attestation is signed with the workflow's own OIDC identity, so the pipeline cannot be impersonated. It reports the source repository and the tag the release was cut at.
Repository signature. That re-signing is nuget.org counter-signing every package it accepts with its own repository certificate — and n8PDF publishes through NuGet trusted publishing (an OIDC exchange from CI, with no long-lived key), so the publish itself is tied to this repository and this workflow. That signature travels on the copy you install; it shows on the package page and can be inspected locally (the restored package lives under ~/.nuget/packages/n8pdf/<version>/):
dotnet nuget verify ~/.nuget/packages/n8pdf/0.1.3/n8pdf.0.1.3.nupkgAuthor signing — a publisher signature from a code-signing certificate — is not applied: it needs a certificate this project does not hold, and the repository signature together with the build-provenance attestation already establish where a package came from. It is the natural next step should the project take on a certificate.
using n8PDF;
Converter.ConvertFile("report.docx", "report.pdf");Streams and byte arrays work too:
byte[] pdf = Converter.Convert(File.ReadAllBytes("report.docx"));
using var input = File.OpenRead("report.docx");
using var output = File.Create("report.pdf");
Converter.Convert(input, output);Every knob lives on ConversionOptions, passed as the optional last argument. The defaults are chosen so that a plain call does the right thing; see The API for the full surface.
var options = new ConversionOptions
{
Title = "Quarterly Report", // PDF information dictionary
PdfA = true, // claim and honour PDF/A-2b
CreationDate = DateTimeOffset.Parse("2026-01-01T00:00:00Z"), // byte-identical output
};
Converter.ConvertFile("report.docx", "report.pdf", options);Leaving Fonts null discovers the platform's fonts — read once per process and shared, so only the first conversion pays the scan (~600ms for a typical system collection; conversions after that pay nothing, about 1.4ms for a page of text). To make output identical across machines, register exactly the faces you mean:
var fonts = new FontLibrary { UseSystemFonts = false };
fonts.RegisterFile("fonts/TimesNewRoman.ttf");
fonts.RegisterDirectory("fonts/", recursive: true);
Converter.ConvertFile("report.docx", "report.pdf", new ConversionOptions { Fonts = fonts });Registering a file that is not a font throws FontFormatException. Keep and reuse the library — anything registered into it is registered once.
⚠️ n8PDF is early —0.1.3and pre-1.0. The audit register of hostile-input findings is fully closed and fuzz-guarded (Security), but for genuinely untrusted sources, pair the limits below with process isolation as Prerelease Considerations describes.
A .docx from an untrusted source is attacker-controlled input. Set Limits and catch PackageTooLargeException:
var options = new ConversionOptions
{
Limits = new PackageLimits
{
MaximumPartBytes = 32 * 1024 * 1024, // per decompressed part
MaximumTotalBytes = 128 * 1024 * 1024, // across the package
MaximumPartCount = 1024,
MaximumImagePixels = 50_000_000, // the default: a 600dpi A4 scan with room to spare
MaximumFontBytes = 16 * 1024 * 1024, // an embedded face past this is left out
}
};
try
{
Converter.ConvertFile(untrusted, output, options);
}
catch (PackageTooLargeException)
{
// The document asked to decompress past the bounds you set.
}Read Security before putting untrusted documents through any converter, this one included — it states plainly what is defended, how the register was closed, and why isolation is still the right posture.
A document written for a mail merge names a data source only the machine it was written on can reach. Converted as it stands, its fields show what Word shows — «FieldName» in guillemets. Give it a record and the letter fills itself in:
var record = new MailMergeRecord(new Dictionary<string, string>
{
["Title"] = "Dr", ["LastName"] = "Hopper"
});
Converter.ConvertFile("letter.docx", "letter.pdf", new ConversionOptions { MergeRecord = record });Text around a field prints only where the field has something to print, exactly as Word does it.
Using n8PDF
What it does
How it works
Contributing