PdfEngine.Layout 1.0.260513.1

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

PdfEngine

PdfEngine is a .NET 8 document engine for generating, enriching, parsing, and transforming PDF documents in business workflows.

It is built for teams that need more than a simple PDF writer:

  • layout-driven PDF generation
  • PDF/A-3A and PDF/A-3B output
  • Factur-X / ZUGFeRD generation
  • parsing for text, tables, images, bookmarks, form fields, and signatures
  • merge, extract, reorder, and split operations
  • stamping overlays on existing PDFs
  • custom fonts, form fields, metadata, attachments, and accessibility options

Official portal:

What You Can Build

With PdfEngine, you can:

  • generate invoices, statements, reports, forms, certificates, and branded documents
  • produce archival PDFs with PDF/A output
  • generate hybrid e-invoices with Factur-X XML payloads
  • inspect existing PDFs and extract their structure
  • split or merge incoming PDFs before downstream processing
  • stamp approvals, review panels, labels, or form-like overlays onto third-party PDFs

Main Components

The repository is split into a few focused assemblies:

  • PdfEngine.Core
    • document model, elements, metadata, fonts, options, and shared types
  • PdfEngine.Layout
    • the main PdfGenerator entry point and rendering pipeline
  • PdfEngine.Parser
    • parsing engine for text, tables, images, signatures, bookmarks, and diagnostics
  • PdfEngine.Infrastructure
    • lower-level helpers used by the rendering and parsing pipeline
  • PdfEngine.Cli
    • a command-line utility for inspection, extraction, merge, and split scenarios

For most generation scenarios, the entry point you will use is:

using PdfEngine.Layout;

var generator = new PdfGenerator();

Quick Start

1. Create a document

using PdfEngine.Domain.Documents;
using PdfEngine.Domain.Elements;
using PdfEngine.Domain.Fonts;
using PdfEngine.Layout;

var document = new PdfDocument
{
    Metadata = new PdfDocumentMetadata
    {
        Title = "Quarterly business review",
        Author = "PdfEngine",
        Subject = "Management report",
        Creator = "PdfEngine sample"
    }
};

var page = new PdfPage();
page.Elements.Add(
    new Paragraph()
        .Add("Quarterly business review", PdfFontType.SerifBold, 24)
        .AddBr()
        .Add("Prepared for leadership and operations teams.", PdfFontType.Sans, 11));

document.Pages.Add(page);

var generator = new PdfGenerator();
byte[] pdfBytes = generator.Generate(document, outputPath: @"D:\output\report.pdf");

2. Use built-in fonts or register your own fonts

using PdfEngine.Core.Fonts;
using PdfEngine.Domain.Fonts;
using PdfEngine.Layout;

var generator = new PdfGenerator()
    .UseBuiltInFontPack(PdfBuiltInFontPack.OpenSans)
    .RegisterFont(PdfFontType.SansBold, @"D:\fonts\Inter-Bold.ttf", "Inter Bold");

3. Generate archival PDFs

using PdfEngine.Domain.Documents;
using PdfEngine.Layout;

var generator = new PdfGenerator();

byte[] pdfA3A = generator.GeneratePdfA3A(document, outputPath: @"D:\output\report-a3a.pdf");
byte[] pdfA3B = generator.GeneratePdfA3B(document, outputPath: @"D:\output\report-a3b.pdf");

Factur-X Generation

PdfEngine can generate a Factur-X document directly from:

  • a ready-made XML payload
  • XML bytes
  • a strongly typed invoice object

Generate Factur-X from structured invoice data

using PdfEngine.Domain.Documents;
using PdfEngine.Layout;

var invoiceData = new PdfFacturXInvoiceData
{
    InvoiceNumber = "INV-2026-0042",
    CurrencyCode = "EUR",
    Seller = new PdfFacturXParty
    {
        Name = "PdfEngine Demo SARL",
        CountryCode = "FR"
    },
    Buyer = new PdfFacturXParty
    {
        Name = "Contoso France",
        CountryCode = "FR"
    }
};

invoiceData.LineItems.Add(new PdfFacturXLineItem
{
    Name = "Business subscription",
    Quantity = 1m,
    UnitPrice = 49m
});

var facturXOptions = new PdfFacturXOptions
{
    ValidationProfile = FacturXValidationProfile.En16931
};

var generator = new PdfGenerator();

byte[] pdfBytes = generator.GenerateFacturX(
    document,
    invoiceData,
    facturXOptions: facturXOptions,
    outputPath: @"D:\output\invoice-facturx.pdf");

