Rendering SVG to a command list or printer

From version 2.40 update 8 onwards, the SVG control package supports rendering to a command list or printer in case of render context GDI+ as well as render context Direct2D.

In this post I will show a couple of examples how this can be used.

Rendering to a command list

A command file is basically a recording of graphical commands that, if played back, result in an image. It is in that respect similar to an SVG file, only it is bound to a specific graphical platform.

The EMF file for the Windows GDI+ platform, also refered to as a metafile, is an example of a command list. But Direct2D also supports a commandlist through the ID2D1CommandList interface (defined in D2D1_1.h).

(Note that the ID2D1CommandList is supported by the Direct2D 3D11 render context not the Direct2D WIC render context.)

The nice thing about a command list, is that it can be used as an (vector) image, so just like an SVG, it can be scaled without quality loss.

In the SVG control package we use interfaces to expose functionality that is independent of the underlying graphical platform. For the command list, the following interface is defined:

var
  CmdList: ISVGRenderContextCmdList;
  ...
CmdList := TSVGRenderContextManager.CreateCmdList;

To record commands in the command list in the “SVG control package”, we need to create a rendercontext based on the commandlist.

var
  Context: ISVGRenderContext;
  ...
Context := TSVGRenderContextManager.CreateRenderContextCmdList(CmdList, 50, 50);

In this example the “width” and “height” of the context is 50 and 50. We can now use the drawing commands on the rendercontext and these will in turn be recorded in the command list. In the example below all the commands needed to render an SVG image are recorded into the command list:

var
  Root: ISVGRoot;
  CmdList: ISVGRenderContextCmdList;
  Bitmap: TBitmap;
  Context: ISVGRenderContext;
  i, j: Integer;
...
  // Render an SVG to a command list

  // Create a root object
  Root := TSVGRootVCL.Create;

  // Load an SVG image in the root object
  if Root.DocFindOrLoad(TSVGIri.Create(isFile, aFilename, '')) = nil then
    raise Exception.CreateFmt('Loading %s failed', [aFilename]);

  // Create a command list to record graphical commands
  CmdList := TSVGRenderContextManager.CreateCmdList;

  // Create a rendercontext for the command list of size 50, 50
  Context := TSVGRenderContextManager.CreateRenderContextCmdList(CmdList, 50, 50);

  Context.BeginScene;
  try
    // Use the build-in function "SVGRenderToRenderContext" to render 
    // the SVG contained in the root object to the command list 
    // via the rendercontext
	
    SVGRenderToRenderContext(Root, Context, 50, 50);
  finally
    Context.EndScene;
  end;

Now the “CmdList” object contains all the graphical commands necessary to render the SVG image on a specific platform, which can be GDI+ or Direct2D 3D11, depending on the global render context settings.

One use for a command list is to render the image as a pattern. The example below renders the SVG recorded in the commandlist as a pattern on a bitmap.

  // Create a bitmap
  FBitmap := TSVGRenderContextManager.CreateCompatibleBitmap(350, 200, True);

  // Create a rendercontext for the bitmap
  Context := TSVGRenderContextManager.CreateRenderContextBitmap(FBitmap);

  // Draw the command list as a pattern on a bitmap
  Context.BeginScene;
  try
    for i := 0 to 3 do
      for j := 0 to 6 do
      begin
        Context.DrawCmdList(CmdList, SVGRect(i*50, j*50, (i+1)*50, (j+1)* 50));
      end;
  finally
    Context.EndScene;
  end;  

Rendering to a printer

Just as for the command list, the SVG control package also has a number of interfaces and functions defined for rendering to a printer, independent of the underlying graphical platform.

Rendering to a printer is supported by render context GDI+, Direct2D 3D11, Quartz and FMX canvas.

First of all, a print job must be created. This can be done using function “CreatePrintJob” of the render context manager.

var
  PrintJob: ISVGPrintJob;
  ..
PrintJob := TSVGRenderContextManager.CreatePrintJob('PrintSVG');

Using the “PrintJob” interface we can create pages to render to.

var
  Context: ISVGRenderContext;
  ..
Context := PrintJob.BeginPage(Printer.PageWidth, Printer.PageHeight);

If we are finished with drawing to the rendercontext we must call “EndPage” on the PrintJob interface, at which point the page is sent to the printer.

PrintJob.EndPage;

In the example below, a number of SVG’s that are contained in a stringlist are each printed on a page.

var
  i: Integer;
  Root: ISVGRoot;
  PrintJob: ISVGPrintJob;
  Context: ISVGRenderContext;
begin
  // Select a printer
  if not PrintDialog1.Execute then
    Exit;

  // Create a root object
  Root := TSVGRootVCL.Create;

  // Create a print job
  PrintJob := TSVGRenderContextManager.CreatePrintJob('PrintSVG');

  for i := 0 to sl.Count - 1 do
  begin

    // Load the SVG in the root
    if Root.DocFindOrLoad(TSVGIri.Create(isFile, sl[i], '')) = nil then
      raise Exception.CreateFmt('Loading %s failed', [sl[i]]);

    // Render to page
    Context := PrintJob.BeginPage(Printer.PageWidth, Printer.PageHeight);
    try
      Context.BeginScene;
      try
        SVGRenderToRenderContext(
          Root,
          Context,
          Printer.PageWidth,
          Printer.PageHeight,
          [sroClippath, sroFilters],
          True // scale to page
          );

      finally
        Context.EndScene;
      end;

    finally
      PrintJob.EndPage;
    end;
  end;
end;

An implementation for rendering SVG to a printer: “PrintPreview”, can be found on the delphi-svg-control-examples github page.

The picture at the start of this post shows a screenshot of the SVG print preview, which has several settings for defining the layout of the SVG on a page. For example, you can set margins and the scaling and alignment.

In can also print one SVG over several pages, in which case you can define a “glue edge”, so you can glue the pages together after printing.

The print preview example can be compiled with the full SVG control package update 8 or later and the latest versions of the SVG control demo package.