Skip to content

EMF for Office and report tools

Word, Excel, PowerPoint and most Delphi report tools only take vector pictures as Windows Enhanced Metafiles (EMF). This page shows how to export a drawing to EMF, so it stays sharp at any zoom and in print, and keeps its text selectable where the font allows it.

EMF export is VCL only.

Requirements

The export is written by the GDI+ backend, whatever your application renders with on screen. Enable GDI+ and the GDI font system in Common\Vcl\ContextSettingsVCL.inc, in addition to the render context your application uses:

{$Define SVGGDIP}
{$Define SVGFontGDI}

This does not change what your application renders with: Direct2D stays the default. GDI+ only has to be available for the export to switch to it. A project can also set these symbols in its own project options instead of in the shared file; see setting this per project.

Without the two defines the export routines still compile, and raise an exception naming them when called.

Exporting

Add BVE.SVG2ExportEMF.VCL to the uses clause. From a file:

SVGFileRenderToEmfFile('drawing.svg', 'drawing.emf');

From a document you already loaded, for example the one in a component after your application has changed it:

SVGRenderToEmfFile(SVG2Image1.SVGRoot, 'drawing.emf');

The document itself is not changed. For the duration of the call it renders through GDI+; afterwards it is back on the backend it had, also when the export raises an exception.

Size

Both routines take a width and a height in pixels. The default, 0 for both, uses the drawing's own size:

SVGRenderToEmfFile(Root, 'drawing.emf', 800, 600);

Text

The next parameter decides how text is written:

Value Result
etmStringsWithPlacedCharacters Each run of text is one piece of text, with every character placed. The default, and the best choice for selectable text.
etmStrings Every placed character starts a new piece of text.
etmPaths Text is converted to outlines. Nothing to select, but it looks the same on every computer, with or without the font.

Text is written as text only for installed TrueType fonts, and the computer that opens the file needs the same fonts to show it as drawn. Choose etmPaths when the file goes to people who may not have them.

Filters and clip paths

The render options default to none, on purpose. Filters and clip paths make parts of the output bitmaps, which defeats the point of a vector file. Pass them only when the drawing needs them:

SVGRenderToEmfFile(Root, 'drawing.emf', 0, 0,
  etmStringsWithPlacedCharacters, [sroFilters, sroClippath]);

Using the EMF

  • In Office: insert the file as a picture, or put it on the clipboard and paste it. Loading the file into a VCL TMetafile and assigning that to the Clipboard gives Office a vector picture to paste.
  • In a report tool: FastReport, ReportBuilder and similar tools accept EMF in their picture components. Export the drawing to a file or a stream before the report runs and load it there.

Many files, several threads, or a DLL

GDI+ has to be started once per process. The export routines do this themselves, counted under a lock, so two threads exporting at the same time cannot shut GDI+ down under each other.

When exporting many files, keep GDI+ running across all of them to save a start and stop per file:

SVGEmfGraphicsAcquire;
try
  for FileName in FileNames do
    SVGFileRenderToEmfFile(FileName, ChangeFileExt(FileName, '.emf'));
finally
  SVGEmfGraphicsRelease;
end;

Every SVGEmfGraphicsAcquire needs a matching SVGEmfGraphicsRelease.

The routines are safe to call from a DLL: GDI+ is started on the first export rather than when the unit is initialised, because a DLL's initialisation runs where GDI+ may not be started.

Doing it by hand

To write into a GDI+ metafile you create yourself, create the render context yourself as well. The document must then use the matching resource factory for the duration of the render, which you set and restore:

SaveFactory := Root.ResourceFactory;
Root.ResourceFactory :=
  TSVGRenderContextManager.CreateResourceFactory(rcGDIPlus, fsGDI);
try
  RCGP := TSVGContextGP.Create(FileName, W, H);
  RC := RCGP;
  RCGP.TextFormattingOptions := [tfoStringsWithPlacedCharacters];

  RC.BeginScene;
  try
    SVGRenderToRenderContext(Root, RC, W, H, []);
  finally
    RC.EndScene;
  end;

  RC := nil;   // the metafile is written when the context is released
finally
  if SaveFactory = TSVGRenderContextManager.ResourceFactory then
    Root.ResourceFactory := nil
  else
    Root.ResourceFactory := SaveFactory;
end;

The factory and the context must match

The resource factory decides what kind of path data and text the document creates; it does not create the render context. A mismatch fails without a clear message: GDI+ path data on a Direct2D context is an access violation inside d2d1.dll, and Direct2D path data on the GDI+ metafile writer gives an empty file.

Examples\RenderToEMF in the examples repository shows both routes side by side.