Delphi / Free Pascal

The RustPdf unit wraps the rust-pdf C core with idiomatic, handle-owning classes. It covers the whole product surface: vector graphics, embedded/subset fonts & Unicode text, paragraphs, images, PDF/A (1b–3a + A-4/4e/4f), tagged/accessible output, attachments, AcroForm fields, manipulation, text extraction, encryption and digital signatures. One unit compiles under both Delphi (10.x+, Windows/macOS/Linux) and Free Pascal (3.2+).

Two classes do almost everything. TPdfDocument authors a new PDF; TPdfEditable loads and manipulates an existing one. Both own a native handle. Free them with .Free (a try…finally is the idiom). Stateless calls (version, licensing, extraction, signing) live on the Pdf record.

Free each handle exactly once. Like any Object Pascal object, a handle must not be freed twice or used after .Free. Using a freed handle raises ERustPdf, but calling .Free a second time is undefined behavior that can crash the process (the object memory is already gone). Stick to the try…finally Doc.Free end idiom and do not also free it elsewhere; if a variable may be freed on more than one path, use FreeAndNil(Doc) so a duplicate free becomes a harmless no-op.

Download

The binding ships as one versioned archive: the RustPdf.pas unit, the native library for each platform (lib/<os-arch>/), a sample and an INSTALL.txt. Basic PDF generation works immediately as a free trial; the corporate features unlock with a license token.

rustpdf-delphi v0.4.8 Windows · macOS · Linux native libraries included · Delphi 10.x+ / FPC 3.2+
Download .zip SHA-256 checksum

