emf-converter 1.1.23 β 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -340
- package/package.json +4 -5
package/README.md
CHANGED
|
@@ -1,378 +1,73 @@
|
|
|
1
1
|
# emf-converter
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/emf-converter)
|
|
4
|
+
[](https://github.com/ChristopherVR/pptx-viewer/blob/main/LICENSE)
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
A zero-dependency TypeScript library that converts **EMF** (Enhanced Metafile) and **WMF** (Windows Metafile) binary buffers into **PNG data URLs** by parsing their record streams and replaying drawing commands onto an HTML Canvas.
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
- [Table of Contents](#table-of-contents)
|
|
9
|
-
- [Overview](#overview)
|
|
10
|
-
- [Quick Start](#quick-start)
|
|
11
|
-
- [API Reference](#api-reference)
|
|
12
|
-
- [`convertEmfToDataUrl(buffer, maxWidth?, maxHeight?)`](#convertemftodataurlbuffer-maxwidth-maxheight)
|
|
13
|
-
- [`convertWmfToDataUrl(buffer, maxWidth?, maxHeight?)`](#convertwmftodataurlbuffer-maxwidth-maxheight)
|
|
14
|
-
- [Architecture](#architecture)
|
|
15
|
-
- [High-Level Pipeline](#high-level-pipeline)
|
|
16
|
-
- [Module Map](#module-map)
|
|
17
|
-
- [EMF Record Replay Loop](#emf-record-replay-loop)
|
|
18
|
-
- [EMF+ Dual-Mode Processing](#emf-dual-mode-processing)
|
|
19
|
-
- [WMF Processing](#wmf-processing)
|
|
20
|
-
- [Deep Dive: How It Works](#deep-dive-how-it-works)
|
|
21
|
-
- [1. Header Parsing](#1-header-parsing)
|
|
22
|
-
- [2. Canvas Creation \& Scaling](#2-canvas-creation--scaling)
|
|
23
|
-
- [3. GDI Record Replay](#3-gdi-record-replay)
|
|
24
|
-
- [4. EMF+ Record Replay](#4-emf-record-replay)
|
|
25
|
-
- [5. Coordinate Systems](#5-coordinate-systems)
|
|
26
|
-
- [6. GDI Object Table](#6-gdi-object-table)
|
|
27
|
-
- [7. DIB (Bitmap) Decoding](#7-dib-bitmap-decoding)
|
|
28
|
-
- [8. Deferred Image Processing](#8-deferred-image-processing)
|
|
29
|
-
- [9. World Transforms](#9-world-transforms)
|
|
30
|
-
- [Supported Record Types](#supported-record-types)
|
|
31
|
-
- [EMF GDI Records](#emf-gdi-records)
|
|
32
|
-
- [EMF+ Records](#emf-records)
|
|
33
|
-
- [WMF Records](#wmf-records)
|
|
34
|
-
- [File Structure Reference](#file-structure-reference)
|
|
35
|
-
- [Limitations](#limitations)
|
|
8
|
+
Windows Metafiles store a sequence of GDI drawing commands and are commonly embedded inside Office documents (PPTX, DOCX). This converter reads the raw binary, interprets each record, and replays the drawing operations onto a Canvas to produce a rasterised PNG. It handles three formats:
|
|
36
9
|
|
|
37
|
-
|
|
10
|
+
| Format | Description | Coordinate system |
|
|
11
|
+
| -------- | ------------------------------ | ----------------------- |
|
|
12
|
+
| **WMF** | Windows Metafile (16-bit) | Window/viewport mapping |
|
|
13
|
+
| **EMF** | Enhanced Metafile (32-bit GDI) | Bounds-based scaling |
|
|
14
|
+
| **EMF+** | GDI+ extension embedded in EMF | World transform matrix |
|
|
38
15
|
|
|
39
|
-
|
|
16
|
+
<samp>**[π¦ npm](https://www.npmjs.com/package/emf-converter)** Β· **[π Full docs](https://christophervr.github.io/pptx-viewer/)**</samp>
|
|
40
17
|
|
|
41
|
-
|
|
18
|
+
---
|
|
42
19
|
|
|
43
|
-
|
|
20
|
+
## Install
|
|
44
21
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
| **EMF** | Enhanced Metafile (32-bit GDI) | 32-bit aligned | Bounds-based scaling |
|
|
49
|
-
| **EMF+** | GDI+ extension embedded in EMF | 32-bit aligned | World transform matrix |
|
|
22
|
+
```bash
|
|
23
|
+
npm install emf-converter
|
|
24
|
+
```
|
|
50
25
|
|
|
51
|
-
|
|
26
|
+
No dependencies. Requires a Canvas API at runtime β `OffscreenCanvas` (Web Workers) or `HTMLCanvasElement`.
|
|
52
27
|
|
|
53
|
-
## Quick
|
|
28
|
+
## Quick start
|
|
54
29
|
|
|
55
30
|
```typescript
|
|
56
|
-
import { convertEmfToDataUrl, convertWmfToDataUrl } from
|
|
31
|
+
import { convertEmfToDataUrl, convertWmfToDataUrl } from 'emf-converter';
|
|
57
32
|
|
|
58
|
-
// Convert an EMF buffer to a PNG data URL
|
|
59
33
|
const emfBuffer: ArrayBuffer = /* loaded from file or network */;
|
|
60
34
|
const pngDataUrl = await convertEmfToDataUrl(emfBuffer);
|
|
61
35
|
// => "data:image/png;base64,iVBORw0KGgo..."
|
|
62
36
|
|
|
63
|
-
// Convert a WMF buffer to a PNG data URL
|
|
64
|
-
const wmfBuffer: ArrayBuffer = /* loaded from file or network */;
|
|
65
37
|
const wmfPng = await convertWmfToDataUrl(wmfBuffer);
|
|
66
38
|
|
|
67
|
-
// Optional: limit output dimensions
|
|
39
|
+
// Optional: limit output dimensions (aspect ratio preserved)
|
|
68
40
|
const scaled = await convertEmfToDataUrl(emfBuffer, 1024, 768);
|
|
69
41
|
```
|
|
70
42
|
|
|
71
|
-
Both functions return `Promise<string | null>` β
|
|
72
|
-
|
|
73
|
-
---
|
|
74
|
-
|
|
75
|
-
## API Reference
|
|
76
|
-
|
|
77
|
-
### `convertEmfToDataUrl(buffer, maxWidth?, maxHeight?)`
|
|
78
|
-
|
|
79
|
-
Converts an EMF binary buffer to a PNG data URL.
|
|
80
|
-
|
|
81
|
-
| Parameter | Type | Description |
|
|
82
|
-
| ----------- | ------------------------- | --------------------------------- |
|
|
83
|
-
| `buffer` | `ArrayBuffer` | The raw EMF file bytes |
|
|
84
|
-
| `maxWidth` | `number` (optional) | Maximum output width in pixels |
|
|
85
|
-
| `maxHeight` | `number` (optional) | Maximum output height in pixels |
|
|
86
|
-
| **Returns** | `Promise<string \| null>` | PNG data URL or `null` on failure |
|
|
43
|
+
Both functions return `Promise<string | null>` β `null` if the buffer is invalid or no Canvas API is available.
|
|
87
44
|
|
|
88
|
-
|
|
45
|
+
## API
|
|
89
46
|
|
|
90
|
-
|
|
47
|
+
### `convertEmfToDataUrl(buffer, maxWidth?, maxHeight?)` Β· `convertWmfToDataUrl(buffer, maxWidth?, maxHeight?)`
|
|
91
48
|
|
|
92
49
|
| Parameter | Type | Description |
|
|
93
50
|
| ----------- | ------------------------- | --------------------------------- |
|
|
94
|
-
| `buffer` | `ArrayBuffer` |
|
|
51
|
+
| `buffer` | `ArrayBuffer` | Raw EMF/WMF file bytes |
|
|
95
52
|
| `maxWidth` | `number` (optional) | Maximum output width in pixels |
|
|
96
53
|
| `maxHeight` | `number` (optional) | Maximum output height in pixels |
|
|
97
54
|
| **Returns** | `Promise<string \| null>` | PNG data URL or `null` on failure |
|
|
98
55
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
## Architecture
|
|
102
|
-
|
|
103
|
-
### High-Level Pipeline
|
|
104
|
-
|
|
105
|
-
The converter follows a three-phase pipeline: **Parse β Replay β Export**.
|
|
106
|
-
|
|
107
|
-
_See the [architecture diagrams on GitHub](https://github.com/ChristopherVR/pptx-viewer/blob/main/packages/emf-converter/README.md) for visual representations._
|
|
108
|
-
|
|
109
|
-
### Module Map
|
|
110
|
-
|
|
111
|
-
Every source file has a specific responsibility. Here's how they connect:
|
|
112
|
-
|
|
113
|
-
_See the [architecture diagrams on GitHub](https://github.com/ChristopherVR/pptx-viewer/blob/main/packages/emf-converter/README.md) for visual representations._
|
|
114
|
-
|
|
115
|
-
### EMF Record Replay Loop
|
|
116
|
-
|
|
117
|
-
The core of the EMF converter is a sequential record-scanning loop that dispatches each record to the appropriate handler:
|
|
118
|
-
|
|
119
|
-
_See the [architecture diagrams on GitHub](https://github.com/ChristopherVR/pptx-viewer/blob/main/packages/emf-converter/README.md) for visual representations._
|
|
120
|
-
|
|
121
|
-
### EMF+ Dual-Mode Processing
|
|
122
|
-
|
|
123
|
-
EMF files can contain embedded EMF+ records inside `EMR_COMMENT` records. When detected, these are processed by a parallel GDI+ replay engine:
|
|
124
|
-
|
|
125
|
-
_See the [architecture diagrams on GitHub](https://github.com/ChristopherVR/pptx-viewer/blob/main/packages/emf-converter/README.md) for visual representations._
|
|
126
|
-
|
|
127
|
-
The EMF+ state (object table, world transform, save stack) **persists across multiple `EMR_COMMENT` records** within the same file, allowing complex drawings to span several comment blocks.
|
|
128
|
-
|
|
129
|
-
### WMF Processing
|
|
130
|
-
|
|
131
|
-
WMF uses a simpler 16-bit record format with word-aligned record sizes:
|
|
132
|
-
|
|
133
|
-
_See the [architecture diagrams on GitHub](https://github.com/ChristopherVR/pptx-viewer/blob/main/packages/emf-converter/README.md) for visual representations._
|
|
134
|
-
|
|
135
|
-
---
|
|
136
|
-
|
|
137
|
-
## Deep Dive: How It Works
|
|
138
|
-
|
|
139
|
-
### 1. Header Parsing
|
|
140
|
-
|
|
141
|
-
**EMF** files begin with an `EMR_HEADER` record (type `1`) containing:
|
|
142
|
-
|
|
143
|
-
- **Bounds rectangle** (8β20 bytes): the logical pixel extents of the drawing
|
|
144
|
-
- **Frame rectangle** (24β36 bytes): the physical dimensions in 0.01mm units
|
|
145
|
-
|
|
146
|
-
The parser in `emf-header-parser.ts` tries the bounds first; if they're degenerate (zero width/height), it falls back to the frame rectangle.
|
|
147
|
-
|
|
148
|
-
**WMF** files may have an optional **Aldus Placeable Metafile (APM)** header (magic `0x9AC6CDD7`) at byte 0, which provides bounds and DPI. The standard WMF header follows, starting with a file type (`1` = in-memory, `2` = on-disk).
|
|
149
|
-
|
|
150
|
-
### 2. Canvas Creation & Scaling
|
|
151
|
-
|
|
152
|
-
`emf-canvas-helpers.ts` β `createCanvas()` creates a rendering surface:
|
|
153
|
-
|
|
154
|
-
1. Compute logical dimensions from the metafile bounds
|
|
155
|
-
2. Apply `maxWidth`/`maxHeight` constraints if provided (maintaining aspect ratio)
|
|
156
|
-
3. Clamp to a maximum of **4096Γ4096** pixels to prevent memory issues
|
|
157
|
-
4. Prefer `OffscreenCanvas` (works in Web Workers); fall back to `HTMLCanvasElement`
|
|
158
|
-
|
|
159
|
-
### 3. GDI Record Replay
|
|
160
|
-
|
|
161
|
-
The GDI replay engine (`emf-record-replay.ts`) scans records sequentially. Each record has an 8-byte header:
|
|
162
|
-
|
|
163
|
-
```
|
|
164
|
-
ββββββββββββββββ¬βββββββββββββββ
|
|
165
|
-
β Record Type β Record Size β
|
|
166
|
-
β (uint32) β (uint32) β
|
|
167
|
-
β 4 bytes β 4 bytes β
|
|
168
|
-
ββββββββββββββββ΄βββββββββββββββ€
|
|
169
|
-
β Record Data β
|
|
170
|
-
β (recSize - 8 bytes) β
|
|
171
|
-
βββββββββββββββββββββββββββββββ
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
Records are dispatched to three handler modules:
|
|
56
|
+
## How it works
|
|
175
57
|
|
|
176
|
-
|
|
177
|
-
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
178
|
-
| `emf-gdi-state-handlers.ts` | SaveDC, RestoreDC, SetTextColor, SetBkColor, SetBkMode, SetPolyFillMode, SetTextAlign + delegates to transform and object handlers |
|
|
179
|
-
| `emf-gdi-draw-handlers.ts` | Delegates to shape handlers (MoveTo, LineTo, Rectangle, Ellipse, Arc family) and text/bitmap handlers (ExtTextOutW, BitBlt, StretchDIBits) |
|
|
180
|
-
| `emf-gdi-poly-path-handlers.ts` | Polygon, Polyline, PolyBezier (16-bit and 32-bit variants), PolyPolygon, BeginPath/EndPath/FillPath/StrokePath/CloseFigure |
|
|
58
|
+
A three-phase pipeline: **parse β replay β export**. The header parser extracts the drawing bounds, a Canvas is created and clamped to 4096Γ4096, then records are scanned sequentially and dispatched to GDI, EMF+, or WMF handlers that drive the Canvas 2D context. Embedded bitmaps (DIB and GDI+ pixel formats) and recursively embedded metafiles are resolved asynchronously after the synchronous replay completes.
|
|
181
59
|
|
|
182
|
-
|
|
60
|
+
It supports 300+ EMF GDI record types, the EMF+ (GDI+) record set, and legacy WMF records, including state, transforms, objects, shapes, poly/path operations, text, bitmaps, and clipping. For the full record-type coverage, coordinate-system details, object tables, and module maps, see the [full documentation](https://christophervr.github.io/pptx-viewer/).
|
|
183
61
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
```
|
|
187
|
-
ββββββββββββββββ¬βββββββββββββββ¬βββββββββββββββ¬βββββββββββββββ
|
|
188
|
-
β Record Type β Record Flagsβ Record Size β Data Size β
|
|
189
|
-
β (uint16) β (uint16) β (uint32) β (uint32) β
|
|
190
|
-
β 2 bytes β 2 bytes β 4 bytes β 4 bytes β
|
|
191
|
-
ββββββββββββββββ΄βββββββββββββββ΄βββββββββββββββ΄βββββββββββββββ€
|
|
192
|
-
β Record Data β
|
|
193
|
-
β (dataSize bytes) β
|
|
194
|
-
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
The EMF+ replay engine (`emf-plus-replay.ts`) dispatches to:
|
|
198
|
-
|
|
199
|
-
| Module | Handles |
|
|
200
|
-
| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
|
|
201
|
-
| `emf-plus-object-parser.ts` | Object definitions: Brush, Pen, Font, Path, Image, StringFormat, ImageAttributes |
|
|
202
|
-
| `emf-plus-draw-handlers.ts` | Shape operations: FillRects, DrawRects, FillEllipse, DrawEllipse, FillPie, DrawPie, DrawArc, DrawLines, FillPolygon |
|
|
203
|
-
| `emf-plus-text-image-handlers.ts` | FillPath, DrawPath, DrawString, DrawDriverString, DrawImage, DrawImagePoints |
|
|
204
|
-
| `emf-plus-state-handlers.ts` | Transform operations (Set/Reset/Multiply/Translate/Scale/Rotate WorldTransform), Save/Restore, Clipping, Rendering hints |
|
|
205
|
-
|
|
206
|
-
### 5. Coordinate Systems
|
|
207
|
-
|
|
208
|
-
The converter manages multiple coordinate mapping systems:
|
|
209
|
-
|
|
210
|
-
_See the [architecture diagrams on GitHub](https://github.com/ChristopherVR/pptx-viewer/blob/main/packages/emf-converter/README.md) for visual representations._
|
|
211
|
-
|
|
212
|
-
**GDI coordinates** (`emf-gdi-coord.ts`) use either simple bounds-based scaling or full window/viewport mapping mode, activated when the metafile sets `SetWindowExtEx`/`SetViewportExtEx`.
|
|
213
|
-
|
|
214
|
-
**EMF+ coordinates** use a 6-element affine transformation matrix `[a, b, c, d, e, f]` applied via `ctx.setTransform()`, supporting rotation, scaling, shearing, and translation.
|
|
215
|
-
|
|
216
|
-
**WMF coordinates** map through closure-based `mx()`/`my()`/`mw()`/`mh()` functions that convert from window space to canvas pixels.
|
|
217
|
-
|
|
218
|
-
### 6. GDI Object Table
|
|
219
|
-
|
|
220
|
-
Both GDI and GDI+ maintain their own object tables β essentially registries of reusable drawing resources:
|
|
221
|
-
|
|
222
|
-
_See the [architecture diagrams on GitHub](https://github.com/ChristopherVR/pptx-viewer/blob/main/packages/emf-converter/README.md) for visual representations._
|
|
223
|
-
|
|
224
|
-
**GDI objects** are created via `EMR_CREATEPEN`, `EMR_CREATEBRUSHINDIRECT`, `EMR_EXTCREATEFONTINDIRECTW`, etc., and selected into the drawing context with `EMR_SELECTOBJECT`. Stock objects (base index `0x80000000`) provide system defaults like `WHITE_BRUSH`, `BLACK_PEN`, etc.
|
|
225
|
-
|
|
226
|
-
**EMF+ objects** are defined via `EMFPLUS_OBJECT` records with a type/ID pair. Drawing commands reference objects by their slot ID in the lower 8 bits of `recFlags`.
|
|
227
|
-
|
|
228
|
-
### 7. DIB (Bitmap) Decoding
|
|
229
|
-
|
|
230
|
-
Metafiles can contain embedded bitmaps as Device-Independent Bitmaps (DIBs). The decoder pipeline handles:
|
|
231
|
-
|
|
232
|
-
_See the [architecture diagrams on GitHub](https://github.com/ChristopherVR/pptx-viewer/blob/main/packages/emf-converter/README.md) for visual representations._
|
|
233
|
-
|
|
234
|
-
EMF+ also has its own bitmap format (`emf-plus-bitmap-decoder.ts`) supporting GDI+ pixel formats:
|
|
235
|
-
|
|
236
|
-
- `PixelFormat24bppRGB`
|
|
237
|
-
- `PixelFormat32bppRGB`
|
|
238
|
-
- `PixelFormat32bppARGB`
|
|
239
|
-
- `PixelFormat32bppPARGB` (pre-multiplied alpha, un-multiplied during decode)
|
|
240
|
-
|
|
241
|
-
### 8. Deferred Image Processing
|
|
242
|
-
|
|
243
|
-
Image draws (both GDI `StretchDIBits`/`BitBlt` and EMF+ `DrawImage`/`DrawImagePoints`) that reference bitmaps or embedded metafiles are collected as **deferred images** during the synchronous replay phase. After all records are processed, these are resolved asynchronously:
|
|
244
|
-
|
|
245
|
-
_See the [architecture diagrams on GitHub](https://github.com/ChristopherVR/pptx-viewer/blob/main/packages/emf-converter/README.md) for visual representations._
|
|
246
|
-
|
|
247
|
-
This two-phase approach is necessary because `createImageBitmap()` is asynchronous, while the GDI record replay loop is synchronous for performance.
|
|
248
|
-
|
|
249
|
-
### 9. World Transforms
|
|
250
|
-
|
|
251
|
-
EMF+ supports a full 2D affine transformation matrix. The converter maintains and composes transforms using standard matrix multiplication:
|
|
252
|
-
|
|
253
|
-
```
|
|
254
|
-
β β β β β β
|
|
255
|
-
β x_out β β a b 0 β β x β
|
|
256
|
-
β y_out β = β c d 0 β Γ β y β
|
|
257
|
-
β 1 β β e f 1 β β 1 β
|
|
258
|
-
β β β β β β
|
|
259
|
-
```
|
|
260
|
-
|
|
261
|
-
Stored as a 6-element tuple: `[a, b, c, d, e, f]`
|
|
262
|
-
|
|
263
|
-
Supported transform operations:
|
|
264
|
-
| Operation | Effect |
|
|
265
|
-
|-----------|--------|
|
|
266
|
-
| `SetWorldTransform` | Replace the current matrix |
|
|
267
|
-
| `ResetWorldTransform` | Reset to identity `[1,0,0,1,0,0]` |
|
|
268
|
-
| `MultiplyWorldTransform` | Pre- or post-multiply with another matrix |
|
|
269
|
-
| `TranslateWorldTransform` | Apply translation `(dx, dy)` |
|
|
270
|
-
| `ScaleWorldTransform` | Apply scaling `(sx, sy)` |
|
|
271
|
-
| `RotateWorldTransform` | Apply rotation by angle (degrees) |
|
|
272
|
-
|
|
273
|
-
Save/Restore operations push/pop the world transform onto a stack, allowing nested coordinate spaces.
|
|
274
|
-
|
|
275
|
-
---
|
|
276
|
-
|
|
277
|
-
## Supported Record Types
|
|
278
|
-
|
|
279
|
-
### EMF GDI Records
|
|
280
|
-
|
|
281
|
-
| Category | Records |
|
|
282
|
-
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
283
|
-
| **Header/Control** | `EMR_HEADER`, `EMR_EOF`, `EMR_COMMENT` |
|
|
284
|
-
| **State** | `EMR_SAVEDC`, `EMR_RESTOREDC`, `EMR_SETTEXTCOLOR`, `EMR_SETBKCOLOR`, `EMR_SETBKMODE`, `EMR_SETPOLYFILLMODE`, `EMR_SETTEXTALIGN`, `EMR_SETROP2`, `EMR_SETSTRETCHBLTMODE`, `EMR_SETMITERLIMIT` |
|
|
285
|
-
| **Transforms** | `EMR_SETWINDOWEXTEX`, `EMR_SETWINDOWORGEX`, `EMR_SETVIEWPORTEXTEX`, `EMR_SETVIEWPORTORGEX`, `EMR_SETMAPMODE`, `EMR_SCALEVIEWPORTEXTEX`, `EMR_SCALEWINDOWEXTEX`, `EMR_SETWORLDTRANSFORM`, `EMR_MODIFYWORLDTRANSFORM` |
|
|
286
|
-
| **Objects** | `EMR_CREATEPEN`, `EMR_EXTCREATEPEN`, `EMR_CREATEBRUSHINDIRECT`, `EMR_EXTCREATEFONTINDIRECTW`, `EMR_SELECTOBJECT`, `EMR_DELETEOBJECT` |
|
|
287
|
-
| **Shapes** | `EMR_MOVETOEX`, `EMR_LINETO`, `EMR_RECTANGLE`, `EMR_ROUNDRECT`, `EMR_ELLIPSE`, `EMR_ARC`, `EMR_ARCTO`, `EMR_CHORD`, `EMR_PIE` |
|
|
288
|
-
| **Poly/Path** | `EMR_POLYGON`, `EMR_POLYLINE`, `EMR_POLYBEZIER`, `EMR_POLYBEZIERTO`, `EMR_POLYLINETO`, `EMR_POLYGON16`, `EMR_POLYLINE16`, `EMR_POLYBEZIER16`, `EMR_POLYBEZIERTO16`, `EMR_POLYLINETO16`, `EMR_POLYPOLYGON`, `EMR_POLYPOLYGON16` |
|
|
289
|
-
| **Path Ops** | `EMR_BEGINPATH`, `EMR_ENDPATH`, `EMR_CLOSEFIGURE`, `EMR_FILLPATH`, `EMR_STROKEANDFILLPATH`, `EMR_STROKEPATH`, `EMR_SELECTCLIPPATH` |
|
|
290
|
-
| **Text** | `EMR_EXTTEXTOUTW` |
|
|
291
|
-
| **Bitmap** | `EMR_BITBLT`, `EMR_STRETCHDIBITS` |
|
|
292
|
-
| **Clipping** | `EMR_INTERSECTCLIPRECT` |
|
|
293
|
-
|
|
294
|
-
### EMF+ Records
|
|
295
|
-
|
|
296
|
-
| Category | Records |
|
|
297
|
-
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
298
|
-
| **Control** | `Header`, `EndOfFile`, `GetDC` |
|
|
299
|
-
| **Objects** | `Object` (Brush, Pen, Path, Font, Image, StringFormat, ImageAttributes) |
|
|
300
|
-
| **Shapes** | `FillRects`, `DrawRects`, `FillEllipse`, `DrawEllipse`, `FillPie`, `DrawPie`, `DrawArc`, `DrawLines`, `FillPolygon` |
|
|
301
|
-
| **Path** | `FillPath`, `DrawPath` |
|
|
302
|
-
| **Text** | `DrawString`, `DrawDriverString` |
|
|
303
|
-
| **Images** | `DrawImage`, `DrawImagePoints` |
|
|
304
|
-
| **Transforms** | `SetWorldTransform`, `ResetWorldTransform`, `MultiplyWorldTransform`, `TranslateWorldTransform`, `ScaleWorldTransform`, `RotateWorldTransform`, `SetPageTransform` |
|
|
305
|
-
| **State** | `Save`, `Restore`, `BeginContainerNoParams`, `EndContainer` |
|
|
306
|
-
| **Clipping** | `ResetClip`, `SetClipRect`, `SetClipPath`, `SetClipRegion` |
|
|
307
|
-
| **Hints** | `SetAntiAliasMode`, `SetTextRenderingHint`, `SetInterpolationMode`, `SetPixelOffsetMode`, `SetCompositingQuality` |
|
|
308
|
-
|
|
309
|
-
### WMF Records
|
|
310
|
-
|
|
311
|
-
| Category | Records |
|
|
312
|
-
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
313
|
-
| **Control** | `META_EOF` |
|
|
314
|
-
| **State** | `META_SAVEDC`, `META_RESTOREDC`, `META_SETWINDOWORG`, `META_SETWINDOWEXT`, `META_SETTEXTCOLOR`, `META_SETBKCOLOR`, `META_SETBKMODE`, `META_SETPOLYFILLMODE`, `META_SETTEXTALIGN`, `META_SETROP2` |
|
|
315
|
-
| **Objects** | `META_CREATEPENINDIRECT`, `META_CREATEBRUSHINDIRECT`, `META_CREATEFONTINDIRECT`, `META_SELECTOBJECT`, `META_DELETEOBJECT` |
|
|
316
|
-
| **Shapes** | `META_MOVETO`, `META_LINETO`, `META_RECTANGLE`, `META_ROUNDRECT`, `META_ELLIPSE`, `META_ARC`, `META_PIE`, `META_CHORD` |
|
|
317
|
-
| **Poly** | `META_POLYGON`, `META_POLYLINE`, `META_POLYPOLYGON` |
|
|
318
|
-
| **Text** | `META_TEXTOUT`, `META_EXTTEXTOUT` |
|
|
319
|
-
|
|
320
|
-
---
|
|
321
|
-
|
|
322
|
-
## File Structure Reference
|
|
323
|
-
|
|
324
|
-
```
|
|
325
|
-
src/
|
|
326
|
-
βββ index.ts # Barrel re-export of public API
|
|
327
|
-
βββ emf-converter.ts # Public API: convertEmfToDataUrl, convertWmfToDataUrl
|
|
328
|
-
βββ emf-types.ts # All TypeScript type definitions & state factories
|
|
329
|
-
βββ emf-constants.ts # Numeric constants for EMF/EMF+/WMF record types
|
|
330
|
-
βββ emf-logging.ts # Debug logging (toggle via DEBUG_EMF flag)
|
|
331
|
-
βββ emf-color-helpers.ts # COLORREF β hex, ARGB β rgba() conversions
|
|
332
|
-
βββ emf-canvas-helpers.ts # Canvas creation, styling, stock objects, UTF-16 reading
|
|
333
|
-
βββ emf-header-parser.ts # EMF & WMF binary header parsers
|
|
334
|
-
β
|
|
335
|
-
βββ emf-record-replay.ts # Main EMF GDI record loop & dispatcher
|
|
336
|
-
βββ emf-gdi-state-handlers.ts # GDI state: save/restore, color/mode settings
|
|
337
|
-
βββ emf-gdi-transform-handlers.ts # GDI coordinate system & world transform records
|
|
338
|
-
βββ emf-gdi-object-handlers.ts # GDI object creation, selection, deletion
|
|
339
|
-
βββ emf-gdi-draw-handlers.ts # GDI draw dispatcher (shapes + text/bitmap)
|
|
340
|
-
βββ emf-gdi-draw-shapes.ts # GDI shape drawing: lines, rects, ellipses, arcs
|
|
341
|
-
βββ emf-gdi-draw-text-bitmap.ts # GDI text output & bitmap block transfers
|
|
342
|
-
βββ emf-gdi-coord.ts # GDI coordinate mapping (gmx/gmy/gmw/gmh)
|
|
343
|
-
βββ emf-gdi-poly-path-handlers.ts # GDI polygon, polyline, bezier, path operations
|
|
344
|
-
βββ emf-gdi-polypolygon-helpers.ts # PolyPolygon specialised helpers
|
|
345
|
-
β
|
|
346
|
-
βββ emf-plus-replay.ts # EMF+ record loop & dispatcher
|
|
347
|
-
βββ emf-plus-object-parser.ts # EMF+ OBJECT record β type-specific parsers
|
|
348
|
-
βββ emf-plus-object-complex.ts # Complex object parsers: Pen, Image, Font
|
|
349
|
-
βββ emf-plus-draw-handlers.ts # EMF+ shape fill/draw handlers
|
|
350
|
-
βββ emf-plus-text-image-handlers.ts # EMF+ text, image, and path-based drawing
|
|
351
|
-
βββ emf-plus-state-handlers.ts # EMF+ transforms, save/restore, clipping
|
|
352
|
-
βββ emf-plus-path.ts # EMF+ path parsing & canvas replay
|
|
353
|
-
βββ emf-plus-read-helpers.ts # EMF+ compressed/float rect & point readers
|
|
354
|
-
βββ emf-plus-bitmap-decoder.ts # EMF+ GDI+ pixel format β BMP decoder
|
|
355
|
-
β
|
|
356
|
-
βββ emf-dib-decoder.ts # DIB header parsing & format dispatcher
|
|
357
|
-
βββ emf-dib-rle-decoder.ts # RLE4/RLE8 bitmap decompression
|
|
358
|
-
βββ emf-dib-uncompressed.ts # Uncompressed & bitfield DIB row decoder
|
|
359
|
-
β
|
|
360
|
-
βββ wmf-replay.ts # WMF record loop & dispatcher
|
|
361
|
-
βββ wmf-draw-handlers.ts # WMF drawing record handlers
|
|
362
|
-
β
|
|
363
|
-
βββ index.test.ts # Test suite
|
|
364
|
-
```
|
|
62
|
+
## Limitations
|
|
365
63
|
|
|
366
|
-
|
|
64
|
+
- **EMF+ region objects** are not parsed (no Canvas 2D equivalent for boolean region clipping).
|
|
65
|
+
- **Gradient brushes are simplified** β GDI+ linear/path gradients use the primary colour only.
|
|
66
|
+
- **No raster operations (ROP)** β `SetROP2` blend modes are not applied.
|
|
67
|
+
- **Limited clipping** β single rect/path clipping is supported; combined regions are not.
|
|
68
|
+
- **Safety limits** β output is clamped to 4096Γ4096; processing stops after 50,000 records (EMF/WMF) or 100,000 (EMF+).
|
|
69
|
+
- **Font rendering** uses the browser's font engine, so glyph metrics may differ from Windows GDI.
|
|
367
70
|
|
|
368
|
-
##
|
|
71
|
+
## License
|
|
369
72
|
|
|
370
|
-
-
|
|
371
|
-
- **Gradient brushes are simplified** β GDI+ `LinearGradient` and `PathGradient` brush types extract only the primary colour rather than rendering full multi-stop gradient fills. The Canvas 2D API does not have a direct equivalent for GDI+ path gradients.
|
|
372
|
-
- **No raster operations (ROP)** β `SetROP2` is acknowledged but GDI raster operation blending modes (XOR, NOT, AND, etc.) have no direct Canvas 2D equivalent and are not applied.
|
|
373
|
-
- **Limited clipping** β `IntersectClipRect` and `SelectClipPath` are supported. Complex GDI region clipping (combining multiple regions with union/intersect/exclude operations) is not, as Canvas 2D only supports a single clip path.
|
|
374
|
-
- **Maximum canvas size** β Output is clamped to 4096Γ4096 pixels to prevent excessive memory usage from malformed or very large metafiles.
|
|
375
|
-
- **Maximum record count** β Processing stops after 50,000 records (EMF/WMF) or 100,000 records (EMF+) as a safety limit to prevent infinite loops from malformed files.
|
|
376
|
-
- **Font rendering** β Text is rendered using the browser's font engine with CSS font matching, so glyph metrics and kerning may differ from the original Windows GDI text rendering.
|
|
377
|
-
- **No EMF spool records** β Print spoolerβspecific record types are not handled. These are only present in EMF files generated by the Windows print subsystem and are not relevant for embedded metafiles in Office documents.
|
|
378
|
-
- **Canvas API required** β The library needs either `OffscreenCanvas` (for Web Worker support) or `HTMLCanvasElement` to be available in the runtime environment. Pure Node.js without a canvas polyfill is not supported.
|
|
73
|
+
[Apache-2.0](LICENSE). Please keep the [`NOTICE`](NOTICE) file with redistributions.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "emf-converter",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.4.1",
|
|
4
4
|
"description": "Convert EMF/WMF metafile binaries to PNG data URLs using Canvas",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"canvas",
|
|
@@ -11,16 +11,15 @@
|
|
|
11
11
|
"windows-metafile",
|
|
12
12
|
"wmf"
|
|
13
13
|
],
|
|
14
|
-
"homepage": "https://github.com/ChristopherVR/
|
|
14
|
+
"homepage": "https://github.com/ChristopherVR/emf-converter",
|
|
15
15
|
"bugs": {
|
|
16
|
-
"url": "https://github.com/ChristopherVR/
|
|
16
|
+
"url": "https://github.com/ChristopherVR/emf-converter/issues"
|
|
17
17
|
},
|
|
18
18
|
"license": "Apache-2.0",
|
|
19
19
|
"author": "ChristopherVR",
|
|
20
20
|
"repository": {
|
|
21
21
|
"type": "git",
|
|
22
|
-
"url": "https://github.com/ChristopherVR/
|
|
23
|
-
"directory": "packages/emf-converter"
|
|
22
|
+
"url": "https://github.com/ChristopherVR/emf-converter.git"
|
|
24
23
|
},
|
|
25
24
|
"files": [
|
|
26
25
|
"dist/",
|