emf-converter 1.4.2 → 1.5.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
@@ -51,29 +51,47 @@ Both functions return `Promise<string | null>` — `null` if the buffer is inval
51
51
 
52
52
  ## API
53
53
 
54
- ### `convertEmfToDataUrl(buffer, maxWidth?, maxHeight?)` · `convertWmfToDataUrl(buffer, maxWidth?, maxHeight?)`
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
- | **Returns** | `Promise<string \| null>` | PNG data URL or `null` on failure |
54
+ ### `convertEmfToDataUrl(buffer, maxWidth?, maxHeight?, options?)` · `convertWmfToDataUrl(buffer, maxWidth?, maxHeight?, options?)`
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 |
63
+
64
+ #### `EmfConvertOptions`
65
+
66
+ | Field | Type | Default | Description |
67
+ | -------------------- | -------------------------- | -------------- | --------------------------------------------------------------------------- |
68
+ | `maxWidth` | `number` | — | Maximum output width in pixels (aspect ratio preserved) |
69
+ | `maxHeight` | `number` | — | Maximum output height in pixels |
70
+ | `dpiScale` | `number` | `1` | Resolution multiplier for sharper output; clamped to `4` |
71
+ | `maxCanvasDimension` | `number` | `8192` | Hard cap on canvas width/height in pixels |
72
+ | `maxRecords` | `number` | `200000`/`500000` | Cap on records processed per stream before replay stops (EMF+ uses the higher default unless overridden) |
73
+ | `fontFamilyMap` | `Record<string, string>` | — | Maps Windows face names (case-insensitive) to fonts available locally, e.g. `{ calibri: 'Carlito' }` |
74
+
75
+ ```ts
76
+ const png = await convertEmfToDataUrl(buffer, undefined, undefined, {
77
+ dpiScale: 2,
78
+ fontFamilyMap: { calibri: 'Carlito', 'ms shell dlg': 'Tahoma' },
79
+ });
80
+ ```
62
81
 
63
82
  ## How it works
64
83
 
65
- 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.
84
+ 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.
66
85
 
67
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.
68
87
 
69
88
  ## Limitations
70
89
 
71
- - **EMF+ region objects** are not parsed (no Canvas 2D equivalent for boolean region clipping).
72
- - **Gradient brushes are simplified** — GDI+ linear/path gradients use the primary colour only.
73
- - **No raster operations (ROP)** — `SetROP2` blend modes are not applied.
74
- - **Limited clipping** — single rect/path clipping is supported; combined regions are not.
75
- - **Safety limits** — output is clamped to 4096×4096; processing stops after 50,000 records (EMF/WMF) or 100,000 (EMF+).
76
- - **Font rendering** uses the browser's font engine, so glyph metrics may differ from Windows GDI.
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
+ - **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
+ - **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.
77
95
 
78
96
  ## License
79
97
 
package/dist/index.d.mts CHANGED
@@ -25,6 +25,23 @@ interface EmfConvertOptions {
25
25
  * Values above 4 are clamped to 4 to prevent excessive memory usage.
26
26
  */
27
27
  dpiScale?: number;
28
+ /**
29
+ * Hard cap on the output canvas width/height in pixels. Guards against
30
+ * pathological metafiles. Defaults to {@link MAX_CANVAS_DIMENSION} (8192).
31
+ */
32
+ maxCanvasDimension?: number;
33
+ /**
34
+ * Maximum number of records processed per stream before replay stops.
35
+ * Defaults to 200,000 for GDI/WMF and 500,000 for the finer-grained EMF+
36
+ * stream. Supplying a value overrides both with the same cap.
37
+ */
38
+ maxRecords?: number;
39
+ /**
40
+ * Optional map from a (case-insensitive) Windows face name to a CSS font
41
+ * family available in the rendering environment, e.g. `{ calibri: 'Carlito',
42
+ * 'ms shell dlg': 'Tahoma' }`. Applied to GDI, WMF, and EMF+ text.
43
+ */
44
+ fontFamilyMap?: Record<string, string>;
28
45
  }
29
46
  /**
30
47
  * Converts an EMF (Enhanced Metafile) binary buffer to a PNG data-URL string
package/dist/index.d.ts CHANGED
@@ -25,6 +25,23 @@ interface EmfConvertOptions {
25
25
  * Values above 4 are clamped to 4 to prevent excessive memory usage.
26
26
  */
27
27
  dpiScale?: number;
28
+ /**
29
+ * Hard cap on the output canvas width/height in pixels. Guards against
30
+ * pathological metafiles. Defaults to {@link MAX_CANVAS_DIMENSION} (8192).
31
+ */
32
+ maxCanvasDimension?: number;
33
+ /**
34
+ * Maximum number of records processed per stream before replay stops.
35
+ * Defaults to 200,000 for GDI/WMF and 500,000 for the finer-grained EMF+
36
+ * stream. Supplying a value overrides both with the same cap.
37
+ */
38
+ maxRecords?: number;
39
+ /**
40
+ * Optional map from a (case-insensitive) Windows face name to a CSS font
41
+ * family available in the rendering environment, e.g. `{ calibri: 'Carlito',
42
+ * 'ms shell dlg': 'Tahoma' }`. Applied to GDI, WMF, and EMF+ text.
43
+ */
44
+ fontFamilyMap?: Record<string, string>;
28
45
  }
29
46
  /**
30
47
  * Converts an EMF (Enhanced Metafile) binary buffer to a PNG data-URL string