leafer-x-metafile
v0.2.0
Published
WMF / EMF+ metafile replay into neutral drawing members — a zero-dependency GDI record player (WMF placeable stream, EMF+ GDI+ records, embedded DIB extraction) for LeaferJS-based renderers
Maintainers
Readme
leafer-x-metafile
Zero-dependency WMF / EMF+ metafile player: replays placeable-WMF record streams and EMF+ (GDI+) record streams into neutral, box-local drawing members — pictures, shapes, paths, and text boxes — that any renderer can consume.
Browsers cannot rasterize GDI metafiles, so Office documents carrying .wmf/.emf vector art render as empty frames. This package reassembles those byte streams (including dual-mode files whose real art hides in META_ESCAPE "WMFC" chunks as an embedded EMF+ stream) and replays them record-by-record into structured members, ready to be painted on canvas or instantiated as LeaferJS elements. Consumed by @docen/docx's layout projection, which adapts the members into its LayoutDoc drawings.
Features
- 🎞️ WMF replay —
wmfMemberswalks a placeable-WMF stream (pens, brushes, fonts, DC state, polygons, polylines, rectangles, ellipses,ExtTextOutwith per-byte Dx tracking,StretchDIBitsblts) - 🖼️ EMF+ replay —
emfPlusMembersreassembles the WMFC-carried EMF and replays its GDI+ records (fill/draw paths, draw-image points, world transforms,DrawString), layered with the GDI-side text the exporters keep there - 🧱 DIB fallback —
wmfDibFallback/bmpDataUrlre-frame the largest blt-record DIB as a BMP data URL when no vector replay applies - 📦 Neutral output — members are plain data (
MetafileMember), geometry in box-local px, text as runs; no Leafer, no DOM, no dependencies
Installation
# Install with pnpm
$ pnpm add leafer-x-metafile
# Install with npm
$ npm install leafer-x-metafileQuick Start
import { emfPlusMembers, wmfMembers } from "leafer-x-metafile";
const bytes = new Uint8Array(/* the .wmf/.emf file */);
const members = emfPlusMembers(bytes, 400, 300) ?? wmfMembers(bytes, 400, 300);
for (const member of members ?? []) {
if (member.kind === "path") drawPath(member.d, member.fill, member.line);
}