Build XML explicitly before generation

using PdfEngine.Domain.Documents;

var xmlBuilder = new PdfFacturXXmlBuilder();
string xmlContent = xmlBuilder.BuildXmlContent(invoiceData);

Validation patterns

PdfEngine ships with PdfFacturXInvoicePreValidator, a profile-aware pre-flight check that catches missing EN 16931 fields before any PDF parsing or XML rendering. It exposes two layered APIs:

Layer Method Returns Use case
Soft (caller-side) Validate(invoice, profile) IReadOnlyList<string> UI feedback, batch reports, unit tests
Hard (built-in safety net) EnsureValid(invoice, profile) void (throws PdfFacturXValidationException) Invoked automatically inside GenerateFacturX / ConvertToFacturX

Inspect errors without try/catch and short-circuit the generation when invalid:

using PdfEngine.Domain.Documents;

IReadOnlyList<string> errors = PdfFacturXInvoicePreValidator.Validate(
    invoiceData, FacturXValidationProfile.En16931);

if (errors.Count > 0)
{
    // Display each entry to the user, return 400 from your API, etc.
    foreach (string e in errors) Console.WriteLine($"  - {e}");
    return; // skip generation
}

byte[] pdfBytes = generator.GenerateFacturX(document, invoiceData, /* ... */);
Pattern 2 — Batch processing

Accumulate errors across many invoices and only convert the valid ones:

var report = new List<(string Id, IReadOnlyList<string> Errors)>();

foreach (PdfFacturXInvoiceData invoice in batch)
{
    var errors = PdfFacturXInvoicePreValidator.Validate(invoice, profile);
    if (errors.Count > 0)
    {
        report.Add((invoice.InvoiceNumber, errors));
        continue;
    }
    generator.GenerateFacturX(document, invoice, /* ... */);
}
Pattern 3 — Built-in safety net (default behaviour)

When you call GenerateFacturX(..., PdfFacturXInvoiceData) or ConvertToFacturX(..., PdfFacturXInvoiceData) directly, the validator runs internally and throws a PdfFacturXValidationException with the full error list if any required field is missing. The exception inherits from ArgumentException and exposes Profile and Errors properties so you can react programmatically.

try
{
    generator.GenerateFacturX(document, invoiceData,
        facturXOptions: new PdfFacturXOptions
        {
            ValidationProfile = FacturXValidationProfile.Basic
        });
}
catch (PdfFacturXValidationException ex)
{
    Console.WriteLine($"Profile {ex.Profile} rejected the invoice:");
    foreach (string e in ex.Errors) Console.WriteLine($"  - {e}");
}
Which fields are checked?

The rules are profile-cumulative (each profile inherits the rules of the lower profile) and mirror the EN 16931 Business Rules (BR-) and Conditional Calculation rules (BR-CO-) implemented by Mustang / FNFE schematron:

  • MINIMUMInvoiceNumber, IssueDate, CurrencyCode, seller Name + CountryCode + VatIdentifier, buyer Name + CountryCode, totals BT-109 / BT-112 / BT-115.
  • BASIC WL — adds TotalTaxAmount (BT-110) and full postal addresses (StreetLine1, PostCode, City) for both seller and buyer.
  • BASIC — adds at least one line item satisfying: Name, Quantity > 0, UnitCode, UnitPrice ≥ 0, TaxCategoryCode, TaxRate ≥ 0.
  • EN 16931 — adds arithmetic consistency |BT-112 − (BT-109 + BT-110)| ≤ 0.01 (BR-CO-15) and the payment-terms conditional rule BR-CO-25 (DueDate or PaymentMeansInformation when DuePayableAmount > 0).
  • EXTENDED — same checks as EN 16931 (additional fields are optional).

For the exhaustive Business Term × profile matrix, see docs/facturx-bt-reference.pdf (or regenerate it from generate_facturx_bt_reference.py).

When NOT to use the soft API

If you control the invoice data end-to-end (deterministic mapping from a trusted database, no user input), the built-in EnsureValid() hook is sufficient. Wrap the call in a single try/catch (PdfFacturXValidationException) to log and continue.

Existing PDF Operations

PdfGenerator also exposes high-level document operations for existing PDFs.

Merge multiple PDFs

var generator = new PdfGenerator();

