Emf2Svg 1.1.0

dotnet add package Emf2Svg --version 1.1.0
                    
NuGet\Install-Package Emf2Svg -Version 1.1.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Emf2Svg" Version="1.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Emf2Svg" Version="1.1.0" />
                    
Directory.Packages.props
<PackageReference Include="Emf2Svg" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Emf2Svg --version 1.1.0
                    
#r "nuget: Emf2Svg, 1.1.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Emf2Svg@1.1.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Emf2Svg&version=1.1.0
                    
Install as a Cake Addin
#tool nuget:?package=Emf2Svg&version=1.1.0
                    
Install as a Cake Tool

Emf2Svg

Disclaimer: All code in this repository was written by Claude Code (Anthropic's AI coding assistant), with the human author directing the design and verifying the output.

⚠️ Experimental — not production-ready. This library covers only a small subset of the EMF specification (common drawing primitives like lines, arcs, and basic pens). Many record types are silently skipped. The implemented handlers have not been thoroughly tested against a wide range of real-world EMF files. Do not use this in critical or production systems without validating the output against your specific files.

A pure C# library for converting EMF (Enhanced Metafile) to SVG. Built as a port of the libemf2svg C library.

Installation

dotnet add package Emf2Svg

Library Usage

File path → SVG string

string svg = EmfProcessor.ConvertToString("input.emf");

Stream → SVG string

using var stream = File.OpenRead("input.emf");
string svg = EmfProcessor.ConvertToString(stream);

File path → file

EmfProcessor.Process("input.emf", "output.svg");

Stream → Stream

Useful when piping into another library without touching the disk:

using var emfStream = File.OpenRead("input.emf");
using var svgStream = new MemoryStream();
EmfProcessor.Process(emfStream, svgStream);

EMF → SVG → PNG (with Svg.Skia)

// dotnet add package Svg.Skia
string svg = EmfProcessor.ConvertToString("input.emf");

using var skSvg = new SKSvg();
skSvg.FromSvg(svg);

var rect = skSvg.Picture.CullRect;
using var bitmap = new SKBitmap((int)rect.Width, (int)rect.Height);
using var canvas = new SKCanvas(bitmap);
canvas.DrawPicture(skSvg.Picture);

using var png = bitmap.Encode(SKEncodedImageFormat.Png, 100);
File.WriteAllBytes("output.png", png.ToArray());

CLI Usage

The Emf2Svg.Cli project provides a command-line interface:

cd Emf2Svg.Cli
dotnet run -- -i input.emf -o output.svg

# List all record types in a file (useful for checking coverage before converting)
dotnet run -- --list-records -i input.emf

Features

  • Pure C#, no native dependencies
  • Available as a NuGet library (Emf2Svg) and a CLI tool (Emf2Svg.Cli)
  • Produces SVG output visually identical to libemf2svg
  • Handles the most common EMF drawing primitives:
    • Lines (LINETO, MOVETOEX)
    • Arcs (ARC, ANGLEARC)
    • Pens and colors (CREATEPEN, SELECTOBJECT, DELETEOBJECT)
    • Clipping regions (INTERSECTCLIPRECT)
    • Device context save/restore (SAVEDC, RESTOREDC)
    • Coordinate transforms (SETMAPMODE, SETWINDOWEXTEX/SETWINDOWORGEX, SETVIEWPORTEXTEX/SETVIEWPORTORGEX) — scoped by SAVEDC/RESTOREDC per MS-EMF

See CONTRIBUTING.md for a full table of all EMF record types and their implementation status.

Output

The SVG output format matches libemf2svg — same path data, same stroke attributes, same clip regions. Each drawing primitive becomes a <path> element with stroke and fill="none". Where the reference behaviour is incorrect the output deliberately differs; those cases are listed in docs/NOTES.md.

Project Structure

Emf2Svg/
├── Emf2Svg.sln
├── Emf2Svg.csproj          NuGet library
├── EmfProcessor.cs         Public API + record dispatch loop
├── EmfStructs.cs           Binary structs (PointL, RectL, ColorRef, LogPen, …)
├── DrawingState.cs         GDI device context + object table
├── CoordTransform.cs       point_cal() equivalent — EMF → SVG coordinates
├── SvgWriter.cs            SVG output (path elements, stroke/fill, clip defs)
├── Handlers/
│   ├── ControlHandlers.cs  HEADER, EOF
│   ├── StateHandlers.cs    Map mode, window/viewport extents, SaveDC/RestoreDC
│   ├── ObjectHandlers.cs   Pen creation, object table, stock objects
│   └── DrawingHandlers.cs  Lines, arcs, clipping
├── Emf2Svg.Cli/
│   ├── Emf2Svg.Cli.csproj  CLI project (references the library)
│   └── Program.cs          CLI entry point
└── Emf2Svg.Tests/
    ├── EmfBuilder.cs       Builds synthetic EMF byte streams for tests
    ├── ControlFiles.cs     EMFs whose output must never change
    └── Golden/             Expected SVG for each control file

Building

Requires .NET 9.

dotnet build

Tests

dotnet test

The suite builds small synthetic EMF byte streams in memory (Emf2Svg.Tests/EmfBuilder.cs), so no binary fixtures are checked in. Golden/ holds the expected SVG for a set of control files that must keep rendering byte-identically — a diff there is a regression.

Coordinate Transform

EMF coordinates are converted to SVG using the same formula as libemf2svg:

svgX = ((emfX - windowOrgX) * scalingX + viewPortOrgX) * globalScaling
svgY = ((emfY - windowOrgY) * scalingY + viewPortOrgY) * globalScaling

globalScaling = imgWidth / |rclBounds.right - rclBounds.left|
scalingX = viewPortExX / windowExX   (when both are set)

For U_MM_TEXT (the most common map mode), scalingX = scalingY = 1.0 and the origins are ignored.

The map mode, the window/viewport origins and the window/viewport extents are all part of the device context. SAVEDC saves them and RESTOREDC puts them back, so a metafile that opens a short anisotropic block to draw a few marks does not leak that scaling into everything drawn afterwards.

Credits

This project is a C# port of libemf2svg by Pierre-Jean Coudert (kakwa), licensed under the MIT License.

The binary struct definitions and coordinate transform logic are derived from:

  • src/lib/emf2svg_utils.c — coordinate math, arc rendering helpers
  • src/lib/emf2svg_rec_*.c — per-record draw handlers
  • src/lib/emf2svg_rec_control.c — SVG header/footer generation
  • vendor/libuemf/include/uemf.h — all EMF binary struct layouts (from libuemf)

License

MIT

Product Compatible and additional computed target framework versions.
.NET net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net9.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.1.0 177 8/20/2026
1.0.2 134 3/15/2026
1.0.1 123 3/15/2026
1.0.0 700 3/15/2026