Table of Contents

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. EndDocument raises an error if NewPage was never called.
  • Setting page size after the page exists. PageSize, PageWidth and PageHeight apply to pages created afterwards — set them before BeginDocument or before the NewPage they 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;

See also