Skip to content

Installation

npond edited this page Aug 24, 2026 · 6 revisions

Installation

Requirements

  • .NET 10 (net10.0). The library targets .NET 10 and uses nothing beyond the base class library — System.IO.Compression and System.Xml.Linq are 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).

Getting the library

NuGet (pending). The package id is n8PDF and the release pipeline builds, tests, and packs it (with symbols and XML documentation) on every release tag — but it has not yet been published to NuGet (#61). Once it is:

dotnet add package n8PDF

From source (today).

git clone https://github.com/nathanpond/n8PDF.git
dotnet build n8PDF/src/n8PDF --configuration Release

Then 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 Release

Builds are deterministic — the same input builds the same assembly, whoever builds it.

First conversion

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);

Options

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);

Reproducible fonts

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.

Converting documents you did not write

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 and what is still open.

Mail merge

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.

Clone this wiki locally