Text extraction
var textItems = await pdf.ExtractTextAsync();
foreach (var item in textItems)
{
Console.WriteLine(item.Text);
}
Docs
Curated documentation for the main API surface, fluent authoring, package choices, and PDF behavior.
Quick Start
Start by installing the package, importing the core namespaces, and deciding whether you are loading an
existing PDF or creating a new one from scratch. The main entry points are Pdf.Load(...) for
existing documents, Pdf.Create(...) for low-level blank documents, and Pdf.New()
for the fluent authoring surface.
dotnet add package ZingPDF
using ZingPDF;
using ZingPDF.Syntax.CommonDataStructures;
using var input = File.OpenRead("input.pdf");
using var pdf = Pdf.Load(input);
var pageCount = await pdf.GetPageCountAsync();
var firstPage = await pdf.GetPageAsync(1);
using var pdf = Pdf.Create(options =>
{
options.MediaBox = Rectangle.FromDimensions(595, 842);
});
using var output = File.Create("blank.pdf");
await pdf.SaveAsync(output);
await Pdf.New()
.Page(page => page
.Size(595, 842)
.Text(text => text
.Value("Monthly delivery report")
.HelveticaBold()
.FontSize(24)
.At(48, 780))
.Rectangle(box => box
.At(48, 720)
.Size(220, 40)
.Stroke(RGBColour.PrimaryBlue, 1)
.Fill(new RGBColour(0.9, 0.97, 1)))
.Text(text => text
.Value("Generated with the fluent authoring API")
.Helvetica()
.FontSize(11)
.InBox(60, 730, 196, 20)
.AlignStart()
.AlignMiddle()
.Padding(0)))
.SaveToFileAsync("authored.pdf");
Pdf.New() is a builder for authored pages. It covers page sizing, text, shapes, paths,
images, watermarks, boxed text alignment, wrapping, and font registration through the same core
document APIs. Use pdf.Pages(...) when you want the same page-level fluent operations on an
existing document.
Use the standard API when you are loading an existing PDF, working directly with Pdf and
Page, or need explicit control over each step. Use the fluent API when you are building a new
document from authored content and want the page layout to read top to bottom.
Standard API:
using System.Text.RegularExpressions;
using var pdf = Pdf.Load(File.OpenRead("input.pdf"));
var font = await pdf.RegisterStandardFontAsync(StandardPdfFonts.HelveticaBold);
var firstPage = await pdf.GetPageAsync(1);
await firstPage.AddTextAsync(
"Approved",
Rectangle.FromCoordinates(
new Coordinate(48, 780),
new Coordinate(168, 804)),
font,
18,
RGBColour.Black);
var summaryPage = await pdf.AppendPageAsync(options =>
{
options.MediaBox = Rectangle.FromDimensions(595, 842);
});
await summaryPage.AddTextAsync(
"Summary",
Rectangle.FromCoordinates(
new Coordinate(48, 780),
new Coordinate(220, 808)),
font,
24,
RGBColour.Black);
await pdf.SaveAsync(File.Create("edited.pdf"));
Fluent API:
using var pdf = Pdf.Load(File.OpenRead("input.pdf"));
await pdf.Pages(pages => pages
.Page(1, page => page
.Text(text => text
.Value("Approved")
.HelveticaBold()
.FontSize(18)
.At(48, 780)))
.Append(page => page
.Size(595, 842)
.Text(text => text
.Value("Summary")
.HelveticaBold()
.FontSize(24)
.At(48, 780))))
.SaveAsync(File.Create("edited.pdf"));
Remove(...) and Delete(...) are also available when you want to drop pages from an existing document.
Dedicated guide: How to create PDFs with the fluent authoring API in C#.
When saving, use an output stream that is writable and seekable. If you save to a different stream than the input stream, it must also be empty.
using var pdf = Pdf.Load(File.OpenRead("input.pdf"));
await pdf.AppendPageAsync(options =>
{
options.MediaBox = Rectangle.FromDimensions(595, 842);
});
await pdf.InsertPageAsync(1, options =>
{
options.MediaBox = Rectangle.FromDimensions(300, 300);
});
await pdf.DeletePageAsync(2);
await pdf.SaveAsync(File.Create("pages-updated.pdf"));
using var pdf = Pdf.Load(File.OpenRead("input.pdf"));
using var selectedPages = await pdf.ExportPagesAsync([1, 3, 5]);
using var selectedOutput = File.Create("selected-pages.pdf");
await selectedPages.SaveAsync(selectedOutput);
var parts = await pdf.SplitAsync(10);
try
{
for (var index = 0; index < parts.Count; index++)
{
using var output = File.Create($"part-{index + 1}.pdf");
await parts[index].SaveAsync(output);
}
}
finally
{
foreach (var part in parts)
{
part.Dispose();
}
}
var textItems = await pdf.ExtractTextAsync();
foreach (var item in textItems)
{
Console.WriteLine(item.Text);
}
using var output = File.Create("watermarked.pdf");
var firstPage = await pdf.GetPageAsync(1);
await firstPage.AddWatermarkAsync("DRAFT");
await pdf.AddWatermarkAsync("CONFIDENTIAL");
pdf.Compress(dpi: 72, quality: 75);
await pdf.SaveAsync(output);
Page.AddWatermarkAsync(...) targets one page. Pdf.AddWatermarkAsync(...)
applies the same text across the whole document.
Related guides: Create a blank PDF, Insert, append, and delete pages, Export selected pages or split a document, Extract text, Add watermarks, and Compress output.
Runnable examples: CreateBlankPdf and CreateProjectStatusReport.
Packages
ZingPDF ships as a small set of packages. Use the core package unless you specifically need HTML rendering, Liquid HTML templates, Google Fonts, or OCR support for scanned or image-based pages.
Start with ZingPDF. Add ZingPDF.FromHTML for HTML-to-PDF,
ZingPDF.Templates.LiquidHtml for Liquid HTML templates,
ZingPDF.GoogleFonts for Google Fonts, and ZingPDF.OCR for scanned or
image-based pages.
ZingPDFThe main PDF library for loading, creating, editing, merging, watermarking, compression, redaction, forms, metadata, and encryption.
net8.0SkiaSharp and ImageSharp, so test on your target platform
ZingPDF.FromHTMLHTML-to-PDF package for Chromium-based rendering.
net8.0ZingPDF.TemplatesShared source and diagnostic types for template renderer packages.
net8.0ZingPDF.Templates.LiquidHtmlLiquid HTML template package for creating PDFs from data models.
net8.0ZingPDF.FromHTML for HTML-to-PDF conversionZingPDF.FromHTMLZingPDF.GoogleFontsGoogle Fonts integration package for registering Google Fonts in a PDF document.
net8.0ZingPDF.OCROCR companion package for scanned and image-based pages, with options for embedded-text-first or OCR-first workflows.
net8.0ZingPDF unless you specifically need HTML
conversion.ZingPDF.Templates.LiquidHtml for Liquid variables, loops, and conditionals in HTML
templates that render to PDF.ZingPDF.GoogleFonts if you need to register Google Fonts directly from the Google
Fonts catalog.ZingPDF.OCR when the page is primarily image-based or when OCR is part of your extraction workflow.ZingPDF.FromHTML as a separate deployment concern with its own runtime
requirements.Templates
ZingPDF.Templates.LiquidHtml renders a Liquid HTML template with a .NET model, converts the
rendered HTML to PDF through ZingPDF.FromHTML, and writes the PDF stream you provide.
dotnet add package ZingPDF.Templates.LiquidHtml
using ZingPDF.Templates.LiquidHtml;
var invoice = new
{
Number = "INV-1001",
CustomerName = "Ada Lovelace",
Items = new[]
{
new { Description = "Consulting", Total = 240m },
new { Description = "Support", Total = 80m }
}
};
await using var output = File.Create("invoice.pdf");
await LiquidHtmlPdfTemplate
.FromFile("invoice.liquid.html")
.RenderAsync(invoice, output);
var html = await LiquidHtmlPdfTemplate
.FromFile("invoice.liquid.html")
.RenderHtmlAsync(invoice);
ZingPDF.FromHTML after Liquid rendering.PdfTemplateRenderException.Diagnostics for template parse, load, and render failures.Dedicated guide: Create a PDF from a Liquid HTML template.
Fonts
ZingPDF registers standard PDF fonts and embedded TrueType fonts. ZingPDF.GoogleFonts
adds Google Fonts registration through the same document-level font path.
using ZingPDF.Fonts;
using ZingPDF.Graphics;
using var pdf = Pdf.Create();
var page = await pdf.GetPageAsync(1);
var font = await pdf.RegisterStandardFontAsync(StandardPdfFonts.Helvetica);
await page.AddTextAsync(
"Hello, world",
Rectangle.FromDimensions(240, 80),
font,
18,
RGBColour.Black);
await Pdf.New()
.Page(page => page
.Text(text => text
.Value("Hello, world")
.Helvetica()
.FontSize(18)
.At(48, 780)))
.SaveToFileAsync("hello.pdf");
StandardPdfFonts.HelveticaStandardPdfFonts.HelveticaBoldStandardPdfFonts.HelveticaObliqueStandardPdfFonts.HelveticaBoldObliqueStandardPdfFonts.TimesRomanStandardPdfFonts.TimesBoldStandardPdfFonts.TimesItalicStandardPdfFonts.TimesBoldItalicStandardPdfFonts.CourierStandardPdfFonts.CourierBoldStandardPdfFonts.CourierObliqueStandardPdfFonts.CourierBoldObliquevar font = await pdf.RegisterTrueTypeFontAsync(
"MyFont-Regular.ttf");
await page.AddTextAsync(
"Custom font text",
Rectangle.FromDimensions(240, 80),
font,
18,
RGBColour.Black);
await Pdf.New()
.Page(page => page
.Text(text => text
.Value("Custom font text")
.WithTrueTypeFont("MyFont-Regular.ttf", fontName: "MyFont-Regular")
.FontSize(18)
.At(48, 780)))
.SaveToFileAsync("custom-font.pdf");
using ZingPDF.GoogleFonts;
var client = new GoogleFontsClient("<google-fonts-api-key>");
var font = await pdf.RegisterGoogleFontAsync(
client,
new GoogleFontRequest
{
Family = "Inter",
Variant = "regular"
});
await page.AddTextAsync(
"Google Fonts text",
Rectangle.FromDimensions(240, 80),
font,
18,
RGBColour.Black);
ZingPDF.GoogleFonts requires a Google Fonts Developer API key and network access.Related guides: Use standard PDF fonts, Use Google Fonts in a PDF, Embed a TrueType font, and Add text to a page.
Drawing
The page API supports drawing vector paths with stroke and fill options. The fluent authoring API uses the same drawing primitives for rectangles, lines, paths, images, watermarks, and positioned or boxed text.
using ZingPDF.Elements.Drawing;
using ZingPDF.Graphics;
await page.AddPathAsync(new Path(
new StrokeOptions(RGBColour.PrimaryRed, 2),
null,
PathType.Linear,
[
new Coordinate(10, 10),
new Coordinate(50, 60),
new Coordinate(80, 20)
]));
await page.AddPathAsync(new Path(
null,
new FillOptions(RGBColour.PrimaryBlue),
PathType.Bezier,
[
new Coordinate(20, 20),
new Coordinate(40, 80),
new Coordinate(90, 80),
new Coordinate(110, 20)
]));
await Pdf.New()
.Page(page => page
.Line(line => line
.From(48, 720)
.To(220, 720)
.Stroke(RGBColour.PrimaryRed, 2))
.Rectangle(box => box
.At(48, 660)
.Size(120, 40)
.Stroke(RGBColour.PrimaryBlue, 1)
.Fill(new RGBColour(0.9, 0.97, 1)))
.Path(path => path
.Bezier()
.Point(220, 660)
.Point(260, 720)
.Point(320, 720)
.Point(360, 660)
.Fill(RGBColour.PrimaryBlue)))
.SaveToFileAsync("drawing.pdf");
await Pdf.New()
.Page(page => page
.Text(text => text
.Value("A longer note that should wrap inside the box.")
.Helvetica()
.FontSize(11)
.InBox(48, 640, 180, 42)
.AlignStart()
.AlignTop()
.Padding(0)
.Wrap()
.ClipOverflow()))
.SaveToFileAsync("boxed-text.pdf");
Related guides: Add text to a page, Use standard PDF fonts, and Embed a TrueType font.
The symbol-by-symbol reference is generated from the source and XML documentation comments, and is best used when you need exact member signatures or type-level detail.
Metadata
GetMetadataAsync() reads and updates the trailer Info dictionary through a
higher-level wrapper.
Title, Author, Subject, Keywords,
Creator, and CreationDate.
Producer is stamped to ZingPDF and
ModifiedDate is refreshed on save.
using var output = File.Create("metadata-updated.pdf");
var metadata = await pdf.GetMetadataAsync();
metadata.Title = "Quarterly Report";
metadata.Author = "Taylor Smith";
metadata.Subject = "Q1 FY2026";
metadata.Keywords = "finance,quarterly";
metadata.Creator = "Back Office Importer";
metadata.CreationDate = new DateTimeOffset(2026, 3, 1, 9, 0, 0, TimeSpan.Zero);
await pdf.SaveAsync(output);
Related guide: How to update PDF metadata in C#.
Forms
GetFormAsync() returns a Form wrapper for terminal field enumeration, typed
field lookup, and field-family-specific behavior.
Form.GetFieldsAsync() returns terminal fields as IFormField instances.Applicant.Address.Suburb.If you already know the fully qualified field name, fetch the field directly instead of enumerating the whole form.
var form = await pdf.GetFormAsync();
if (form is null)
{
return;
}
var nameField = await form.GetFieldAsync("Applicant.Name");
if (nameField is not null)
{
await nameField.SetValueAsync("Taylor Smith");
}
var stateField = await form.GetFieldAsync("Applicant.State");
if (stateField is not null)
{
await stateField.SelectOptionByTextAsync("NSW");
}
GetFieldsAsync() returns terminal fields in document order so you can inspect names and map
them into your own data model.
var form = await pdf.GetFormAsync();
if (form is null)
{
return;
}
foreach (var field in await form.GetFieldsAsync())
{
Console.WriteLine(field.Name);
}
FlattenAsync() writes the current field appearances into page content and removes the
interactive form structure.
var form = await pdf.GetFormAsync();
if (form is null)
{
return;
}
await form.FlattenAsync();
await pdf.SaveAsync(File.Create("form-flattened.pdf"));
TextFormField is the simplest path when you need to fill names, dates, IDs, or other typed values.
GetValueAsync().SetValueAsync(...) and ClearAsync().Form.GetFieldAsync<TextFormField>(name).ChoiceFormField covers dropdowns and list boxes.
GetOptionsAsync().SelectOptionByTextAsync(...) and SelectOptionByValueAsync(...).Form.GetFieldAsync<ChoiceFormField>(name).CheckboxFormField and RadioButtonFormField expose selectable options through SelectableOption.
GetOptionsAsync().SelectAsync() and DeselectAsync().PushButtonFormField is read-only at the high level today.
SignatureFormField supports both inspection and signing.
SignAsync(...) to sign an existing visible field.Pdf.SignInvisibleAsync(...) when you only need cryptographic validation and want to add a hidden field automatically.Related guides: Fill PDF form fields and Flatten a PDF form and Sign a PDF form field.
Encryption
ZingPDF supports authenticating encrypted PDFs, applying password protection to plain PDFs, controlling user permissions, and saving decrypted output from encrypted input. New encrypted output can use legacy RC4-128 for compatibility or AES-128 and AES-256 through the Standard security handler.
using var pdf = Pdf.Load(File.OpenRead("encrypted.pdf"));
await pdf.AuthenticateAsync("password");
var pageCount = await pdf.GetPageCountAsync();
using var pdf = Pdf.Create();
await pdf.EncryptAsync("user-password", "owner-password");
await pdf.SaveAsync(File.Create("protected.pdf"));
If you do not pass an algorithm, ZingPDF writes RC4-128 for compatibility. Pass
PdfEncryptionAlgorithm.Aes128 or PdfEncryptionAlgorithm.Aes256 when you want AES
output.
// AES-128
using var aes128Pdf = Pdf.Create();
await aes128Pdf.EncryptAsync(
"user-password",
"owner-password",
PdfEncryptionAlgorithm.Aes128);
await aes128Pdf.SaveAsync(File.Create("protected-aes128.pdf"));
// AES-256
using var aes256Pdf = Pdf.Create();
await aes256Pdf.EncryptAsync(
"user-password",
"owner-password",
PdfEncryptionAlgorithm.Aes256);
await aes256Pdf.SaveAsync(File.Create("protected-aes256.pdf"));
Pass PdfEncryptionPermissions when the file should open with a password but still limit what
the user can do afterward.
using var restrictedPdf = Pdf.Create();
await restrictedPdf.EncryptAsync(
"user-password",
"owner-password",
PdfEncryptionAlgorithm.Aes256,
PdfEncryptionPermissions.Print | PdfEncryptionPermissions.FillForms);
await restrictedPdf.SaveAsync(File.Create("restricted.pdf"));
EncryptAsync(...) supports Standard security handler RC4-128, AES-128, and AES-256 output.
If you do not pass an algorithm, ZingPDF writes RC4-128 for compatibility.
using var pdf = Pdf.Load(File.OpenRead("encrypted.pdf"));
await pdf.DecryptAsync("password");
await pdf.SaveAsync(File.Create("decrypted.pdf"));
DecryptAsync(...) removes encryption from the latest saved revision. If you also need to
remove older encrypted history from the file bytes, call RemoveHistoryAsync() before saving.
Related guides: Encrypt a PDF with AES and Remove a password from a PDF.
Redaction
ZingPDF can mark exact extracted-text matches or explicit page regions, remove matched text from supported page content, redact intersecting vector and XObject content, draw visible redaction overlays, and rewrite the document so earlier file history is not preserved.
using var pdf = Pdf.Load(File.OpenRead("input.pdf"));
var plan = await pdf.RedactionAsync();
await plan.MarkTextAsync("12345");
await plan.MarkTextAsync(new Regex(@"\b\d{3}-\d{2}-\d{4}\b"));
plan.MarkRegion(
1,
Rectangle.FromCoordinates(
new Coordinate(72, 520),
new Coordinate(180, 548)));
await plan.ApplyAsync(new PdfRedactionOptions
{
OverlayText = "REDACTED"
});
await pdf.SaveAsync(File.Create("redacted.pdf"));
MarkTextAsync(...) uses extracted glyph runs to find exact string or regex matches and maps those matches back to supported text-showing operators.MarkRegion(...) adds an explicit rectangle on a known page and removes intersecting supported text, vector, image XObject, and form XObject content.ApplyAsync(...) rewrites matched content, draws the redaction overlay, and switches the document to rewritten-file save behavior.PdfRedactionOptions controls fill colour and overlay text. Incremental-save redaction is not supported.This version removes matched text from supported page content and structurally redacts intersecting vector paths, image XObjects, and form XObjects. It still refuses inline images and shading content, and it does not yet derive OCR bounds for scanned pages.
Redaction currently uses the standard Pdf API because it is a document-level workflow over an existing file, rather than an authored page layout.
Related guide: Redact PDF content in C#.
Save Behavior
By default, SaveAsync(...) writes an incremental update. That preserves earlier revisions
in the file history.
SaveAsync(...) appends a new revision to the file by default, leaving earlier revisions in
the document history.
using var pdf = Pdf.Load(File.OpenRead("input.pdf"));
await pdf.AddWatermarkAsync("INTERNAL");
await pdf.SaveAsync(File.Create("output.pdf"));
Call RemoveHistoryAsync() before saving if you want the file rewritten with only the latest
live objects. This removes earlier incremental revisions, helping with file size and document security.
using var pdf = Pdf.Load(File.OpenRead("input.pdf"));
await pdf.RemoveHistoryAsync();
await pdf.SaveAsync(File.Create("sanitized.pdf"));
Pdf.Load(...) requires a seekable input stream. That is a parsing requirement and does not change if you later call RemoveHistoryAsync().SaveAsync(...) currently requires a writable, seekable output stream because the save pipeline checks stream length and, in some paths, resets and truncates the stream before writing.Form updates and metadata stamping are applied as part of the save pipeline.
Compress(...) works on eligible unfiltered streams and can recompress JPEG image streams.
DecompressAsync() skips JPEG image streams.
Related guides: Rewrite a PDF without incremental history, Redact PDF content, Add watermarks, and Compress output.
OCR
The core package extracts embedded PDF text. Add ZingPDF.OCR when you need OCR-based text extraction for scanned or image-based pages.
using ZingPDF.OCR;
using var pdf = Pdf.Load(File.OpenRead("scanned.pdf"));
var engine = new TesseractOcrEngine("./tessdata", "eng");
var text = await pdf.ExtractPlainTextWithOcrAsync(engine);
ExtractTextWithOcrAsync(...) prefers embedded text first by default, and that behavior can be changed with PdfOcrOptions.ExtractPlainTextWithOcrAsync(...) returns a joined plain-text view across pages.Low-Level Access
IPdf.Objects exposes the underlying PDF object collection and page tree for integrations
that need direct syntax-level access.
For exact member signatures and the generated type reference, go straight to the API docs.
Technical FAQ
ZingPDF keeps the input stream open and resolves PDF objects on demand. It does not materialize the whole file into a full in-memory document model at load time.
Pdf.Load(...) holds a seekable input stream for the lifetime of the Pdf instance.Pdf instances should be treated as not thread-safe. The document model is mutable, the
object collection keeps mutable caches and update state, and parsing operates against a shared underlying
stream. That model keeps single-document workflows fast and predictable.
Pdf instance across concurrent operations.Pdf instances on separate streams.ZingPDF tolerates some malformed content and common cross-reference defects, but it does not try to repair every damaged file structure.
startxref is missing or out of range and a classic xref table still exists near the end of the file.Evaluation FAQ
ZingPDF is the default package for loading, creating, editing, forms, encryption, and save
workflows. Add companion packages only for the extra capability they provide.
ZingPDF.FromHTML for Chromium-based HTML-to-PDF.ZingPDF.Templates.LiquidHtml for Liquid HTML templates that render to PDF.ZingPDF.GoogleFonts for Google Fonts registration.ZingPDF.OCR for OCR on scanned or image-based pages.Evaluation and other non-commercial use stay free. Paid seats are required when the library is used for commercial work or internal business operations outside genuine evaluation.
ZingPDF targets net8.0 and is intended for Windows, Linux, and macOS deployments that support
.NET 8.
ZingPDF.FromHTML has additional browser-runtime requirements.ZingPDF.Templates.LiquidHtml has the same browser-runtime requirements after Liquid rendering.Pdf.Load(...) must be seekable.ZingPDF covers the main document-editing path, but some PDF features are still outside the current high-level API.