emf-converter 1.5.0 → 2.0.0

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 CHANGED
@@ -44,22 +44,20 @@ const pngDataUrl = await convertEmfToDataUrl(emfBuffer);
44
44
  const wmfPng = await convertWmfToDataUrl(wmfBuffer);
45
45
 
46
46
  // Optional: limit output dimensions (aspect ratio preserved)
47
- const scaled = await convertEmfToDataUrl(emfBuffer, 1024, 768);
47
+ const scaled = await convertEmfToDataUrl(emfBuffer, { maxWidth: 1024, maxHeight: 768 });
48
48
  ```
49
49
 
50
50
  Both functions return `Promise<string | null>` — `null` if the buffer is invalid or no Canvas API is available.
51
51
 
52
52
  ## API
53
53
 
54
- ### `convertEmfToDataUrl(buffer, maxWidth?, maxHeight?, options?)` · `convertWmfToDataUrl(buffer, maxWidth?, maxHeight?, options?)`
54
+ ### `convertEmfToDataUrl(buffer, options?)` · `convertWmfToDataUrl(buffer, options?)`
55
55
 
56
- | Parameter | Type | Description |
57
- | ----------- | ------------------------------------- | ---------------------------------------------------- |
58
- | `buffer` | `ArrayBuffer` | Raw EMF/WMF file bytes |
59
- | `maxWidth` | `number` (optional) | Maximum output width in pixels |
60
- | `maxHeight` | `number` (optional) | Maximum output height in pixels |
61
- | `options` | `EmfConvertOptions \| number` (opt.) | Options object, or a numeric `dpiScale` (legacy) |
62
- | **Returns** | `Promise<string \| null>` | PNG data URL or `null` on failure |
56
+ | Parameter | Type | Description |
57
+ | ----------- | ----------------------------- | ---------------------------------------------------- |
58
+ | `buffer` | `ArrayBuffer` | Raw EMF/WMF file bytes |
59
+ | `options` | `EmfConvertOptions` (optional)| Output size, DPI scale, record limits, font mapping |
60
+ | **Returns** | `Promise<string \| null>` | PNG data URL or `null` on failure |
63
61
 
64
62
  #### `EmfConvertOptions`
65
63
 
@@ -73,7 +71,7 @@ Both functions return `Promise<string | null>` — `null` if the buffer is inval
73
71
  | `fontFamilyMap` | `Record<string, string>` | — | Maps Windows face names (case-insensitive) to fonts available locally, e.g. `{ calibri: 'Carlito' }` |
74
72
 
75
73
  ```ts
