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.