Skip to content

Printing

A drawing is printed through a print job: you start a page, render onto it like onto any other surface, and end the page. This page shows how to fit a drawing to the page, how to print several drawings, and how to print a drawing at its real size, so that 100 mm on screen is 100 mm on paper.

Print jobs are available on the Direct2D and GDI+ render contexts, which includes the VCL default. On the Skia render context a print job writes a PDF file instead of printing; see PDF through Skia.

A print job

TSVGRenderContextManager.CreatePrintJob starts a job on the printer that is currently selected, for example with a TPrintDialog. Each call to BeginPage starts a page and returns a render context for it; EndPage finishes the page. The job is sent to the printer when the last reference to it is released.

The page and its render context work in 1/96 inch, the unit of SVG pixels, whatever the printer's resolution, on both Direct2D and GDI+. A drawing laid out at 96 to the inch therefore prints at its real size, and a position of 96 on the page is one inch from its origin. Printer.PageWidth and Printer.PageHeight are in printer dots, so convert them first:

function PageWidth96: TSVGFloat;
begin
  Result := Printer.PageWidth * 96 / GetDeviceCaps(Printer.Handle, LOGPIXELSX);
end;

function PageHeight96: TSVGFloat;
begin
  Result := Printer.PageHeight * 96 / GetDeviceCaps(Printer.Handle, LOGPIXELSY);
end;

Changed in update 25

Until update 24, a page on the GDI+ render context worked in printer dots, while Direct2D already worked in 1/96 inch. Code written for GDI+ that passes printer dots now prints too large; pass 1/96 inch instead.

What works today

The other print jobs have units of their own, taken from the PrintPreview example and not measured: on macOS (Quartz) a page works in points, 1/72 inch, and with the FMX canvas render context in printer dots.

Fitting drawings to the page

This prints a list of drawings, each on its own page and scaled to fill the printable area:

uses
  Winapi.Windows,
  Vcl.Printers,
  BVE.SVG2Types,
  BVE.SVG2Intf,
  BVE.SVG2SaxParser,
  BVE.SVG2Elements.VCL;

function LoadDrawing(const aFileName: string): ISVGRoot;
var
  Parser: TSVGSaxParser;
begin
  Result := TSVGRootVcl.Create;
  Parser := TSVGSaxParser.Create(nil);
  try
    Parser.Parse(aFileName, Result);
  finally
    Parser.Free;
  end;
end;

procedure PrintFitted(const aFileNames: TStrings);
var
  i: Integer;
  PrintJob: ISVGPrintJob;
  Context: ISVGRenderContext;
begin
  PrintJob := TSVGRenderContextManager.CreatePrintJob('Drawings');

  for i := 0 to aFileNames.Count - 1 do
  begin
    Context := PrintJob.BeginPage(PageWidth96, PageHeight96);
    try
      Context.BeginScene;
      try
        SVGRenderToRenderContext(LoadDrawing(aFileNames[i]), Context,
          PageWidth96, PageHeight96, [sroFilters, sroClippath], True);
      finally
        Context.EndScene;
      end;
    finally
      PrintJob.EndPage;
    end;
  end;
end;

The last parameter, True, scales the drawing into the area given by the width and height before it, keeping its aspect ratio and centring it.

Printing at real size

Drawings made to scale, such as floor plans, die-lines and labels, have to come out at their real size. SVGRenderAtRealSize renders a drawing at its own size, with its top left corner at a position on the page, in 1/96 inch:

procedure PrintRealSize(const aFileName: string);
const
  Mm = 96 / 25.4; // 1/96 inch per millimetre
var
  PrintJob: ISVGPrintJob;
  Context: ISVGRenderContext;
begin
  PrintJob := TSVGRenderContextManager.CreatePrintJob(ExtractFileName(aFileName));
  Context := PrintJob.BeginPage(PageWidth96, PageHeight96);
  try
    Context.BeginScene;
    try
      // 20 mm from the top and the left of the page
      SVGRenderAtRealSize(LoadDrawing(aFileName), Context, 20 * Mm, 20 * Mm);
    finally
      Context.EndScene;
    end;
  finally
    PrintJob.EndPage;
  end;
end;

A drawing with width="100mm" prints 100 mm wide, on Direct2D and on GDI+. The same size is used for bitmaps at a print resolution, where a 50 mm shape renders to 590 pixels at 300 dpi.

Two things to keep in mind:

  • The drawing needs a real size. Its width and height must be in absolute units such as mm, cm or in. A drawing without them, or with percentages, has no real size; SVGRenderAtRealSize then lays it out in 300 by 150, or in the area you pass to its overload.
  • The page may start at the printable area, not the paper edge. Most printers cannot print in a margin of a few millimetres. On GDI+, position (0, 0) is the top-left corner of the area the printer can print on, and to place a drawing at an exact distance from the paper edge you subtract GetDeviceCaps(Printer.Handle, PHYSICALOFFSETX) and PHYSICALOFFSETY, converted to 1/96 inch. Whether Direct2D does the same has not been checked on such a printer yet.

Drawings larger than the page

A drawing at real size that does not fit on the page is cut off at the page edge. To print it as large as possible while keeping its proportions, use the smaller of the two scale factors, which is what fitting to the page does. To print it across several pages, render it once per page with the page's part of the drawing moved into view; the page render context accepts a transformation matrix through MultiplyMatrix for this.

PDF files

Printing to a PDF printer driver, such as Microsoft Print to PDF, produces a PDF file. See PDF for what that gives you.