76
- const png = await convertEmfToDataUrl(buffer, undefined, undefined, {
74
+ const png = await convertEmfToDataUrl(buffer, {
77
75
  dpiScale: 2,
78
76
  fontFamilyMap: { calibri: 'Carlito', 'ms shell dlg': 'Tahoma' },
79
77
  });
@@ -83,13 +81,19 @@ const png = await convertEmfToDataUrl(buffer, undefined, undefined, {
83
81
 
84
82
  A three-phase pipeline: **parse → replay → export**. The header parser extracts the drawing bounds, a Canvas is created and clamped to 8192×8192 (configurable via `maxCanvasDimension`), 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.
85
83
 
86
- 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.
84
+ 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, gradients, raster operations, and clipping:
85
+
86
+ - **Clip regions with full boolean combine modes** — the converter tracks the active clip as a list of path shapes, so `Intersect`, `Union`, `Xor`, `Exclude`, `Complement`, and `Replace` combine modes work for `EMR_INTERSECTCLIPRECT` / `EMR_EXCLUDECLIPRECT` / `EMR_EXTSELECTCLIPRGN` (all `RGN_*` modes), the EMF+ `SetClipRect` / `SetClipPath` / `SetClipRegion` records (all `CombineMode` values, including nested region-node trees), and clip translation via `EMR_OFFSETCLIPRGN` / EMF+ `OffsetClip`. Subtraction and symmetric difference are expressed through even-odd fill-rule clipping, which Canvas 2D cannot do with plain `clip()` stacking.
87
+ - **Gradient brushes** — GDI+ linear gradients render as Canvas linear gradients with their full colour-stop list (preset blend colours and blend factors are expanded into stops, and the optional brush transform rotates the gradient axis). Path gradients render as radial gradients from the centre colour to the surrounding colour across the boundary radius.
88
+ - **Raster operations (ROP2)** — every `SetROP2` mode is mapped: `R2_BLACK`, `R2_WHITE`, `R2_NOP`, `R2_COPYPEN`, `R2_NOTCOPYPEN`, and `R2_NOT` are emulated exactly (via colour inversion and `difference` compositing); the remaining bitwise AND/OR/XOR-family modes are approximated with the nearest arithmetic composite (`difference` / `darken` / `lighten`), combined with pen-colour inversion for the NOT variants.
89
+ - **GDI world transforms** — the scale and translation set by `EMR_SETWORLDTRANSFORM` / `EMR_MODIFYWORLDTRANSFORM` are applied to all GDI drawing, which is required for GDI+-exported EMF files (they record coordinates at 16× sub-pixel precision with a compensating transform). EMF+ records support the full affine transform set.
87
90
 
88
91
  ## Limitations
89
92
 
90
- - **Region boolean ops are partial** — rectangle, path, and union-of-rectangles regions (multi-rect `RGNDATA`, EMF+ rect/path region trees) are clipped correctly, but `Xor` / `Exclude` / `Complement` region operations have no Canvas 2D equivalent and fall back to intersect-or-skip. `EMR_EXCLUDECLIPRECT` and `EMR_OFFSETCLIPRGN` are recognised but not applied (Canvas 2D cannot subtract from or translate an active clip).
91
- - **Gradient brushes are simplified** — GDI+ linear/path gradient brushes render with their primary colour only (no interpolated colour stops yet).
92
- - **Raster operations (ROP2) are partial** — `SetROP2` modes `R2_COPYPEN` (default) and `R2_NOP` are faithful; `R2_XORPEN`, `R2_MASKPEN`, `R2_MERGEPEN`, and `R2_NOT` are approximated via Canvas composite modes (`xor` / `multiply` / `lighten` / `difference`). The bitwise NOT/NAND/NOR-family modes have no Canvas equivalent and fall back to normal source-over drawing.
93
+ - **Approximated edge cases in region ops** — all six combine modes are exact while the tracked clip is at most one composable shape (the overwhelmingly common case). When the clip is already an intersection of several shapes, or was set from a live path bracket (`EMR_SELECTCLIPPATH`), `Union` / `Xor` / `Complement` degrade to the nearest conservative approximation (a console log notes when this happens).
94
+ - **Bitwise ROP2 modes are arithmetic approximations** — Canvas compositing cannot reproduce true bitwise AND/OR/XOR against the destination, so those modes use `darken` / `lighten` / `difference` stand-ins; only the modes listed above as exact are pixel-faithful.
95
+ - **Gradient details** — gradient wrap/tile modes clamp instead of tiling, path gradients are radial approximations of the true boundary-shaped falloff, and texture (image) brushes fall back to solid black.
96
+ - **GDI rotation/skew** — rotation and shear components of the *GDI* world transform are ignored (EMF+ transforms are unaffected); plain-GDI metafiles using rotated world transforms are rare.
93
97
  - **Safety limits** — output is clamped to 8192×8192 and replay stops after 200,000 records (EMF/WMF) or 500,000 (EMF+). All three are overridable via `maxCanvasDimension` / `maxRecords`.
94
98
  - **Font rendering** uses the host Canvas font engine, so glyph metrics may differ from Windows GDI. Weight, italic, underline, and strike-out are honoured; supply `fontFamilyMap` to remap Windows face names to fonts available in your environment.
95
99
 
package/dist/index.d.mts CHANGED
@@ -58,14 +58,12 @@ interface EmfConvertOptions {
58
58
  * - The logical bounds are zero-sized or negative.
59
59
  * - No canvas API is available (e.g. headless test environment).
60
60
  *
61
- * @param buffer - The raw EMF file bytes.
62
- * @param maxWidth - Optional cap on the output canvas width (pixels).
63
- * @param maxHeight - Optional cap on the output canvas height (pixels).
64
- * @param optionsOrDpiScale - Either an {@link EmfConvertOptions} object, or
65
- * a numeric DPI scale (for backward compatibility). Default DPI scale is 2.
61
+ * @param buffer - The raw EMF file bytes.
62
+ * @param options - Optional {@link EmfConvertOptions} controlling output size,
63
+ * DPI scale, record limits, and font mapping.
66
64
  * @returns A `data:image/png;base64,…` string, or `null` on failure.
67
65
  */
68
- declare function convertEmfToDataUrl(buffer: ArrayBuffer, maxWidth?: number, maxHeight?: number, optionsOrDpiScale?: EmfConvertOptions | number, recursionDepth?: number): Promise<string | null>;
66
+ declare function convertEmfToDataUrl(buffer: ArrayBuffer, options?: EmfConvertOptions, recursionDepth?: number): Promise<string | null>;
69
67
  /**
70
68
  * Converts a WMF (Windows Metafile) binary buffer to a PNG data-URL string.
71
69
  *
@@ -80,14 +78,12 @@ declare function convertEmfToDataUrl(buffer: ArrayBuffer, maxWidth?: number, max
80
78
  * - The header cannot be parsed or reports invalid dimensions.
81
79
  * - No canvas API is available.
82
80
  *
83
- * @param buffer - The raw WMF file bytes.
84
- * @param maxWidth - Optional cap on the output canvas width (pixels).
85
- * @param maxHeight - Optional cap on the output canvas height (pixels).
86
- * @param optionsOrDpiScale - Either an {@link EmfConvertOptions} object, or
87
- * a numeric DPI scale. Default DPI scale is 2.
81
+ * @param buffer - The raw WMF file bytes.
82
+ * @param options - Optional {@link EmfConvertOptions} controlling output size,
83
+ * DPI scale, record limits, and font mapping.
88
84
  * @returns A `data:image/png;base64,…` string, or `null` on failure.
89
85
  */
90
- declare function convertWmfToDataUrl(buffer: ArrayBuffer, maxWidth?: number, maxHeight?: number, optionsOrDpiScale?: EmfConvertOptions | number, recursionDepth?: number): Promise<string | null>;
86
+ declare function convertWmfToDataUrl(buffer: ArrayBuffer, options?: EmfConvertOptions, recursionDepth?: number): Promise<string | null>;
91
87
 
92
88
  /**
93
89
  * Canvas creation, styling, string reading, stock objects, and export helpers.
package/dist/index.d.ts CHANGED
@@ -58,14 +58,12 @@ interface EmfConvertOptions {
58
58
  * - The logical bounds are zero-sized or negative.
59
59
  * - No canvas API is available (e.g. headless test environment).
60
60
  *
61
- * @param buffer - The raw EMF file bytes.
62
- * @param maxWidth - Optional cap on the output canvas width (pixels).
63
- * @param maxHeight - Optional cap on the output canvas height (pixels).
64
- * @param optionsOrDpiScale - Either an {@link EmfConvertOptions} object, or
65
- * a numeric DPI scale (for backward compatibility). Default DPI scale is 2.
61
+ * @param buffer - The raw EMF file bytes.
62
+ * @param options - Optional {@link EmfConvertOptions} controlling output size,
63
+ * DPI scale, record limits, and font mapping.
66
64
  * @returns A `data:image/png;base64,…` string, or `null` on failure.
67
65
  */
68
- declare function convertEmfToDataUrl(buffer: ArrayBuffer, maxWidth?: number, maxHeight?: number, optionsOrDpiScale?: EmfConvertOptions | number, recursionDepth?: number): Promise<string | null>;
66
+ declare function convertEmfToDataUrl(buffer: ArrayBuffer, options?: EmfConvertOptions, recursionDepth?: number): Promise<string | null>;
69
67
  /**
70
68
  * Converts a WMF (Windows Metafile) binary buffer to a PNG data-URL string.
71
69
  *
@@ -80,14 +78,12 @@ declare function convertEmfToDataUrl(buffer: ArrayBuffer, maxWidth?: number, max
80
78
  * - The header cannot be parsed or reports invalid dimensions.
81
79
  * - No canvas API is available.
82
80
  *
83
- * @param buffer - The raw WMF file bytes.
84
- * @param maxWidth - Optional cap on the output canvas width (pixels).
85
- * @param maxHeight - Optional cap on the output canvas height (pixels).
86
- * @param optionsOrDpiScale - Either an {@link EmfConvertOptions} object, or
87
- * a numeric DPI scale. Default DPI scale is 2.
81
+ * @param buffer - The raw WMF file bytes.
82
+ * @param options - Optional {@link EmfConvertOptions} controlling output size,
83
+ * DPI scale, record limits, and font mapping.
88
84
  * @returns A `data:image/png;base64,…` string, or `null` on failure.
89
85
  */
90
- declare function convertWmfToDataUrl(buffer: ArrayBuffer, maxWidth?: number, maxHeight?: number, optionsOrDpiScale?: EmfConvertOptions | number, recursionDepth?: number): Promise<string | null>;
86
+ declare function convertWmfToDataUrl(buffer: ArrayBuffer, options?: EmfConvertOptions, recursionDepth?: number): Promise<string | null>;
91
87
 
92
88
  /**
93
89
  * Canvas creation, styling, string reading, stock objects, and export helpers.