Skip to content

PDF

A PDF file is made by printing to a PDF printer driver, such as Microsoft Print to PDF, which comes with Windows, or, with the Skia render context, written by Skia directly, without a printer driver. This page covers what that gives you: vector output, selectable text, and embedded fonts, and where the limits are.

Printing to a PDF printer

Select the PDF printer, for example with a TPrintDialog or by setting Printer.PrinterIndex, and print as shown on the Printing page. Fitting to the page and printing at real size work the same way.

Writing the PDF without a dialog

Left to itself, the PDF printer driver asks the user for a file name. To write the PDF without asking, for example in a batch job, pass the file name as the last parameter of CreatePrintJob:

PrintJob := TSVGRenderContextManager.CreatePrintJob('Drawings',
  bqHighQuality, [], 'C:\Output\drawings.pdf');

The driver then writes to that file instead. The file is complete once the print job is released, when PrintJob goes out of scope or you set it to nil. Read it only after that.

The output file is whatever the driver produces, so other printer drivers write their own format to it. Only Microsoft Print to PDF has been tested.

On FPC under Windows, CreatePrintJob also takes the printer's DevMode, after the job name:

PrintJob := TSVGRenderContextManager.CreatePrintJob('Drawings',
  TPrinterDevice(Printer.Printers.Objects[Printer.PrinterIndex]).DevModeW,
  bqHighQuality, [], 'C:\Output\drawings.pdf');

What works today

Writing to a file works with the Direct2D, GDI+ and Skia render contexts, so on Windows. On macOS (Quartz) and with the FMX canvas render context, CreatePrintJob raises an exception when you pass a file name.

PDF through Skia

With the Skia render context and font system (SVGSkia and SVGFontSkia, see Choosing a backend), the PDF is written by Skia itself. No printer driver is involved and none has to be installed, which suits servers and batch jobs, and it works the same on VCL and FMX.

The print job is created as above; the file name is required, because there is no printer to send the job to:

PrintJob := TSVGRenderContextManager.CreatePrintJob('Drawings',
  bqHighQuality, [tfoStringsWithPlacedCharacters], 'C:\Output\drawings.pdf');
RC := PrintJob.BeginPage(793.7, 1122.5); // A4 in 1/96 inch

There is no printer to take a page size from, so BeginPage takes the page size you want, in 1/96 inch like the page's render context: 210 mm is 210 / 25.4 × 96 = 793.7. Every page can have its own size.

To write to a stream instead of a file, for example to send the PDF from a web server, create the print job yourself, from unit BVE.SVG2ContextSkia:

PrintJob := TSVGPrintJobSkia.Create(Stream, False, 'Drawings',
  bqHighQuality, [tfoStringsWithPlacedCharacters]);

The stream is complete once the print job is released. Pass True as the second parameter to have the print job free the stream.

What differs from a printer driver:

  • Filters, masks, clip paths and patterns become bitmaps at 300 dpi, the last parameter of TSVGPrintJobSkia.Create.
  • Text inside a clip path, a mask or a group with opacity is part of such a bitmap, so it cannot be selected, even with a text formatting option.
  • Real text is drawn with the glyphs as Skia shaped them, so kerning and ligatures are as on screen.

What stays vector

Shapes and paths go into the PDF as vector graphics and stay sharp at any zoom. Filters and masks become bitmaps, as described in What stays vector.

Selectable text

By default, text is drawn as outlines. It looks exactly as in the drawing, but the PDF contains no text to select or search. To write real text, pass a text formatting option when you create the print job:

PrintJob := TSVGRenderContextManager.CreatePrintJob('Drawings',
  bqHighQuality, [tfoStringsWithPlacedCharacters]);
Option Effect
[] All text as outlines. The default.
[tfoStrings] Real text for each piece of text that starts at a position of its own.
[tfoStringsWithPlacedCharacters] As tfoStrings, and also for text in which every character is positioned separately.

Real text is only written for simple text: glyphs that are not rotated, text written left to right, no unicode-bidi other than normal, and not text on a path. Other text still becomes outlines, so the drawing always looks right; only its selectability varies. Outlined (stroked) glyphs are not supported as real text.

Every character is placed where the drawing puts it, on Direct2D and GDI+: text with an x and y for each character, and text spaced with dx, dy, letter-spacing or textLength. In a PDF the text is the drawing, so this matters for how it looks, not only for selecting it.

Large type has a limit that comes from the printer driver. Microsoft Print to PDF keeps text up to about 25 mm (70 pt) as text, and rotated text up to about 16 mm; above that it writes outlines, so a large heading prints correctly but cannot be selected. On GDI+ this holds for text in a solid, opaque colour and an installed font. Text with a gradient or transparency, or in a font that is not installed, is written as outlines from about 12 mm (32 pt). Direct2D writes text as text at every size the test measured, up to 80 mm.

Fonts that are not installed

A drawing may use a font that is not installed on the computer, loaded from the drawing itself or from your application (see Fonts that are not installed). To get selectable text in such a font, the font has to be embedded in the PDF. This works with the default VCL setup, the Direct2D (Direct3D 11) render context with DirectWrite. GDI+ cannot pass such fonts on to the printer.

It also works with the Skia print job, which embeds the fonts itself, from TrueType, WOFF and WOFF2 files alike. Text in such a font looks right, but Edge and Chrome can run words together when it is copied or searched: Skia writes it in short pieces, and their PDF engine loses some of the spaces between them. The spaces are in the file; pypdf, for one, finds them all.

With a PDF printer driver, two more conditions come from the font and the driver:

  • Microsoft Print to PDF embeds TrueType (.ttf) fonts only.
  • The font's licence flags must allow embedding.

Where these are not met, choose outlines, the default, and the text still looks as drawn.