Creating a PDF document
Every document made with TTMSFNCPDFLib follows the same skeleton: begin the
document, add at least one page, draw, end the document. Everything else on this
page — page size, metadata, headers, security — is configuration you apply around
that skeleton, and almost all of it must be set before the page it should
affect is created.
This chapter covers the document itself. Drawing on the page is covered in Drawing graphics and Text and fonts.
Starting a new document
Call BeginDocument to start a document, always paired with EndDocument. When
AFileName is empty, EndDocument returns the content as a TMemoryStream
instead of writing to disk — which is what you want when the PDF is going to a
database blob, an HTTP response, or an email attachment. Pass True as the second
parameter to EndDocument to open the generated file in the default PDF viewer.
procedure TForm1.GeneratePDF(AFileName: string);
var
p: TTMSFNCPDFLib;
begin
p := TTMSFNCPDFLib.Create;
try
p.BeginDocument(AFileName);
p.NewPage;
p.EndDocument;
finally
p.Free;
end;
end;
Add the appropriate unit to your uses clause:
uses
FMX.TMSFNCPDFLib; // FMX
VCL.TMSFNCPDFLib; // VCL
WEBLib.TMSFNCPDFLib; // WEB Core
Key class: TTMSFNCPDFLib
Adding pages
Call NewPage to start a new page. Each NewPage call clears the content buffer
but preserves appearance settings — fill, stroke, font — from the previous
page, so a document-wide look is set once rather than re-applied per page.
EndDocument raises an error if no page was added.
| Call | Purpose |
|---|---|
NewPage |
Appends a page and makes it current |
InsertPage(APageIndex) |
Inserts a page at a specific position |
GetPageCount |
Total number of pages |
GetPageIndex |
Zero-based index of the current page |
Page size and orientation
| Property | Description |
|---|---|
PageSize |
Standard size (A4, Letter, Legal, …) or psCustom |
PageOrientation |
poPortrait or poLandscape |
PageWidth / PageHeight |
Custom size when PageSize = psCustom |
Supported sizes include ISO A0–A8, B0–B10, C2–C6, SRA and RA series, US Letter, Legal, Ledger, Tabloid, Executive, ANSI C–E, and traditional British sizes.
Tip
psCustom is not only for exotic paper. Fitting the page to the content is the
simplest way to produce a PDF with no trailing empty space — a receipt, a label,
or a one-block report. The example on the chapter index uses
a 595 × 440 custom page for exactly that reason.
Metadata
Set Author, Creator, Title, Subject, and Keywords directly on the
TTMSFNCPDFLib instance before EndDocument. These populate the document
properties a PDF reader shows, and Title is what most readers put in the window
caption instead of the file name.
Headers, footers, and page numbering
A header or footer is drawn automatically on every page, so it is configured once on the document rather than redrawn per page.
| Property | Description |
|---|---|
Header / Footer |
Content text (supports HTML) |
HeaderSize / FooterSize |
Height of the area |
HeaderMargins / FooterMargins |
Margins around the area |
HeaderAlignment / FooterAlignment |
Horizontal text alignment |
HeaderFont / FooterFont |
Font for the text |
Use GetHeaderRect and GetFooterRect to retrieve the bounding rectangles — for
example to keep body content clear of them.
Page numbering is separate, because it needs to be resolved after the total page count is known:
| Property | Description |
|---|---|
PageNumber |
pnNone (default), pnHeader, or pnFooter |
PageNumberFormat |
Format string |
PageNumberSize / PageNumberMargins / PageNumberAlignment / PageNumberFont |
Layout and appearance |
GetPageNumberRect retrieves the page number bounding rectangle.
Note
Header and Footer default to a non-empty value. If a generated page shows an
unexpected caption at the top, set Header := '' and Footer := '' explicitly
before BeginDocument.
Archival format, security, and page boxes
Set PDFStandard to pdfNone (default) or pdfA1 for PDF/A-1 archival format.
For protection, set OwnerPassword and UserPassword. AllowsPrinting and
AllowsCopying (both True by default) control what a reader may do with the
document once opened.
The five page boxes describe the physical sheet and the regions within it — set them when the PDF goes to a commercial printer:
| Property | Description |
|---|---|
MediaBox |
Physical page size |
CropBox |
Region displayed or printed |
BleedBox |
Bleed area |
TrimBox |
Final trimmed dimensions |
ArtBox |
Extent of meaningful content |
Generation callbacks
The document raises events around the parts it draws for you, so you can replace
the default rendering rather than switch it off and rebuild it by hand. Set
ADefaultDraw := False in a Before… handler to suppress the built-in output and
draw your own.
| Event | Fires |
|---|---|
OnBeforeDrawHeader / OnAfterDrawHeader |
Around the header |
OnBeforeDrawFooter / OnAfterDrawFooter |
Around the footer |
OnBeforeDrawPageNumber / OnAfterDrawPageNumber |
Around the page number |
OnNewPageStarted |
After each NewPage; provides APageIndex |
procedure TForm1.PDFBeforeDrawHeader(Sender: TObject; APageIndex: Integer;
AHeader: UnicodeString; ARect: TRectF;
AGraphics: ITMSFNCCustomPDFGraphicsLib; var ADefaultDraw: Boolean);
begin
ADefaultDraw := False;
AGraphics.Font.Size := 9;
AGraphics.DrawText('Page ' + IntToStr(APageIndex + 1), ARect.TopLeft);
end;
Working in millimetres or inches
PDF coordinates are points (1/72 inch) with the origin at the top-left. When your
measurements come from a print spec instead, convert rather than hand-computing.
All functions accept an optional ADPI parameter (default 72.0):
| Function | Description |
|---|---|
InchToPixel(AInch, ADPI) |
Inches → PDF points |
MillimeterToPixel(AMillimeter, ADPI) |
Millimetres → PDF points |
MillimeterRect(AX, AY, AWidth, AHeight, ADPI) |
TRectF from mm coordinates |
InchRect(AX, AY, AWidth, AHeight, ADPI) |
TRectF from inch coordinates |
p.Graphics.DrawRectangle(MillimeterRect(10, 10, 100, 150));
Common mistakes
- Ending a document with no page.
EndDocumentraises an error ifNewPagewas never called. - Setting page size after the page exists.
PageSize,PageWidthandPageHeightapply to pages created afterwards — set them beforeBeginDocumentor before theNewPagethey should affect. - Expecting appearance to reset per page. It does not: fill, stroke and font carry over from the previous page by design.
- Forgetting the default header. See the note above — an unexplained caption
at the top of the page is almost always the default
Header.
Putting it together
The report on the chapter index is this chapter's skeleton
with content on top: a custom page fitted to its table, both default captions
cleared, and everything drawn between one BeginDocument/EndDocument pair. It
is the source of the screenshot there, so the code and the picture cannot drift
apart.
procedure TForm1.BuildReport(const AFileName: string);
const
Rows: array[0..7, 0..2] of string = (
('Northern Europe', 'Alice Moreau', '184.000'),
('Southern Europe', 'Bob Devlin', '72.500'),
('North America', 'Carol Yang', '412.800'),
('South America', 'Dan Pereira', '43.000'),
('Middle East', 'Eve Novak', '159.000'),
('South Asia', 'Frank Bauer', '91.000'),
('East Asia', 'Grace Lim', '213.000'),
('Oceania', 'Hugo Marsh', '66.500'));
var
p: TTMSFNCPDFLib;
i: Integer;
y: Single;
Euro: string;
begin
{ Build the euro sign from its code point so the source stays ASCII-safe. }
Euro := Char($20AC);
p := TTMSFNCPDFLib.Create;
try
{ Header/Footer default to a non-empty value - clear them before drawing
your own, or the built-in caption appears at the top of every page. }
p.Header := '';
p.Footer := '';
{ A custom page fitted to the content, so the PDF has no trailing empty
space below the table. }
p.PageSize := psCustom;
p.PageWidth := 595;
p.PageHeight := 440;
p.BeginDocument(AFileName);
try
p.NewPage;
{ Header band: a filled rectangle with no stroke, drawn before the text
that sits on top of it. }
p.Graphics.Fill.Color := gcSteelblue;
p.Graphics.Fill.Kind := gfkSolid;
p.Graphics.Stroke.Kind := gskNone;
p.Graphics.DrawRectangle(RectF(40, 40, 555, 92));
p.Graphics.Font.Color := gcWhite;
p.Graphics.Font.Name := 'Segoe UI';
p.Graphics.Font.Size := 18;
p.Graphics.DrawText('Regional sales report', PointF(54, 55));
p.Graphics.Font.Size := 10;
p.Graphics.DrawText('Fiscal year 2026 - all regions', PointF(54, 76));
{ Mini HTML for a paragraph that mixes weight and colour inline. }
p.Graphics.Font.Color := gcBlack;
p.Graphics.Font.Size := 10;
p.Graphics.DrawHTMLText(
'Revenue by region for the full year. Figures in <b>EUR</b>, ' +
'rounded to the nearest thousand. Regions above ' +
'<font color="#1F7A1F"><b>150.000</b></font> are on track.',
RectF(40, 108, 555, 148));
{ Table header. Alignment is canvas state, so it is set for the currency
column and restored immediately afterwards. }
y := 158;
p.Graphics.Fill.Color := gcGainsboro;
p.Graphics.Stroke.Kind := gskSolid;
p.Graphics.Stroke.Color := gcSilver;
p.Graphics.DrawRectangle(RectF(40, y, 555, y + 24));
p.Graphics.Font.Size := 10;
p.Graphics.DrawText('Region', PointF(50, y + 6));
p.Graphics.DrawText('Account manager', PointF(220, y + 6));
p.Graphics.Alignment := gtaTrailing;
p.Graphics.DrawText('Revenue', RectF(390, y + 6, 545, y + 22));
p.Graphics.Alignment := gtaLeading;
{ Rows, with alternating band fills. }
y := y + 24;
for i := 0 to High(Rows) do
begin
if i mod 2 = 1 then
p.Graphics.Fill.Color := gcWhitesmoke
else
p.Graphics.Fill.Color := gcWhite;
p.Graphics.DrawRectangle(RectF(40, y, 555, y + 22));
p.Graphics.DrawText(Rows[i][0], PointF(50, y + 5));
p.Graphics.DrawText(Rows[i][1], PointF(220, y + 5));
p.Graphics.Alignment := gtaTrailing;
p.Graphics.DrawText(Euro + ' ' + Rows[i][2],
RectF(390, y + 5, 545, y + 21));
p.Graphics.Alignment := gtaLeading;
y := y + 22;
end;
{ Totals row. }
p.Graphics.Fill.Color := gcLightsteelblue;
p.Graphics.DrawRectangle(RectF(40, y, 555, y + 24));
p.Graphics.DrawText('Total', PointF(50, y + 6));
p.Graphics.Alignment := gtaTrailing;
p.Graphics.DrawText(Euro + ' 1.241.800', RectF(390, y + 6, 555, y + 22));
p.Graphics.Alignment := gtaLeading;
{ Footer rule and caption, clear of the table. }
p.Graphics.Stroke.Color := gcSilver;
p.Graphics.DrawLine(PointF(40, 398), PointF(555, 398));
p.Graphics.Font.Size := 8;
p.Graphics.Font.Color := gcGray;
p.Graphics.DrawText('Generated with TMS FNC PDF Library', PointF(40, 406));
p.Graphics.Alignment := gtaTrailing;
p.Graphics.DrawText('Page 1 of 1', RectF(390, 406, 555, 420));
p.Graphics.Alignment := gtaLeading;
finally
p.EndDocument;
end;
finally
p.Free;
end;
end;