byte[] merged = generator.Merge(
    new[]
    {
        @"D:\input\part-1.pdf",
        @"D:\input\part-2.pdf",
        @"D:\input\appendix.pdf"
    },
    outputPath: @"D:\output\merged.pdf");

Extract pages

var generator = new PdfGenerator();

byte[] extracted = generator.ExtractPages(
    @"D:\input\catalog.pdf",
    new[] { 1, 2, 5, 8 },
    outputPath: @"D:\output\catalog-selection.pdf");

Reorder pages

var generator = new PdfGenerator();

byte[] reordered = generator.ReorderPages(
    @"D:\input\board-pack.pdf",
    new[] { 3, 1, 2, 4 },
    outputPath: @"D:\output\board-pack-reordered.pdf");

Split when a reference changes

var generator = new PdfGenerator();

var splitDocuments = generator.SplitByReferenceChange(
    @"D:\input\batched-invoices.pdf",
    new[]
    {
        @"Invoice\s*No\.\s*:\s*(?<value>[A-Z0-9\-]+)",
        @"Customer\s*Ref\.\s*:\s*(?<value>[A-Z0-9\-]+)"
    });

Stamping Existing PDFs

Stamp(...) lets you add overlays to a PDF that was generated by PdfEngine or by a third-party source.

Typical use cases:

  • approval stamps
  • internal review blocks
  • labels, badges, and watermarks
  • visual enrichment of incoming third-party PDFs

Approval overlay

using PdfEngine.Domain.Common;
using PdfEngine.Domain.Documents;
using PdfEngine.Domain.Elements;
using PdfEngine.Domain.Fonts;
using PdfEngine.Layout;

var stamp = new PdfStampDocument();

stamp.ForPage(1).Elements.Add(
    new RectangleElement()
        .SetSize(220, 54)
        .SetStroke(2f, PdfColor.Red)
        .SetFillColor(new PdfColor(1f, 0.97f, 0.97f))
        .WithPosition(26, 28));

stamp.ForPage(1).Elements.Add(
    new Paragraph()
        .Add("APPROUVE", PdfFontType.SansBold, 18)
        .SetColor(PdfColor.Red)
        .WithPosition(40, 60));

var generator = new PdfGenerator();
byte[] stamped = generator.Stamp(
    @"D:\input\report.pdf",
    stamp,
    outputPath: @"D:\output\report-stamped.pdf");

Richer review panel overlay

var stamp = new PdfStampDocument();

stamp.ForPage(1).Elements.Add(
    new StyledBlock()
        .SetTitle("Validation interne")
        .SetText("Document verifie le 02/05/2026.\nControle visuel OK.\nTransmission autorisee.")
        .SetWidth(250)
        .SetPadding(14)
        .SetBorderColor(new PdfColor(0.1f, 0.32f, 0.6f))
        .SetBackgroundColor(new PdfColor(0.95f, 0.97f, 1f))
        .SetTitleColor(new PdfColor(0.1f, 0.32f, 0.6f))
        .SetTextColor(new PdfColor(0.18f, 0.22f, 0.28f))
        .SetTitleFont(PdfFontType.SansBold, 14)
        .SetTextFont(PdfFontType.Sans, 10)
        .WithPosition(26, 110));

stamp.ForPage(1).Elements.Add(
    new BadgeElement()
        .SetText("Controle qualite")
        .SetFont(PdfFontType.SansBold, 10)
        .SetBackgroundColor(new PdfColor(0.12f, 0.48f, 0.85f))
        .SetTextColor(PdfColor.White)
        .SetPadding(10, 6)
        .WithPosition(36, 122));

Parsing and Inspection

The parser can inspect an existing PDF and extract:

  • full text and structured text
  • text blocks and diagnostics
  • tables and merged tables
  • image streams
  • bookmarks
  • form fields
  • embedded files
  • signatures
  • PDF/A and Factur-X hints

Parse a PDF

using PdfEngine.Parser.Engine;

byte[] pdfBytes = File.ReadAllBytes(@"D:\input\document.pdf");

var parser = new PdfParserEngine();
PdfParsedDocument parsed = parser.Parse(pdfBytes);

Console.WriteLine(parsed.PageCount);
Console.WriteLine(parsed.Metadata.Title);
Console.WriteLine(parsed.Bookmarks.Count);
Console.WriteLine(parsed.Signatures.Count);

Extract tables

using PdfEngine.Parser.Engine;

