Skip to main content

Parse and render WMF, EMF, and EMF+

Metafile Renderer is a TypeScript parser and renderer for Windows Metafile formats. Parsing has no runtime dependency. PNG rendering uses CanvasKit/Skia and bundled metric-compatible fonts, so the same input can render consistently in a browser or Node.js.

Choose an operating mode

ModeDependenciesResult
Parse onlyTypeScript coreRecords and decoded fields
Browser renderTypeScript core, CanvasKit WASM, bundled fontsPNG Blob
Node.js renderTypeScript core, CanvasKit, bundled or Windows system fontsPNG Uint8Array

The package is currently private and is not published to a public npm registry. Pin an approved source commit or release artifact rather than copying individual source files.

Render in Node.js

import {readFile, writeFile} from 'node:fs/promises';
import {initCanvasKitNode} from './src/render/canvaskit-node.ts';
import {CanvasKitRenderer} from './src/render/canvaskit-renderer.ts';
import {parseWmf} from './src/wmf/parse.ts';
import {playWmf} from './src/wmf/playback.ts';
import {playEmf} from './src/emf/playback.ts';

const dependencies = await initCanvasKitNode();

async function render(inputPath, outputPath, format) {
const bytes = new Uint8Array(await readFile(inputPath));
const renderer = new CanvasKitRenderer(dependencies);

if (format === 'emf') playEmf(bytes, renderer, 1200, 800);
else playWmf(parseWmf(bytes), renderer, 1200, 800);

const png = renderer.finish();
if (!png) throw new Error('The metafile did not produce visible output.');
await writeFile(outputPath, png);
}

await render('diagram.emf', 'diagram.png', 'emf');

Initialize CanvasKit and fonts once, then reuse the returned dependencies. Create a Renderer for each output operation.

Parse without rendering

Parsing does not require WASM or fonts and is suitable for inspection, indexing, and security preflight checks.

import {parseWmf} from './src/wmf/parse.ts';
import {parseEmf} from './src/emf/parse.ts';
import {enumerateEmf} from './src/emf/parse-plus.ts';

const parsedWmf = parseWmf(wmfBytes);
const parsedEmf = parseEmf(emfBytes);

for (const record of enumerateEmf(emfBytes)) {
console.log(record.type, record.fields);
}

Render in the browser

The supplied browser entry exposes window.MetafileFE. Files remain in the browser and are not uploaded.

<input id="source" type="file" accept=".wmf,.emf" />
<img id="preview" alt="Rendered metafile" />
<script>window.process = window.process || {env: {}};</script>
<script src="/vendor/canvaskit/canvaskit.js"></script>
<script type="module" src="/frontend/dist/main.js"></script>
<script type="module">
await MetafileFE.init((done, total) => console.log(`fonts ${done}/${total}`));
source.addEventListener('change', async () => {
const bytes = new Uint8Array(await source.files[0].arrayBuffer());
const result = MetafileFE.render(bytes);
preview.src = URL.createObjectURL(result.blob);
console.log(result.format, result.width, result.height, result.ms);
});
</script>

Performance and deployment

The complete font set includes large CJK fonts. Loading every font up front can add tens of megabytes to the first visit. For a production application, cache CanvasKit and fonts with immutable URLs and load Korean, Japanese, Simplified Chinese, and Traditional Chinese families only when the document requires them.

Limit input byte size and output dimensions before rendering. The browser API caps dimensions, but an application should also apply its own memory and execution-time limits. Do not treat a successfully parsed metafile as trusted active content.

Troubleshooting

SymptomCheck
CanvasKitInit is missingLoad canvaskit.js before the frontend module
WASM fails to loadVerify /vendor/canvaskit/canvaskit.wasm, MIME type, and CSP
Text is missing or substitutedConfirm the required font family was loaded
Browser starts slowlyReduce the initial font set and enable long-lived asset caching
Output size is unexpectedPass explicit dimensions or derive the native size from the metafile header

Open the live Metafile Renderer Playground to upload a WMF or EMF file and compare browser and reference rendering after the renderer image and its large font assets are deployed.