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.
Files changed (2) hide show
  1. package/README.md +35 -340
  2. package/package.json +4 -5
package/README.md CHANGED
@@ -1,378 +1,73 @@
1
1
  # emf-converter
2
2
 
3
- 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.
3
+ [![npm version](https://img.shields.io/npm/v/emf-converter.svg)](https://www.npmjs.com/package/emf-converter)
4
+ [![license](https://img.shields.io/npm/l/emf-converter.svg)](https://github.com/ChristopherVR/pptx-viewer/blob/main/LICENSE)
4
5
 
5
- ## Table of Contents
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
- - [emf-converter](#emf-converter)
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
- ## Overview
16
+ <samp>**[πŸ“¦ npm](https://www.npmjs.com/package/emf-converter)** Β· **[πŸ“– Full docs](https://christophervr.github.io/pptx-viewer/)**</samp>
40
17
 
41
- Windows Metafiles (EMF/WMF) are vector image formats that store a sequence of GDI (Graphics Device Interface) drawing commands. They are commonly embedded inside Office documents (PPTX, DOCX) and legacy Windows applications. This converter reads the raw binary data, interprets each record, and replays the drawing operations onto an HTML5 Canvas to produce a rasterised PNG.
18
+ ---
42
19
 
43
- The library handles three distinct formats:
20
+ ## Install
44
21
 
45
- | Format | Description | Record Size | Coordinate System |
46
- | -------- | ------------------------------ | ------------------- | ----------------------- |
47
- | **WMF** | Windows Metafile (16-bit) | 16-bit word-aligned | Window/viewport mapping |
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 Start
28
+ ## Quick start
54
29
 
55
30
  ```typescript
56
- import { convertEmfToDataUrl, convertWmfToDataUrl } from "emf-converter";
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>` β€” they return `null` if the buffer is invalid or no canvas API is available.
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
- ### `convertWmfToDataUrl(buffer, maxWidth?, maxHeight?)`
45
+ ## API
89
46
 
90
- Converts a WMF binary buffer to a PNG data URL.
47
+ ### `convertEmfToDataUrl(buffer, maxWidth?, maxHeight?)` Β· `convertWmfToDataUrl(buffer, maxWidth?, maxHeight?)`
91
48
 
92
49
  | Parameter | Type | Description |
93
50
  | ----------- | ------------------------- | --------------------------------- |
94
- | `buffer` | `ArrayBuffer` | The raw WMF file bytes |
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
- | Module | Handles |
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
- ### 4. EMF+ Record Replay
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
- EMF+ records are embedded inside `EMR_COMMENT` records, identified by the signature `0x2B464D45` ("EMF+" in little-endian). Each EMF+ record has a 12-byte header:
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
- ## Limitations
71
+ ## License
369
72
 
370
- - **No EMF+ region objects** β€” `EMFPLUS_OBJECTTYPE_REGION` is not parsed. Region objects define complex clipping areas via boolean operations on shapes; GDI+ regions have no direct Canvas 2D equivalent.
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.23",
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/pptx-viewer",
14
+ "homepage": "https://github.com/ChristopherVR/emf-converter",
15
15
  "bugs": {
16
- "url": "https://github.com/ChristopherVR/pptx-viewer/issues"
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/pptx-viewer.git",
23
- "directory": "packages/emf-converter"
22
+ "url": "https://github.com/ChristopherVR/emf-converter.git"
24
23
  },
25
24
  "files": [
26
25
  "dist/",