Inkwright.Forms 0.1.2

There is a newer version of this package available.
See the version list below for details.
dotnet add package Inkwright.Forms --version 0.1.2
                    
NuGet\Install-Package Inkwright.Forms -Version 0.1.2
                    
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="Inkwright.Forms" Version="0.1.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Inkwright.Forms" Version="0.1.2" />
                    
Directory.Packages.props
<PackageReference Include="Inkwright.Forms" />
                    
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 Inkwright.Forms --version 0.1.2
                    
#r "nuget: Inkwright.Forms, 0.1.2"
                    
#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 Inkwright.Forms@0.1.2
                    
#: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=Inkwright.Forms&version=0.1.2
                    
Install as a Cake Addin
#tool nuget:?package=Inkwright.Forms&version=0.1.2
                    
Install as a Cake Tool

Inkwright

A modern .NET 10 PDF library (Apache-2.0) with not one native dependency. The architecture is layered the way System.Text.Json is: COS primitives → streaming I/O → document model → add-ons that only the people who need them pay for.

What v1 does

  • Reads anything: cross-reference tables and streams, hybrid files, object streams, incremental updates. A broken file does not throw — it is recovered by scanning, and LoadDiagnostics says exactly what had to be repaired. Across a corpus of 7807 real files, 7800 open and round-trip without a single refusal; five are deliberately corrupt fixtures from other people's fuzzing sets (no catalogue, no header) that are honestly rejected with PdfFormatException, and two more are empty and never reach the parser.
  • Lossless editing: an object the model never parsed is copied byte for byte on rewrite. Object numbers survive, and so do diagrams, annotations and everything the library did not understand.
  • Incremental saving: changes are appended and not one original byte moves — the only way not to break signatures somebody else made.
  • Report composer (Inkwright.Layout): two-phase Measure/Draw with SpacePlan, line breaking and justification, tables with repeating headers, running heads, "page X of Y", explicit breaks, and a LayoutException carrying the element tree instead of silently clipping.
  • Text: built-in metrics for the fourteen standard fonts, TrueType/OpenType embedding with subsetting and Identity-H, letter extraction with coordinates. Checked against PdfPig on real files: mean similarity ≥ 0.90.
  • PDF → Markdown: Tagged PDF becomes CommonMark/GFM headings, paragraphs, lists, links and tables; raster Figure elements come out as deduplicated PNG sidecars. The untagged fallback and every loss of semantics land in the diagnostics rather than passing quietly.
  • Images: JPEG is embedded as it stands, PNG without a single re-encode — its deflate stream is already what PDF wants — and alpha becomes a soft mask.
  • Comments: notes, highlight/underline/strikeout over words found by search, FreeText and callouts, shapes, polylines, ink, stamps, attachments and links. Reply threads and review states per ISO 32000-1, cascading deletion together with the popup and the thread, flattening into page content, export to flat records with the quoted text, and a ready appearance stream for every type. Review rounds travel to other tools and back through XFDF (ISO 19444-1) and FDF (§12.7.7), merging on /NM so a second import does not duplicate anything.
  • Forms (Inkwright.Forms): typed fields, filling with generated appearance streams, flattening, and [PdfForm] plus an incremental source generator for reflection-free mapping and Native AOT.
  • Signatures (Inkwright.Signing): PAdES building blocks from B-B to B-LTA — signature and document timestamps (RFC 3161), the validation data store (DSS/VRI), DocMDP certification signatures and FieldMDP field locking — plus external keys for an HSM, built entirely on the base class library with no BouncyCastle. For B-LT and B-LTA the application supplies OCSP/CRL data it has already verified and sets the trust policy. Digests are SHA-2, and also SHA-3 and SHAKE256 per ISO/TS 32001 where the platform provides them; the Edwards curves of ISO/TS 32002 are verified by arithmetic of the library's own.
  • Archival documents: Tagged PDF and eleven PDF/A levels — eight from parts 1 to 3 (A1B, A1A, A2B, A2U, A2A, A3B, A3U, A3A) and three from part 4 (A4, A4E, A4F) — with a generated sRGB ICC profile and a validator that names the ISO 19005 clause a file breaks. Reconciled clause by clause against the texts of parts 1–3 and part 4, and checked against 106 archival files from another implementation. PDF/A-4 is the only level a PDF 2.0 file can claim: parts 1 to 3 require a %PDF-1.n header and part 4 requires %PDF-2.n. Factur-X and ZUGFeRD attachments with the fx: schema description.
  • Accessibility: PDF/UA-1 and UA-2 profiles (ISO 14289) with a validator that tells a broken requirement from an unmet recommendation and names the clause; conformance declarations per PDF Declarations, including the Well-Tagged PDF 1.0 levels.
  • Encryption: reads RC4, AES-128, AES-256 and AES-GCM; writes AES-256 in cipher block chaining (revision 6) or Galois/counter mode (revision 7, ISO/TS 32003) with authentication. Certificate encryption to X.509 recipients (§7.6.5) both ways, on EnvelopedCms.
  • Navigation: outlines (PdfOutline) are read and edited in the model and built by the composer, destinations to a page and to a structure element (§12.3.2.3), and output intents on the document and on a page.
  • Integrity: the authentication code of ISO/TS 32004 — written and verified, as a standalone token or as a signature attribute; checked against 94 files from another implementation.

A quick look

using Inkwright;
using Inkwright.Layout;

// A report that flows across pages by itself
Report.Create()
    .Page(page =>
    {
        page.Size(PageSize.A4);
        page.Margin(45);
        page.Header().Text("Quarterly report", TextStyle.Default.FontSize(18).Bold());
        page.Content().Table(table =>
        {
            table.Columns(ColumnWidth.Fraction(4), ColumnWidth.Fraction(1));
            table.Grid();
            table.Header().Text("Item").Text("Amount");
            foreach (var line in lines)
                table.Row().Text(line.Name).Text(line.Total);
        });
        page.Footer().AlignCenter().Text(text => text.PageNumbers("{0} of {1}"));
    })
    .Save("report.pdf");
