Generating PDF in TMS WEB Core
The PDF Library is shareable between VCL, FMX, and WEB Core, and the generation code is the same on all three. Two things differ in the browser, and both are about resources the runtime cannot simply read from disk: fonts must be fetched before they can be embedded, and compression needs a JavaScript library that is not bundled by default.
Get those two right and the rest of this guide applies unchanged.
Font caching
In VCL and FMX, TTMSFNCPDFLib reads font files from the system font directory
when it embeds a subset. A browser has no such directory, so fonts have to be
fetched over HTTP first and cached before generation starts.
Pre-load each font you intend to use:
AddFontToCache('http://www.myserver.com/fonts/arial.ttf');
AddFontToCache('http://www.myserver.com/fonts/tahoma.ttf');
AddFontToCache starts an asynchronous download. Generating a document before it
completes produces a PDF with the font missing, so wait for the cache rather than
generating immediately: handle Application.OnFontCacheReady and start the export
from there.
Important
This is the most common WEB Core issue. A document that renders correctly in a
desktop build and loses its fonts in the browser almost always means generation
ran before OnFontCacheReady fired — not that the font is wrong.
Serve the font files from the same origin as the application where you can; a cross-origin font request needs the usual CORS headers on the font server, and a blocked request fails the same way a missing file does.
Compression
WEB Core requires the Pako ZLib library for PDF compression. Enable it via Manage JavaScript Libraries → Pako ZLib Compression (PDF) in the project context menu.

Without it, generation still works but the output is uncompressed — noticeably larger files, which matters most when the PDF is downloaded over a slow connection or stored per user.
What stays the same
Everything else in this guide is portable as written:
| Area | WEB Core |
|---|---|
| Document skeleton | Identical — BeginDocument / NewPage / EndDocument |
| Drawing and text | Identical API |
| Form fields | Not available (Windows and Linux only) |
| Output | Use the stream overload and hand the bytes to a browser download |
Because EndDocument returns a TMemoryStream when no file name is given, the
browser case is the stream case — there is no local path to write to. See
Creating a PDF document.
Putting it together
Both browser-specific concerns in one flow: cache the fonts, wait for the cache, generate to a stream, and hand the bytes to the browser. Only the first and last steps differ from the desktop code.
procedure TForm1.WebFormCreate(Sender: TObject);
begin
{ A browser has no system font directory, so every font the document embeds
has to be fetched first. AddFontToCache starts an asynchronous download. }
AddFontToCache('http://www.myserver.com/fonts/arial.ttf');
AddFontToCache('http://www.myserver.com/fonts/tahoma.ttf');
{ Generating before the downloads finish produces a PDF with the font
missing, so the export is started from the ready callback - never directly
after the AddFontToCache calls above. }
Application.OnFontCacheReady := HandleFontCacheReady;
end;
procedure TForm1.HandleFontCacheReady(Sender: TObject);
var
p: TTMSFNCPDFLib;
s: TMemoryStream;
begin
p := TTMSFNCPDFLib.Create;
try
p.Header := '';
p.Footer := '';
{ No file name: there is no local path to write to in the browser, so
EndDocument hands the bytes back as a stream instead. }
p.BeginDocument('');
try
p.NewPage;
p.Graphics.Font.Name := 'Arial';
p.Graphics.Font.Size := 16;
p.Graphics.DrawText('Generated in the browser', PointF(40, 40));
finally
s := p.EndDocument;
end;
{ Hand the stream to whatever delivers it to the user - a download link,
an upload to your server, or an attachment. }
DownloadStream(s, 'report.pdf');
finally
p.Free;
end;
end;
Common mistakes
- Generating before the font cache is ready. Wait for
Application.OnFontCacheReady. - Assuming system fonts exist. They do not; every embedded font must be cached first.
- Cross-origin font requests without CORS headers. The download fails and the font is missing from the output.
- Expecting a file path. Use the stream overload and trigger a download.
- Missing Pako ZLib. Output is valid but uncompressed.