Prefer to build from source? make delphi-dist assembles the same archive. Using Boss? The repo is boss install-able. After unzipping, drop the matching native library next to your executable (matching the app's bitness on Windows). Full steps are in the bundled INSTALL.txt and the Installation section below.

Installation

The binding is a single source unit, RustPdf.pas, with no native build step and no headers to install. It loads the shared library (libpdf_ffi) built from the Rust core at run time, so there is no fixed link name. Add the binding directory to your unit search path:

delphi
uses RustPdf;   // add bindings/delphi to the unit search path (-Fu / Project Options)

Build the native library and (optionally) point RUSTPDF_LIB at it:

shell
cargo build -p pdf-ffi                       # → target/debug/libpdf_ffi.{dylib,so,dll}
export RUSTPDF_LIB=/path/to/target/debug/libpdf_ffi.dylib   # optional

The loader searches, in order: $RUSTPDF_LIB; the library next to the executable or in the current directory (the deployment layout); target/debug and target/release walking up from those dirs (the dev tree); then the bare platform name via the OS loader. Compile a console program with Free Pascal:

shell
fpc -Mdelphi -Fubindings/delphi hello.dpr

or with Delphi's command-line compiler:

shell
dcc64 -Ubindings/delphi hello.dpr

Verify the library loads:

delphi
Writeln(Pdf.Version);   // native library version, e.g. 0.4.8

Quick start

A one-page document with a filled rectangle, saved to disk:

delphi
program hello;
{$IFDEF FPC}{$MODE DELPHI}{$H+}{$CODEPAGE UTF8}{$ENDIF}
uses RustPdf;
var
  Doc: TPdfDocument;
begin
  Doc := TPdfDocument.Create;          // A4 by default
  try
    Doc.AddPage;
    Doc.FillRgb(0.86, 0.20, 0.18);
    Doc.Rect(72, 640, 200, 120);       // x, y, width, height (points)
    Doc.Fill;
    Doc.SaveToFile('out.pdf');
  finally
    Doc.Free;
  end;
end.

Most methods return the document, so calls chain:

delphi
var
  Doc: TPdfDocument;
  Font: Integer;
  Data: TBytes;
begin
  Doc := TPdfDocument.Create;
  try
    Font := Doc.AddFontFile('Roboto-Regular.ttf');
    Doc.AddPage
       .FillRgb(0.1, 0.1, 0.12).Rect(0, 800, 595, 42).Fill
       .ShowText(Font, 24, 72, 740, 'Olá, açúcar — café');
    Data := Doc.ToBytes;               // in-memory bytes instead of a file
  finally
    Doc.Free;
  end;
end;
UTF-8 source. Under Free Pascal, add {$CODEPAGE UTF8} so accented string literals reach the engine as proper UTF-8. Delphi handles UTF-8 source natively.

Licensing & activation

Basic generation (everything above) is always free. The corporate features (PDF/A, digital signatures/PAdES, encryption, accessibility) require an active license token. Without one, those calls raise ERustPdf and produce no output.

Activation needs no rebuild. Easiest is an environment variable, auto-activated the first time a corporate feature is used:

shell
export RUSTPDF_LICENSE="010f0000…"           # the token we email you
# or point at a file:
export RUSTPDF_LICENSE_FILE=/etc/rustpdf/license.txt

Or activate explicitly in code:

delphi
Pdf.ActivateLicense(Token);   // raises ERustPdf if forged / expired / malformed
Verification is fully offline: signature + expiry checked against a public key embedded in the library. No network callback, no telemetry.

Coordinate system

Threading & concurrency

The core is Send but not Sync: you can build many documents in parallel, but a single handle must never be touched by two threads at once.

delphi
type
  TRenderThread = class(TThread)
  protected
    procedure Execute; override;
  end;

procedure TRenderThread.Execute;
var
  Doc: TPdfDocument;
begin
  Doc := TPdfDocument.Create;        // one document per thread
  try
    Doc.AddPage.FillRgb(0.1, 0.1, 0.12).Rect(72, 700, 200, 80).Fill;
    Doc.SaveToFile(Format('out-%d.pdf', [ThreadID]));
  finally
    Doc.Free;
  end;
end;

Authoring: create & save

TPdfDocument.Create free

Creates an empty document (A4 default page size). Always pair with try…finally Doc.Free end so the native handle is released.

MethodDescription
AddPageAppend a page using the default size.
AddPage(Width, Height)Append a page with an explicit size in points.
DefaultSize(W, H)Default size for subsequently added pages.
SetVersion(V)PDF header version: 0 → 1.4, 1 → 1.5, 2 → 1.7, 3 → 2.0.
PageCountNumber of pages so far.
ToBytesRender the document to TBytes.
SaveToFile(Path)Render and write to a file.
Validity. A document must have at least one page. Serializing an empty document raises an error. Color components (RGB/Gray/CMYK) are clamped to the valid 0–1 range.

Pages & vector graphics

Graphics state and path operators mirror PDF's content-stream model. Colors are RGB in 0.0–1.0.

MethodDescription
FillRgb(R, G, B)Fill color.
StrokeRgb(R, G, B)Stroke color.
LineWidth(W)Stroke width in points.
Rect(X, Y, W, H)Add a rectangle subpath.
FillFill the current path with the fill color.
StrokeStroke the current path with the stroke color.
delphi
Doc.AddPage;
Doc.StrokeRgb(0.10, 0.45, 0.90).LineWidth(3);
Doc.Rect(72, 600, 300, 160).Stroke;
Doc.FillRgb(0.95, 0.77, 0.06);
Doc.Rect(120, 640, 120, 80).Fill;
Doc.SaveToFile('shapes.pdf');

Fonts & text

Fonts are embedded and subsetted, with HarfBuzz-quality shaping, kerning and full Unicode (Type0/CIDFontType2 with ToUnicode, so text extracts and copies correctly, provided the embedded font covers those characters). Register a font once, then reference it by its integer id.

AddFontFile(Path): Integer   AddFont(Data: TBytes): Integer
ShowText(Font, Size, X, Y, Text, HeadingLevel = 0)

HeadingLevel (1–6) tags the run as H1H6 in an accessible document (see Accessibility); leave 0 for ordinary text. Strings cross the boundary as UTF-8.

delphi
var Regular: Integer;
…
Regular := Doc.AddFontFile('Roboto-Regular.ttf');
Doc.AddPage;
Doc.ShowText(Regular, 28, 72, 760, 'Invoice #1024');
Doc.ShowText(Regular, 12, 72, 720, '日本語 · Ελληνικά · العربية');
Doc.SaveToFile('text.pdf');
Font coverage. Characters outside the embedded font's coverage are silently dropped. They render as the missing-glyph box and won't extract or copy. The bundled Roboto fallback covers Latin, Greek and Cyrillic but not CJK, Arabic, Hebrew or emoji. Embed a font that covers every script you write.
No NUL bytes in strings. Text passed across the API must not contain a NUL (#0) character. It truncates the string at the FFI boundary, silently dropping everything after the NUL. This applies to shown text, metadata and field names.

Paragraphs

The paragraph layer wraps, aligns and justifies text inside a fixed width (greedy line breaking using shaped glyph widths).

Paragraph(Font, Size, X, Y, Width, Text, Align = paLeft)
delphi
const
  Intro = 'A long paragraph that wraps to the given width and is justified ' +
          'automatically; extra space is distributed between words.';
…
F := Doc.AddFontFile('Roboto-Regular.ttf');
Doc.AddPage;
Doc.Paragraph(F, 12, 72, 700, 451, Intro, paJustify);
Doc.SaveToFile('paragraph.pdf');

See the TPdfAlign enum for the alignment options.

Images

JPEGs are embedded verbatim (DCTDecode, no re-encode). PNGs are decoded and re-encoded (FlateDecode); alpha becomes an /SMask, palette becomes an Indexed color space. Register an image once, draw it many times.

MethodDescription
AddImageFile(Path): IntegerLoad JPEG/PNG from a file; returns the image id.
AddImagePng(Data: TBytes): IntegerRegister a PNG from memory.
AddImageJpeg(Data: TBytes): IntegerRegister a JPEG from memory.
DrawImage(Image, X, Y, W, H)Draw at (x, y) scaled to w × h points.
Figure(Image, X, Y, W, H, Alt)Draw as a tagged /Figure with alt text (accessibility).
delphi
Logo := Doc.AddImageFile('logo.png');
Doc.AddPage;
Doc.DrawImage(Logo, 72, 680, 160, 90);
Doc.SaveToFile('with_image.pdf');

PDF/A licensed

Produce archival-grade output. Pdfa defaults to A-2b; pass a TPdfaLevel for a specific level. An embedded sRGB ICC profile, output intent, XMP metadata and document /ID are added automatically; A-1b also forces PDF 1.4 and emits a /CIDSet, and A-4 (ISO 19005-4) is based on PDF 2.0.

Pdfa   Pdfa(Level: TPdfaLevel)
delphi
Doc.Pdfa(palA2B).SetInfo('Q3 Report', 'Acme Inc.', '', '', '');
F := Doc.AddFontFile('Roboto-Regular.ttf');
Doc.AddPage;
Doc.ShowText(F, 20, 72, 760, 'Archival report');
Doc.SaveToFile('report_pdfa.pdf');   // raises ERustPdf without a license granting PDF/A
A document title is recommended for valid PDF/A metadata, and required for the accessible “a” levels: call SetInfo with a non-empty title.

Accessibility (Tagged PDF / PDF/UA) licensed

Tagged builds a logical structure tree (PDF/UA-1). Combine with Pdfa(palA2A) for archival and accessible output. Use HeadingLevel on ShowText for H1H6, and Figure(…, Alt) for described images.

Tagged
delphi
Doc.Pdfa(palA2A).Tagged.SetInfo('Accessible report', '', '', '', '');
F := Doc.AddFontFile('Roboto-Regular.ttf');
Doc.AddPage;
Doc.ShowText(F, 26, 72, 760, 'Annual report', 1);   // H1
Doc.ShowText(F, 14, 72, 720, 'Overview', 2);         // H2
Doc.ShowText(F, 11, 72, 690, 'Body paragraph of the section…');
Chart := Doc.AddImageFile('chart.png');
Doc.Figure(Chart, 72, 520, 300, 150, 'Revenue grew 18% year over year');
Doc.SaveToFile('accessible.pdf');
Figures need a tagged document. Figure() only produces an accessible, alt-texted figure inside a tagged/accessible document; on a plain document the alt text has no effect.

Attachments (PDF/A-3) licensed

PDF/A-3 allows embedding arbitrary source files (e.g. the XML behind an e-invoice). Each attachment carries a MIME type and an TAFRelationship.

AttachFile(Name, Mime, Data, Relationship = afSource, Description = '')
delphi
Xml := ...;   // TBytes of invoice.xml
Doc.Pdfa(palA3B).SetInfo('E-invoice 1024', '', '', '', '');
F := Doc.AddFontFile('Roboto-Regular.ttf');
Doc.AddPage;
Doc.ShowText(F, 18, 72, 760, 'Invoice 1024');
Doc.AttachFile('invoice.xml', 'text/xml', Xml, afSource, 'Structured invoice data');
Doc.SaveToFile('einvoice.pdf');
PDF/A-4f needs an attachment. The A4f profile requires at least one embedded file (ISO 19005-4); the library now rejects A4f output that has no attachment, so call AttachFile before serializing.
The licence gate is on PDF/A, not on AttachFile itself. The licensed badge above reflects the PDF/A-3 workflow shown here. Calling AttachFile on a plain (non-PDF/A) document does not require a licence: it succeeds and produces a valid PDF with an /EmbeddedFile. The licence is enforced only when you also request a PDF/A level, which is what makes the attachment archival.

ZUGFeRD / Factur-X e-invoices licensed

Turn the document into a ZUGFeRD / Factur-X electronic invoice: the embedded XML (the Cross-Industry Invoice) is attached as factur-x.xml, the file is marked PDF/A-3, and the Factur-X identification is written into the XMP metadata. The visible PDF is the human-readable invoice; the embedded XML is its machine-readable twin. Validates as PDF/A-3 plus Factur-X under veraPDF.

Facturx(Xml: TBytes; Profile: TFacturxProfile = fxEN16931)
delphi
var Xml: TBytes;
…
Xml := TEncoding.UTF8.GetBytes(LoadXmlText);   // your Cross-Industry Invoice XML
Doc.SetInfo('Invoice INV-2026-001', '', '', '', '');
F := Doc.AddFontFile('Roboto-Regular.ttf');
Doc.AddPage;
Doc.ShowText(F, 18, 72, 760, 'Invoice INV-2026-001');
Doc.Facturx(Xml, fxEN16931);
Doc.SaveToFile('einvoice.pdf');   // PDF/A-3 + Factur-X; needs a PDF/A license

See the TFacturxProfile enum for the conformance levels (fxMinimum to fxExtended).

AcroForm fields

Build interactive forms with generated appearance streams (no NeedAppearances). Rectangles are PdfRect(x0, y0, x1, y1); Page is a 0-based page index. Dotted names ('a.b.c') create hierarchical fields.

MethodDescription
TextField(Name, Page, Rect, Value = '', Size = 0)Text input (Size = 0 → auto font size).
Checkbox(Name, Page, Rect, Checked)Checkbox (Checked: Boolean).
Dropdown(Name, Page, Rect, Options, Selected = -1, Size = 0)Combo box from an array of strings.
RadioGroup(Name, Page, Buttons, Selected = -1)Buttons: TRadioButtons; each has a Rect and ExportValue.
delphi
var Buttons: TRadioButtons;
…
Doc.AddPage;
Doc.TextField('applicant.name', 0, PdfRect(72, 700, 320, 720));
Doc.Checkbox('agree', 0, PdfRect(72, 660, 88, 676), False);
Doc.Dropdown('plan', 0, PdfRect(72, 620, 240, 640), ['Starter', 'Pro', 'Enterprise'], 1);

SetLength(Buttons, 2);
Buttons[0].Rect := PdfRect(72, 580, 88, 596);   Buttons[0].ExportValue := 'monthly';
Buttons[1].Rect := PdfRect(140, 580, 156, 596); Buttons[1].ExportValue := 'annual';
Doc.RadioGroup('billing', 0, Buttons, 1);
Doc.SaveToFile('form.pdf');

Fill and flatten fields later with TPdfEditable.

Page index and rectangle are validated. A field (or internal link) whose page does not exist, or whose rectangle is degenerate (x1 < x0 or zero area), is rejected when the document is serialized: ToBytes/Save raise ERustPdf ("targets page index N…" / "degenerate rectangle…") instead of producing an invisible, never-appearing widget. Pass a 0-based page index that exists and a rectangle with x1 > x0 and y1 > y0.

Add clickable link rectangles to the current page: a web link opens a URL; an internal link jumps to another page (optionally scrolling so a given Top y-coordinate sits at the top of the view).

LinkUri(R: TPdfRect; Uri: string)   LinkToPage(R: TPdfRect; PageIndex: NativeUInt [; Top: Double])
delphi
F := Doc.AddFontFile('Roboto-Regular.ttf');
Doc.AddPage;
Doc.ShowText(F, 14, 72, 760, 'Visit rustpdf.dev (see page 2)');
Doc.LinkUri(PdfRect(72, 756, 320, 776), 'https://rustpdf.dev/');   // web link
Doc.LinkToPage(PdfRect(330, 756, 430, 776), 1, 800);              // jump to page 2
Doc.AddPage;
Doc.SaveToFile('links.pdf');

Rectangles are PdfRect(x0, y0, x1, y1) in points; PageIndex is 0-based. The two-argument LinkToPage jumps without scrolling; the three-argument overload positions Top at the top of the view.

Bookmarks / outline free

Build a navigable document outline. A TPdfBookmark has a title, a target page and an optional Top; nest children with .Child(...). The object owns its children, so freeing the root frees the whole tree. A document with bookmarks opens with the outline pane shown.

TPdfBookmark.Create(Title; Page [; Top])   .Child(BM): TPdfBookmark   AddBookmark(BM)
delphi
var Root: TPdfBookmark;
…
F := Doc.AddFontFile('Roboto-Regular.ttf');
Doc.AddPage; Doc.AddPage; Doc.AddPage;

Root := TPdfBookmark.Create('Chapter 1', 0, 820);
try
  Root.Child(TPdfBookmark.Create('Section 1.1', 1))
      .Child(TPdfBookmark.Create('Section 1.2', 2));
  Doc.AddBookmark(Root);
  Doc.AddBookmark(TPdfBookmark.Create('Chapter 2', 2));
finally
  Root.Free;   // frees the whole tree
end;
Doc.SaveToFile('outline.pdf');

Metadata

SetInfo(Title, Author, Subject, Keywords, Creator)

Sets the document information dictionary (and, for PDF/A, the matching XMP). Pass an empty string '' for any field you want to leave unset.

delphi
Doc.SetInfo('Q3 Report', 'Acme Inc.', 'Quarterly results', 'finance, q3', '');

Manipulation: load an existing PDF

TPdfEditable parses an existing document (classic & xref streams, object streams, all standard filters, RC4/AES decryption) into an editable model. Pages are a flat list; the page tree is rebuilt on output.

TPdfEditable.Load(Data: TBytes)   TPdfEditable.Load(Data, Password)
TPdfEditable.LoadFromFile(Path)   TPdfEditable.LoadFromFile(Path, Password)
delphi
var Ed: TPdfEditable;
…
Ed := TPdfEditable.LoadFromFile('in.pdf');
try
  Writeln(Ed.PageCount);
finally
  Ed.Free;
end;

// encrypted input:
Ed := TPdfEditable.LoadFromFile('secured.pdf', 'user-or-owner-pw');
try
  Ed.SaveToFile('plain.pdf');
finally
  Ed.Free;
end;
Corrupt or truncated input. A badly damaged file (truncated mid-stream, for example) may still load via the recovery scan but recover zero pages, and Load does not raise in that case. Serializing a zero-page document raises ERustPdf ("document has no pages") instead of writing an invalid file, but check PageCount > 0 after loading untrusted input before relying on it.

Pages: merge, split, reorder, rotate

MethodDescription
Merge(Other)Append all pages of another TPdfEditable (objects renumbered & remapped).
RotatePage(Index, Degrees)Rotate one page (90 / 180 / 270).
DeletePage(Index)Remove a page.
ReorderPages(Order)Reorder with a full permutation array of indices.
ExtractPages(Indices): TPdfEditableNew document containing just those pages.
PageCountCurrent page count.
delphi
A := TPdfEditable.LoadFromFile('a.pdf');
B := TPdfEditable.LoadFromFile('b.pdf');
try
  A.Merge(B);                       // a now has a's pages followed by b's
  A.RotatePage(0, 90);
  A.ReorderPages([2, 1, 0]);
  A.SaveToFile('merged.pdf');
finally
  A.Free; B.Free;
end;
Page indices are 0-based. An out-of-range index to rotate/delete is silently ignored, and extract skips out-of-range indices. ReorderPages requires a true permutation of every page index (each used exactly once); an invalid argument (wrong length, a repeated index, or out-of-range) is rejected and leaves the page order unchanged. The call is a silent no-op that raises no error, so an invalid reorder cannot be detected from a return value.

Metadata, overlay & form fill

MethodDescription
SetInfo(Key, Value)Set one info entry (e.g. 'Title').
GetInfo(Key): stringRead an info entry.
SetXmp(Xml: TBytes)Replace the XMP metadata stream.
OverlayPage(Index, Content: TBytes)Overlay a content-stream fragment onto a page (stamps/watermarks).
FillTextField(Name, Value): BooleanFill an AcroForm text field; returns whether it was found.
delphi
Ed := TPdfEditable.LoadFromFile('form.pdf');
try
  Ed.SetInfo('Title', 'Filled form');
  Found := Ed.FillTextField('applicant.name', 'Jane Doe');
  Writeln('filled: ', Found, ' | title: ', Ed.GetInfo('Title'));
  Ed.SaveToFile('filled.pdf');
finally
  Ed.Free;
end;

Form fill & flatten free

Fill the fields of an existing AcroForm and (optionally) flatten them: filling generates a fresh appearance stream (no NeedAppearances), and flattening bakes every widget's appearance into the page content and removes the interactive form entirely.

MethodDescription
FieldNames: TPdfStringArrayFully-qualified names of every terminal field.
FillTextField(Name, Value): BooleanSet a text (or text-style choice) field; returns whether it matched.
SetCheckbox(Name, Checked = True): BooleanCheck/uncheck a checkbox.
SetRadio(Name, ExportValue): BooleanSelect a radio button by its export value.
SetChoice(Name, Value): BooleanSet a dropdown / list-box value.
FlattenFormsBake all fields into static content and drop the /AcroForm.
delphi
var Names: TPdfStringArray; I: Integer;
…
Ed := TPdfEditable.LoadFromFile('form.pdf');
try
  Names := Ed.FieldNames;                  // ['applicant.name', 'agree', 'plan', …]
  for I := 0 to High(Names) do
    Writeln(Names[I]);
  Ed.FillTextField('applicant.name', 'Jane Doe');
  Ed.SetCheckbox('agree', True);
  Ed.SetRadio('billing', 'annual');
  Ed.SetChoice('plan', 'Pro');
  Ed.FlattenForms;                         // optional: make it non-editable
  Ed.SaveToFile('filled.pdf');
finally
  Ed.Free;
end;

Watermarks free

Stamp a diagonal text watermark or a centered image watermark across every page, drawn semi-transparently over the existing content. Text uses the standard Helvetica font, so keep it to WinAnsi (Latin-1) for stamps like "CONFIDENTIAL". Set OpaqueBackground := True to white out a band behind the text instead of stamping over the content; pass RotationDeg to rotate the image watermark.

WatermarkText(Text; Size = 64; R = 0.5; G = 0.5; B = 0.5; Opacity = 0.30; RotationDeg = 45; OpaqueBackground = False): TPdfEditable
WatermarkImageFile(Path; Width, Height; Opacity = 0.30; RotationDeg = 0): TPdfEditable
delphi
Ed := TPdfEditable.LoadFromFile('report.pdf');
try
  Ed.WatermarkText('CONFIDENTIAL', 64, 0.5, 0.5, 0.5, 0.25, 45, False);
  Ed.WatermarkImageFile('stamp.png', 200, 80, 0.30, 30);   // rotated 30°
  Ed.SaveToFile('stamped.pdf');
finally
  Ed.Free;
end;

Positioned drawing free

Stamp a single page in place: fill a rectangle and/or drop a line of text at exact coordinates. Coordinates are in the page's visible space (origin lower-left, y up), so content lands where a viewer sees it regardless of the page's /Rotate; RotationDeg rotates the text counter-clockwise about its anchor. Text uses the standard Helvetica font (keep it WinAnsi / Latin-1). Both return whether the page existed.

function FillRect(PageIndex: Integer; X, Y, Width, Height, R, G, B, Opacity: Double): Boolean;
function PlaceText(PageIndex: Integer; X, Y: Double; const Text: UTF8String; Size, R, G, B, RotationDeg: Double; Align: TPdfAlign = paLeft; FontId: Integer = -1; Anchor: TVerticalAnchor = vaBaseline): Boolean;
function MaskedText(PageIndex: Integer; X, Y, Width, Height: Double; const Text: UTF8String; Size: Double; TextR, TextG, TextB, BgR, BgG, BgB: Double; Align: TPdfAlign = paLeft; FontId: Integer = -1; VAlign: TVerticalAlign = vlMiddle; Padding: Double = -1): Boolean;
function DrawImage(PageIndex: Integer; const Image: TBytes; X, Y, Width, Height, RotationDeg: Double; Anchor: TImageAnchor = iaCorner): Boolean;

A typical use is masking a spot with an opaque white box, then writing a value over it. The optional Align (paLeft, paRight, paCenter, paJustify) on PlaceText shifts the start point along the baseline so the text is left/right/center-aligned about (X, Y). MaskedText does the mask-then-write in one call: it fills the box [X, Y, X+Width, Y+Height] in the background colour (each 0..1; defaults to white), then writes the text in the text colour (defaults to black) horizontally aligned per Align and vertically centered in the box. DrawImage takes raw JPEG/PNG image bytes and places the image with its lower-left corner at (X, Y) scaled to Width × Height points. The trailing FontId/Anchor/VAlign/Padding parameters are covered in Stamping fonts, anchors & paragraphs below.

delphi
var Data: TBytes;
    G: TPageGeometry;
    Ed: TPdfEditable;
begin
  Data := TFile.ReadAllBytes('invoice.pdf');
  G := Pdf.MeasurePage(Data, 0);                       // read the page geometry first
  Ed := TPdfEditable.Load(Data);
  try
    Ed.FillRect(0, 400, G.Height - 60, 120, 18, 1, 1, 1, 1);   // mask: opaque white
    Ed.PlaceText(0, 404, G.Height - 56, 'PAID', 12, 0, 0.5, 0, 0);
    Ed.DrawImage(0, TFile.ReadAllBytes('logo.png'), 404, G.Height - 120, 120, 48, 0);
    Ed.Save('stamped.pdf');
  finally
    Ed.Free;
  end;
end;

Stamping fonts, anchors & paragraphs free

The stamping primitives above also accept an embedded font, explicit vertical anchors, and a word-wrapping paragraph variant. See the interactive positioning guide for a visual tour of every anchor.

Embedded stamping fonts

Register a TrueType/OpenType font on the editable document and pass its id as the FontId parameter of PlaceText, MaskedText or PlaceParagraph. The font is embedded as a subset, so stamped text renders with the real glyphs and metrics (full Unicode, not just WinAnsi). FontId = -1 keeps the built-in Helvetica.

function AddFontFile(const Path: string): Integer;
function AddFont(const Data: TBytes): Integer;
delphi
var FId: Integer;
…
FId := Ed.AddFontFile('Roboto-Regular.ttf');
Ed.PlaceText(0, 72, 700, 'Olá — texto Unicode', 14, 0, 0, 0, 0, paLeft, FId);

Vertical anchors (TVerticalAnchor)

PlaceText and PlaceParagraph take an Anchor saying what Y means. vaBaseline is the historical PlaceText default. vaTop hangs the text from Y (the baseline lands ascent × Size below it, matching legacy fixed-position layout); vaBottom rests the descender line on Y. vaLineTop/vaLineBottom anchor via the legacy line box (ascent + descent), so a single line and a wrapped paragraph agree vertically; for PlaceParagraph they also use the line-box height as the leading basis.

delphi
// Baseline exactly where legacy fixed-position layout would put it:
Ed.PlaceText(0, 72, 720, 'hung from the top', 12, 0, 0, 0, 0, paLeft, -1, vaTop);
Ed.PlaceText(0, 72, 640, 'line box bottom', 12, 0, 0, 0, 0, paLeft, -1, vaLineBottom);

MaskedText: vertical alignment & padding

VAlign positions the line inside the box: vlMiddle (default) keeps the historical cap-height centering, vlTop hangs the line from the top edge (top line-alignment in rectangle-based text APIs semantics), vlBottom rests the descender line on the bottom edge. Padding is the horizontal edge inset in points for paLeft/paRight: a negative value keeps the historical min(0.15 × Size, Width / 4), and 0 starts flush with the box edge (legacy PDF libraries DrawString semantics).

delphi
// White box, top-aligned text, flush with the left edge:
Ed.MaskedText(0, 100, 240, 200, 42, 'ALIGNED TOP', 12,
              0, 0, 0, 1, 1, 1, paLeft, -1, vlTop, 0);

Wrapped paragraphs

function PlaceParagraph(PageIndex: Integer; X, Y, Width: Double; const Text: UTF8String; Size: Double = 12; R: Double = 0; G: Double = 0; B: Double = 0; Align: TPdfAlign = paLeft; FontId: Integer = -1; MaxHeight: Double = 0; LineHeight: Double = 1.0; Anchor: TVerticalAnchor = vaTop; RotationDeg: Double = 0): Boolean;
function PlaceParagraphMeasured(PageIndex: Integer; X, Y, Width: Double; const Text: UTF8String; Size, R, G, B: Double; Align: TPdfAlign; FontId: Integer; MaxHeight, LineHeight: Double; Anchor: TVerticalAnchor; RotationDeg: Double; out Lines: Integer; out Height: Double): Boolean;

PlaceParagraph breaks Text into lines that fit Width points (greedy, by word; #10 forces a break) and draws them downward from (X, Y). With vaBottom/vaLineBottom the block is bottom-pinned: its bottom rests on Y and it grows upward by its real content height. MaxHeight > 0 is a height ceiling that truncates overflowing lines (with a bottom anchor the last lines stay pinned and the overflow is cut from the top); 0 means unlimited. LineHeight scales the default 1.2 × Size leading, and RotationDeg rotates the laid-out block counter-clockwise about the anchor. PlaceParagraphMeasured additionally reports the number of lines drawn and the consumed block height in points, so you can stack blocks without re-measuring.

delphi
var Lines: Integer; Height: Double;
…
// Wrap into a 130 pt column from the top-left corner (72, 700):
Ed.PlaceParagraph(0, 72, 700, 130, 'terms and conditions apply to every order');
// Bottom-pinned block with a 60 pt ceiling, measured:
Ed.PlaceParagraphMeasured(0, 300, 120, 130, 'grows upward from its anchor', 10,
                          0, 0, 0, paLeft, -1, 60, 1.0, vaBottom, 0, Lines, Height);
Writeln(Format('%d lines, %.1f pt used', [Lines, Height]));

Stamp space: visible vs media

procedure SetStampSpace(Space: TStampSpace);

ssVisible (the default) interprets coordinates in the page's displayed space, compensating /Rotate so a stamp with RotationDeg = 0 reads upright on screen. ssMedia interprets coordinates and RotationDeg in the raw PDF user space, never composing with the page's /Rotate or crop offset (the raw-coordinate semantics of legacy layout engines). Use ssMedia to reproduce legacy-engine placement on rotated or scanned pages; the setting applies to subsequent FillRect/PlaceText/MaskedText/PlaceParagraph/DrawImage calls. Watermarks and redaction are unaffected.

delphi
Ed.SetStampSpace(ssMedia);      // legacy raw-space coordinates from here on
Ed.PlaceText(0, 200, 300, 'MEDIA', 10, 0, 0, 0, 0);
Ed.SetStampSpace(ssVisible);    // back to the default

Image rotation anchor

DrawImage takes a trailing Anchor: iaCorner (default) rotates the image about its own lower-left corner at (X, Y), so a rotated image sweeps around that point; iaBoundingBox lands the rotated image's bounding box with its lower-left at (X, Y) (bounding-box layout semantics: the drawn pixels always sit at or above/right of the anchor; a 90° image occupies [X, X+Height] × [Y, Y+Width]).

delphi
Ed.DrawImage(0, Logo, 260, 60, 80, 30, 90, iaBoundingBox);

Redaction licensed

True redaction: the text and graphics whose origin falls inside a rectangle are removed from the content stream (not just covered), so the data is gone from the file and is no longer extractable. Opaque black boxes are then painted over the regions. Returns True when the page index was valid.

Redact(PageIndex: NativeUInt; const Rects: array of TPdfRect): Boolean
delphi
Ed := TPdfEditable.LoadFromFile('statement.pdf');
try
  Ed.Redact(0, [PdfRect(60, 590, 400, 620), PdfRect(60, 540, 400, 570)]);
  Ed.SaveToFile('redacted.pdf');     // raises ERustPdf without a license granting redaction
finally
  Ed.Free;
end;
Content under a rect is deleted before the file is written, so Pdf.ExtractText on the output no longer returns it.

Convert to PDF/A licensed

Convert an existing PDF to archival PDF/A (a basic profile: A-1b, A-2b or A-3b). An sRGB output intent, PDF/A XMP metadata (synced with /Info) and a document /ID are added. Fails if any font is not embedded (PDF/A requires every font embedded) or a level-A profile is requested.

ConvertToPdfa(Level: TPdfaLevel = palA2B): TPdfEditable
delphi
Ed := TPdfEditable.LoadFromFile('in.pdf');
try
  Ed.ConvertToPdfa(palA2B);          // raises ERustPdf if fonts aren't embedded
  Ed.SaveToFile('archival.pdf');     // veraPDF: PDF/A-2b compliant
finally
  Ed.Free;
end;

Optimize & compact

MethodDescription
OptimizeDrop unreferenced objects, Flate-compress uncompressed streams, dedupe identical objects.
Compact(On = True)Pack objects into object streams + emit a cross-reference stream.
delphi
Ed := TPdfEditable.LoadFromFile('big.pdf');
try
  Ed.Optimize.Compact(True);
  Ed.SaveToFile('small.pdf');
finally
  Ed.Free;
end;

Encryption licensed

Apply standard-handler encryption at output. AES-256 (V5/R6) uses OS-CSPRNG keys/IVs.

Encrypt(Method = encAES256, User = '', Owner = '', ReadOnlyPerms = False)
delphi
Ed := TPdfEditable.LoadFromFile('in.pdf');
try
  Ed.Encrypt(encAES256, '', 'owner-secret', True);
  Ed.SaveToFile('secured.pdf');     // raises ERustPdf without an Encryption license
finally
  Ed.Free;
end;

See the TEncryption enum for RC4 / AES-128 / AES-256.

Passwords protect, permissions only advise. A non-empty user password is real cryptographic protection: opening with the wrong password is rejected (cross-checked against qpdf for all three ciphers). The read-only permission flags, by contrast, are advisory: they are enforced only by the viewer, and a file with an empty user password opens with no prompt, so any tool can strip the restrictions. Treat read-only as a hint to well-behaved viewers, not as an access control.

Output & incremental update

MethodDescription
ToBytes: TBytesSerialize the manipulated document.
SaveToFile(Path)Serialize to a file.
ToBytesIncremental(Original: TBytes): TBytesAppend only changes to the original bytes (signature-safe, non-destructive).
delphi
Original := ...;   // TBytes of in.pdf
Ed := TPdfEditable.Load(Original);
try
  Ed.SetInfo('Subject', 'reviewed');
  Incremental := Ed.ToBytesIncremental(Original);   // original bytes preserved verbatim
finally
  Ed.Free;
end;

Normalize & downgrade free

Turn a PDF/A or higher-version file back into a plain PDF. StripPdfa removes the PDF/A conformance markers (the output intent and the pdfaid XMP), SetVersion sets the header version, and Normalize does both in one call (strip PDF/A and set the version).

MethodDescription
SetVersion(V): TPdfEditableSet the PDF header version: 0 → 1.4, 1 → 1.5, 2 → 1.7, 3 → 2.0.
StripPdfa: TPdfEditableRemove PDF/A conformance (output intent + pdfaid XMP), leaving a plain PDF.
Normalize(V): TPdfEditableStrip PDF/A and set the version V in one step.
delphi
Ed := TPdfEditable.LoadFromFile('archival_pdfa.pdf');
try
  Ed.Normalize(2);                   // strip PDF/A + downgrade to PDF 1.7
  Ed.SaveToFile('plain.pdf');
finally
  Ed.Free;
end;

Digital signatures licensed

Sign a PDF with a PKCS#7 detached signature via an incremental update (the original bytes are preserved). Keys and certificates are passed as DER TBytes. Pades = True switches to PAdES-B-B.

Pdf.Sign(PdfBytes, KeyDer, CertDer, Reason = '', Location = '', Name = '', Pades = False): TBytes
delphi
PdfBytes := ...;   // TBytes of contract.pdf
KeyDer   := ...;   // PKCS#8 private key (DER)
CertDer  := ...;   // X.509 certificate (DER)

Signed := Pdf.Sign(PdfBytes, KeyDer, CertDer, 'Approved', 'New York', 'Jane Doe', True);
// Verify in a shell: pdfsig contract.signed.pdf  →  "Signature is Valid."

Deferred / HSM signing licensed

Sign without ever handing the library a private key. When the key lives in an HSM, a cloud KMS, a smartcard or a PKI token (any PKI: eIDAS, AATL, a national CA), you supply the raw RSA signature and the binding builds and embeds the CMS / PKCS#7 container. There are two flavours: a synchronous remote signer callback, and a two-phase begin / complete flow for asynchronous or out-of-band signing.

Model A: remote signer callback

The binding assembles the signed attributes and calls back for the raw RSA PKCS#1 v1.5 signature over their SHA-256 digest. The certificate is passed as DER; the key stays inside your HSM. TPdfRemoteSign is a method pointer (of object, so it can carry state), so the callback is a method of one of your classes. Chain are intermediate certificates (DER), supplied independently of the key.

Pdf.SignWith(PdfBytes, CertDer, Callback: TPdfRemoteSign [; Chain: array of TBytes [; Options: TSigningOptions]]): TBytes
delphi
type
  TMySigner = class
    function SignHash(const DataToSign: TBytes): TBytes;   // call your HSM / KMS / token
  end;
…
var
  Signer: TMySigner;
  Opts: TSigningOptions;
  Signed: TBytes;
begin
  Signer := TMySigner.Create;
  try
    Opts := SigningOptions;        // zero-initialised record
    Opts.Reason := 'Approved';
    Opts.Pades  := True;           // PAdES-B-B
    Signed := Pdf.SignWith(PdfBytes, CertDer, Signer.SignHash, [IntermediateDer], Opts);
  finally
    Signer.Free;
  end;
end;
SignHash receives the bytes to sign and must return the raw RSA PKCS#1 v1.5 signature over their SHA-256 digest. The private key never reaches this library, so the same code path works for a cloud KMS, a smartcard or a PKI token.

Model B: two-phase begin / complete

For an asynchronous signer or a detached workflow, phase 1 prepares the PDF with a placeholder and returns the exact bytes the signature covers; you sign the digest out of band and build the CMS container; phase 2 embeds it. A TSigningSession exposes Document (the prepared PDF), Bytes (the bytes covered by the signature), Hash (their SHA-256, the value a remote signer signs) and Complete(Container), which embeds a finished DER CMS / PKCS#7 container and returns the final signed PDF. Free the session with .Free.

Pdf.BeginSigning(PdfBytes [; Options: TSigningOptions]): TSigningSession
Pdf.CompleteSignature(Document, Container: TBytes): TBytes
delphi
var
  Session: TSigningSession;
  Container, Signed: TBytes;
begin
  Session := Pdf.BeginSigning(PdfBytes);   // or BeginSigning(PdfBytes, Opts)
  try
    Container := BuildCmsContainer(Session.Hash);   // sign the digest remotely, assemble DER CMS
    Signed := Session.Complete(Container);          // = Pdf.CompleteSignature(Session.Document, Container)
  finally
    Session.Free;
  end;
end;

Inspect existing signature fields

Before signing, list the signature fields already present in the document. An empty result means the document is unsigned.

Pdf.ListSignatures(PdfBytes: TBytes): TSignatureFields
delphi
var F: TSignatureField;
…
for F in Pdf.ListSignatures(PdfBytes) do
  Writeln(F.Name, ' signed: ', F.Signed);

TSigningOptions

Both models accept an optional TSigningOptions record; build a zero-initialised one with the SigningOptions helper, then set the fields you need.

FieldMeaning
Reason, Location, NameSignature metadata (all UTF8String).
PadesTrue produces a PAdES-B-B signature (ETSI.CAdES.detached).
CertifyCertify the document (DocMDP); apply only on the first signature. See TCertify below.
ContainerSizeReserved /Contents bytes; 0 uses the default (8192). Raise it for large cloud-HSM CMS containers.
HasPolicy, PolicySet HasPolicy := True to attach a TSignaturePolicy identifier (PAdES-EPES).
Visible, VisiblePage, VisibleRect, VisibleText, VisibleImageSet Visible := True to draw a visible signature on page VisiblePage inside VisibleRect ([x0, y0, x1, y1] in points). VisibleText holds the lines (separated by #10). VisibleImage takes embedded PNG/JPEG bytes, drawn aspect-fit behind any text.

TCertify

ValueDocMDP level
CertifyNoneNot a certifying signature (default).
CertifyLocked/P 1: no changes permitted after signing.
CertifyForms/P 2: form-filling and signing permitted.
CertifyFormsAndAnnotations/P 3: form-filling, signing and annotations permitted.

TSignaturePolicy

A PAdES-EPES signature-policy identifier. OID is the policy OID (dotted-decimal); Hash is the policy document hash (TBytes); HashAlgorithmOID is the hash algorithm OID (empty means SHA-256); URI is an optional SPURI qualifier pointing to where the policy can be retrieved.

Timestamp & DSS (PAdES LTV) licensed

Build long-term-validation signatures offline. AddDss appends a Document Security Store (/DSS with certs/CRLs, PAdES-B-LT); Timestamp appends an RFC 3161 document timestamp (/DocTimeStamp, PAdES-B-LTA).

Pdf.AddDss(PdfBytes, Certs, Crls): TBytes
Pdf.Timestamp(PdfBytes, TsaKeyDer, TsaCertDer, Date = ''): TBytes
delphi
// B-LT: embed validation material (caller supplies DER certs/CRLs)
Lt := Pdf.AddDss(Signed, [CertDer], [CrlDer]);

// B-LTA: add a document timestamp signed by a TSA key/cert
Lta := Pdf.Timestamp(Lt, TsaKeyDer, TsaCertDer);

Network timestamp (PAdES AD-RT)

To stamp with a real network TSA instead of a local key, the binding exposes a transport-agnostic flow: you do the HTTP POST, the library handles the PDF and RFC 3161 plumbing. Phase 1 prepares the document; build a TSA request, POST it, extract the token, then embed it.

Pdf.BeginTimestamp(PdfBytes: TBytes): TTimestampSession
Pdf.TimestampRequest(const Imprint: TBytes [; Nonce: TBytes]; CertReq = True): TBytes
Pdf.TimestampTokenFromResponse(const Response: TBytes): TBytes
delphi
var
  Session: TTimestampSession;
  Imprint, Req, Resp, Token, Lta: TBytes;
begin
  Session := Pdf.BeginTimestamp(Signed);
  Imprint := Sha256(Session.Tbs);                  // hash the bytes the timestamp covers
  Req := Pdf.TimestampRequest(Imprint);            // RFC 3161 TimeStampReq (DER)
  Resp := HttpPost('https://freetsa.org/tsr', Req, 'application/timestamp-query');
  Token := Pdf.TimestampTokenFromResponse(Resp);   // extract the TimeStampToken
  Lta := Pdf.CompleteSignature(Session.Document, Token);
end;

Free public TSAs work: FreeTSA (https://freetsa.org/tsr) and DigiCert (http://timestamp.digicert.com). The integrator performs the HTTP POST; the binding builds the request and embeds the returned token.

Validate signatures licensed

Validate every signature in a PDF: each report recomputes the /ByteRange digest, parses the CMS, and checks that the cryptographic signature is valid, that the messageDigest matches the covered bytes, and whether the signature covers the whole document.

Object Pascal ships no bundled JSON parser, so the binding returns the report as the raw JSON array text: VerifySignaturesJson gives back the verbatim JSON string (a UTF8String) for you to parse with your preferred JSON unit. The string '[]' means the document is unsigned. Signature validation is a licensed feature: this call requires an active license (signatures) even when the document is unsigned. It is not available on the free tier.

Pdf.VerifySignaturesJson(PdfBytes: TBytes): UTF8String

Each array entry is an object with field_name, sub_filter, signer, covers_whole_document, digest_valid, signature_valid, is_valid and byte_range. Each report now also carries the signer-certificate detail: issuer, serial_number, valid_from, valid_to, algorithm (e.g. SHA256withRSA), signing_time, cert_count and has_timestamp. field_name and signer may be empty, and any of the certificate fields may be null, when absent.

delphi
var Json: UTF8String;
…
Data := ...;   // TBytes of contract.signed.pdf
Json := Pdf.VerifySignaturesJson(Data);
if Json = '[]' then
  Writeln('document is unsigned')
else
  Writeln(string(Json));   // hand the JSON to your parser of choice

Text & image extraction

Extract a document's text, mapping shown glyph codes back to Unicode through each font's ToUnicode map, with space/line inference. ExtractPageText does the same for a single page (0-based), raising ERustPdf (psInvalidArgument) if the page is out of range. Raster images can be pulled out too: JPEGs are written verbatim as .jpg, everything else as .png. The output directory is created automatically if it does not already exist. ExtractImagesToDir returns how many images were written.

Pdf.ExtractText(PdfBytes: TBytes): string free
Pdf.ExtractPageText(PdfBytes: TBytes; PageIndex: NativeUInt): string free
Pdf.ExtractImagesToDir(PdfBytes: TBytes; Dir: string): NativeUInt free
delphi
var N: NativeUInt;
…
Data := ...;   // TBytes of report.pdf
Writeln(Pdf.ExtractText(Data));
Writeln(Pdf.ExtractPageText(Data, 0));   // just the first page

N := Pdf.ExtractImagesToDir(Data, 'out_images/');   // count of images written
Writeln(Format('wrote %d image(s)', [Int64(N)]));

Find every occurrence of a query string and get back each match with its bounding box, so you can highlight, redact or link the located text. FindText returns a TTextHits array of TTextHit records; the search is case-insensitive unless you pass CaseSensitive := True. See the PDF text search concept page for background.

Pdf.FindText(const PdfBytes: TBytes; const Query: string; CaseSensitive: Boolean = False): TTextHits

Each TTextHit carries Page (0-based), Text, and X, Y, Width, Height in PDF points with the box origin at the lower-left corner.

delphi
var
  Hits: TTextHits;
  Hit: TTextHit;
begin
  Data := ...;   // TBytes of report.pdf
  Hits := Pdf.FindText(Data, 'Invoice');
  for Hit in Hits do
    Writeln(Format('page %d  "%s"  [%.1f %.1f %.1f %.1f]',
      [Hit.Page, Hit.Text, Hit.X, Hit.Y, Hit.Width, Hit.Height]));
end;

Page geometry free

Read each page's size, rotation and boxes without modifying the file. Width/Height ignore /Rotate; RotatedWidth/RotatedHeight are the dimensions a viewer actually shows. Sizes are in PDF points (72 per inch).

class function MeasurePages(const Pdf: TBytes): TArray<TPageGeometry>; static;
class function MeasurePage(const Pdf: TBytes; Index: Integer): TPageGeometry; static;

Each TPageGeometry has Page (0-based), Width, Height, Rotation (0/90/180/270), RotatedWidth, RotatedHeight, and the MediaBox/CropBox as a TPdfRect (X0, Y0, X1, Y1 with Width/Height methods).

delphi
var Data: TBytes;
    G: TPageGeometry;
begin
  Data := TFile.ReadAllBytes('report.pdf');
  G := Pdf.MeasurePage(Data, 0);
  WriteLn(Format('page %d: %.1f x %.1f pt, rotate %d', [G.Page, G.Width, G.Height, G.Rotation]));
  WriteLn(Format('visible: %.1f x %.1f pt', [G.RotatedWidth, G.RotatedHeight]));
end;

Document inspection free

Get a non-mutating overview of a PDF, its version, PDF/A level (if any) and encryption posture, without decrypting or rewriting it. Handy for triage before deciding how to process a file.

class function Inspect(const Pdf: TBytes): TPdfOverview; static;

TPdfOverview carries Version (e.g. '1.7'), PdfaLevel (e.g. '2b', or empty if not PDF/A), Encrypted, Encryption ('None', 'RC4', 'AES-128' or 'AES-256'), RequiresPassword, and PageCount.

delphi
var O: TPdfOverview;
begin
  O := Pdf.Inspect(TFile.ReadAllBytes('report.pdf'));
  WriteLn(Format('PDF %s, %d page(s)', [O.Version, O.PageCount]));
  WriteLn(Format('PDF/A: %s, encryption: %s', [O.PdfaLevel, O.Encryption]));
  if O.RequiresPassword then WriteLn('needs a password to open');
end;

Render a page to an image licensed

Rasterize a page to a PNG image. A native Rust renderer (built on tiny-skia, with no headless browser) interprets the page content stream, painting real glyph outlines, vector graphics, images, color and transparency. Page rendering is a Pro feature; page_count is free.

Pdf.RenderPageToPng(const PdfBytes: TBytes; PageIndex: NativeUInt; Dpi: Double): TBytes licensed
Pdf.PageCount(const PdfBytes: TBytes): NativeUInt free
delphi
Data := ReadBytes('report.pdf');
WriteLn(Pdf.PageCount(Data), ' page(s)');
Png := Pdf.RenderPageToPng(Data, 0, 150.0);
WriteBytes('page1.png', Png);

Enums

TPdfaLevel

ValueLevel
palA1BPDF/A-1b (basic, PDF 1.4)
palA2BPDF/A-2b (basic): default of Pdfa
palA2APDF/A-2a (accessible: pair with Tagged)
palA3BPDF/A-3b (basic, allows attachments)
palA3APDF/A-3a (accessible + attachments)
palA4PDF/A-4 (ISO 19005-4, based on PDF 2.0)
palA4EPDF/A-4e (engineering)
palA4FPDF/A-4f (requires at least one embedded file)

TPdfAlign

ValueMeaning
paLeftLeft-aligned (default)
paRightRight-aligned
paCenterCentered
paJustifyJustified (space distributed between words)

TVerticalAnchor

ValueWhat Y means (PlaceText / PlaceParagraph)
vaBaselineThe text baseline (PlaceText default)
vaTopThe top: baseline lands ascent × Size below Y (legacy fixed-position layout; PlaceParagraph default)
vaBottomThe bottom: descender line rests on Y (PlaceParagraph: bottom-pinned block)
vaLineTopTop of the layout line box (ascent + descent)
vaLineBottomBottom of the layout line box (PlaceParagraph: bottom-pinned)

TVerticalAlign

ValueLine position inside the MaskedText box
vlTopHangs from the top edge (top line-alignment in rectangle-based text APIs)
vlMiddleCap-height centered (default)
vlBottomDescender line rests on the bottom edge

TStampSpace

ValueCoordinate space of the stamping primitives
ssVisibleThe page's displayed space, compensating /Rotate (default)
ssMediaRaw PDF user space, no /Rotate composition (legacy layout semantics)

TImageAnchor

ValueRotation anchor of DrawImage
iaCornerThe image's own lower-left corner at (X, Y) (default)
iaBoundingBoxThe rotated image's bounding box lands its lower-left at (X, Y) (bounding-box layout)

TAFRelationship

ValueMeaning
afSourceSource data for the document (e.g. the invoice XML)
afDataData used to derive the visual content
afAlternativeAlternative representation
afSupplementSupplementary material
afUnspecifiedUnspecified relationship

TEncryption

ValueCipher
encRC4_128RC4-128 (legacy)
encAES128AES-128
encAES256AES-256 (V5/R6): recommended

TFacturxProfile

ValueConformance level
fxMinimumMinimal header data only
fxBasicWLBasic, without line items
fxBasicBasic, with line items
fxEN16931EN 16931 (Comfort): the interoperable core, default
fxExtendedEN 16931 plus extensions

Error handling

Every failing native call raises ERustPdf (a Exception) carrying the Status (a TPdfStatus) and the library's last-error message. License failures (missing/expired/forged token, or a feature the token doesn't grant) surface here too. Token activation failures carry the dedicated license status code (12); a gated build, sign or encrypt call instead reports that operation’s own status (for example Serialize = 4 or Sign = 10) with the same “requires a valid license” message.

delphi
try
  Doc.Pdfa.SetInfo('x', '', '', '', '');
  Doc.AddPage;
  Doc.SaveToFile('out.pdf');
except
  on E: ERustPdf do
    Writeln('failed: ', E.Message, ' (status ', Ord(E.Status), ')');
end;

Utilities

FunctionDescription
Pdf.Version: stringNative library version string.
Pdf.ActivateLicense(Token)Activate a license token (raises on invalid/expired).
Looking for another language? The same API exists in Python, Swift, C#, Go, PHP, Ruby and Node: browse all docs. They share one core, so behavior is identical.