// Editing somebody else's file: a stamp on top, original bytes untouched
using PdfDocument document = PdfDocument.Load("invoice.pdf");

using (ContentCanvas canvas = ContentCanvas.ForPage(document.Pages[0]))
{
    canvas.Save()
          .Opacity(0.15)
          .Transform(PdfMatrix.Rotation(38) * PdfMatrix.Translation(140, 240))
          .DrawText("PAID", 0, 0, document.Fonts.HelveticaBold, 72, PdfColor.Parse("#1B7F3B"))
          .Restore();
    canvas.Commit();
}

document.SaveIncremental("invoice-paid.pdf");
// The text back out, with coordinates
using PdfDocument document = PdfDocument.Load("scan.pdf");

foreach (PdfLetter letter in document.Pages[0].ExtractLetters())
    Console.WriteLine($"{letter.Text} at {letter.Origin} {letter.FontSize:0.#} pt");
// Reviewing: highlight words, leave a note, reply, set a state
PdfPage page = document.Pages[0];

page.Annotations.MarkText("delivery dates", PdfAnnotationType.Highlight, author: "Priya");
PdfTextAnnotation note = page.Annotations.AddNote(
    new PdfPoint(505, 700), "Has the rate changed?", "Priya", PdfNoteIcon.Help);

note.Reply("No, unchanged", "Tom");
note.SetReviewState(PdfReviewState.Completed, "Priya");

document.SaveIncremental("reviewed.pdf");
// A review round travels between reviewers apart from the document, as XFDF or FDF
document.ExportReview("round-4.xfdf");

PdfReviewImportResult round = other.ImportReview("round-4.xfdf");
Console.WriteLine($"{round.Added} added, {round.Updated} updated");
// A form filled from a typed record
[PdfForm]
public partial record Invoice(
    [property: PdfField("customer.name")] string Customer,
    [property: PdfField("invoice.total", Format = "0.00")] decimal Total,
    [property: PdfField("invoice.paid")] bool Paid);

PdfForm form = PdfForm.Open(document)!;
form.Fill(new Invoice("Ada Lovelace", 1234.50m, true));
form.Flatten();
// A signature as an incremental update
byte[] signed = PdfSigner.Sign(document, new PdfSignatureOptions
{
    Certificate = certificate,
    Reason = "Approved",
    Appearance = PdfRectangle.FromSize(60, 60, 220, 70),
});

Performance

A 200-page report (8000 table rows) and a 90-page document, measured on an Intel Core i7-8700 under Windows Server 2022 with .NET 10.0.10 and BenchmarkDotNet 0.15.8. Method and comparison notes are in docs/benchmarks.md; numbers move with the machine, so treat them as a shape rather than a promise.

Time Allocated
Inkwright, canvas, 200 pp. 27.3 ms 13.6 MB
PDFsharp 6.2, 200 pp. 57.7 ms 38.1 MB
Inkwright, composer, 200 pp. 204.0 ms 108.7 MB
Inkwright, open a file 217 µs 188 KB
PdfPig 0.1.11, open a file 724 µs 485 KB
Inkwright, extract text 93 ms 116 MB
PdfPig 0.1.11, extract text 135 ms 103 MB

Documentation

Building

dotnet build Inkwright.slnx
dotnet test Inkwright.slnx
dotnet pack Inkwright.slnx -c Release --output artifacts/packages
dotnet run --project samples/Inkwright.Samples
dotnet run -c Release --project benchmarks/Inkwright.Benchmarks -- --filter *

Layout of the repository

src/Inkwright                  core: COS, filters, reader, writer, DOM, content, fonts, text,
                               colour, functions, image decoding, merging
src/Inkwright.Cjk              the 168 predefined CMaps of ISO 32000-2 Table 116
src/Inkwright.Layout           composer, Tagged PDF, PDF/A profiles
src/Inkwright.Forms            AcroForm: fields, appearances, flattening
src/Inkwright.Forms.Generator  Roslyn incremental generator (packed into Forms)
src/Inkwright.Signing          CMS/PAdES signatures on the base class library
tests/                         xUnit: round-trip over 7807 real PDFs, extraction checked against
                               PdfPig, layout, tagging, PDF/A, forms, signatures
benchmarks/                    BenchmarkDotNet against PDFsharp and PdfPig
samples/                       an invoice, low-level drawing, editing, a form, an archival document

The limits chosen on purpose are listed in architecture.md: no rasterisation, no shaping of complex scripts, JPX and progressive JPEG pass through untouched, and certificate handlers are not supported.

Built on this

Quillwright.Pdf prints Word documents: it does the pagination, tables, running heads and tagging itself and writes the file through Inkwright. A useful example of what ContentCanvas gives you alongside font metrics and a structure tree.

Cellwright.Pdf prints Excel workbooks: the styled grid with number formats, two-dimensional pagination with scaling, repeating headers and running heads. The text layer it shares with Quillwright — Inkwright.Text.BidiLayout and ArabicShaper — lives here: the same UAX #9, only pointing the producer's way, logical order → drawing order.

Third-party code this project borrows from is listed in THIRD-PARTY-NOTICES.md.

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.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Inkwright.Forms:

Package Downloads
Inkwright.Signing

Digital signatures for Inkwright built entirely on the .NET base class library: detached CMS, PAdES B-B through B-LTA building blocks, RFC 3161 timestamps, external signer hooks and signature verification. No BouncyCastle.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.1.3 102 8/9/2026
0.1.2 115 8/1/2026