var parser = new PdfParserEngine();
var tablesByPage = parser.ExtractTables(File.ReadAllBytes(@"D:\input\statement.pdf"));
var mergedTables = parser.ExtractTablesMergedAcrossPages(File.ReadAllBytes(@"D:\input\statement.pdf"));

Extract image streams

using PdfEngine.Parser.Engine;

var parser = new PdfParserEngine();
var images = parser.ExtractImageStreams(File.ReadAllBytes(@"D:\input\brochure.pdf"));

Best-Effort Conversion

PdfEngine currently exposes best-effort conversion methods for existing PDFs:

  • ConvertToPdfA3ABestEffort(...)
  • ConvertToPdfA3BBestEffort(...)
  • ConvertToFacturXBestEffort(...)

These methods enrich an existing PDF with additional metadata and attachments, but they do not reconstruct every internal resource of arbitrary third-party PDFs.

That means:

  • they are useful for controlled workflows
  • they can work very well on many input files
  • they should not be presented as strict conformance guarantees for every arbitrary PDF source

For strict output, prefer native generation:

  • GeneratePdfA3A(...)
  • GeneratePdfA3B(...)
  • GenerateFacturX(...)

Command-Line Utility

The repository also contains a CLI project for diagnostics and batch work.

Examples:

dotnet run --project .\PdfEngine.Cli\PdfEngine.Cli.csproj -- info D:\pdfs-for-tests\sample.pdf
dotnet run --project .\PdfEngine.Cli\PdfEngine.Cli.csproj -- text D:\pdfs-for-tests\sample.pdf
dotnet run --project .\PdfEngine.Cli\PdfEngine.Cli.csproj -- tables D:\pdfs-for-tests\sample.pdf --merge-pages
dotnet run --project .\PdfEngine.Cli\PdfEngine.Cli.csproj -- merge D:\output\merged.pdf D:\a.pdf D:\b.pdf

Notes About Licensing

Some advanced features are guarded by runtime licensing checks.

How to get a license

To obtain a license key:

  1. go to https://www.pdf-engine.fr
  2. choose the plan that matches your usage
  3. create your client account
  4. complete the signup and, for paid plans, the checkout flow
  5. open your client dashboard to retrieve your license key

The client portal is the reference place to:

  • create your account
  • choose a Demo, Starter, Business, or Pro plan
  • complete payment when required
  • receive your access code
  • view and copy your active license key
  • manage your current subscription plan

If you are starting with an evaluation flow, the Demo plan is the fastest way to validate the SDK before switching to a higher plan.

If your integration uses licensed capabilities, register your key before calling those features:

using PdfEngine.Domain.Licensing;

await PdfEngineLicense.RegisterAsync("YOUR-LICENSE-KEY");

The exact licensing flow depends on your deployment and package strategy.

Build

dotnet build .\PdfEngine.Core\PdfEngine.Core.csproj
dotnet build .\PdfEngine.Layout\PdfEngine.Layout.csproj
dotnet build .\PdfEngine.Parser\PdfEngine.Parser.csproj

Repository Structure

PdfEngine.Core/           Shared document model, options, fonts, common types
PdfEngine.Infrastructure/ Shared infrastructure for rendering and parsing
PdfEngine.Layout/         PdfGenerator and rendering pipeline
PdfEngine.Parser/         Pdf parsing engine
PdfEngine.Cli/            Command-line tooling

Summary

PdfEngine is best suited when you want one engine that can cover the full document lifecycle:

  • create PDFs from a structured layout model
  • generate archival and invoice-compliant outputs
  • parse incoming PDFs for downstream workflows
  • operate on existing PDFs
  • enrich third-party files with stamps and overlays

If your use case mixes generation, parsing, archival output, and workflow enrichment, PdfEngine gives you a single .NET-native toolchain for that pipeline.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  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

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
1.0.260513.1 40 5/13/2026
1.0.260512.1 67 5/12/2026
1.0.260510.2 94 5/10/2026
1.0.260510.1 88 5/10/2026
1.0.260509.6 97 5/10/2026
1.0.260509.2 96 5/9/2026
1.0.260509.1 94 5/9/2026
1.0.260507.5 84 5/7/2026
1.0.260507.4 84 5/7/2026
1.0.260507.3 82 5/7/2026
1.0.260507.2 84 5/7/2026
1.0.260507.1 86 5/7/2026
1.0.260506.9 95 5/6/2026
1.0.260506.8 83 5/6/2026
1.0.260506.7 92 5/6/2026
1.0.260506.6 85 5/6/2026
1.0.260506.1 85 5/6/2026