emf-converter 3.0.2 → 3.1.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.
Files changed (4) hide show
  1. package/README.md +4 -2
  2. package/dist/index.js +1483 -409
  3. package/dist/index.mjs +1483 -409
  4. package/package.json +1 -1
package/README.md CHANGED
@@ -109,7 +109,8 @@ A three-phase pipeline: **parse → replay → export**. The header parser extra
109
109
  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:
110
110
 
111
111
  - **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.
112
- - **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.
112
+ - **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 or shears the gradient axis). `WrapMode` tiling (`Tile` / `TileFlipX` / `TileFlipY` / `TileFlipXY`) is supported for a linear gradient at any angle, by unrolling one period into a single hard-stopped Canvas gradient spanning the drawing surface. Path gradients render the true boundary-shaped falloff (fanned into triangles from the centre point, with per-vertex surround colours, blend curves, preset colours, and focus scales), not a radial approximation, rasterised into a tiled `CanvasPattern` for all five `WrapMode` values. Verified against real GDI+ fixtures under `src/__fixtures__/gdi/`; see [Limitations](#limitations) for the residual, measured mismatch in flip-mode tiling.
113
+ - **Raster operations (ROP3)**: `EMR_BITBLT`, `EMR_STRETCHBLT`, and `EMR_STRETCHDIBITS` (and `PatBlt`-style brush-only fills) evaluate all 256 ROP3 codes exactly, per pixel and per bit, against the real destination, brush, and (when present) source pixels, matching Windows GDI bit-for-bit rather than approximating with Canvas composite modes.
113
114
  - **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.
114
115
  - **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.
115
116
 
@@ -117,7 +118,8 @@ It supports 300+ EMF GDI record types, the EMF+ (GDI+) record set, and legacy WM
117
118
 
118
119
  - **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).
119
120
  - **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.
120
- - **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.
121
+ - **Gradient tiling has a small, measured residual, worst in flip modes**: linear-gradient tiling is pixel-exact except for float colour rounding at period seams (measured against real GDI+ output, worst case 1.46% of pixels differing by up to 11 levels on one channel, for `InterpolationColors` preset stops; a handful of individual seam pixels can differ by more where a hard colour step falls exactly on a device pixel boundary). Path-gradient tiling carries a larger residual in `TileFlipX/Y/XY` modes (measured 1.7-6.5% of pixels for the fixtures in `src/__fixtures__/gdi/grad-path-*`, worse for a boundary whose gradient centre is off-centre in its own bounding box, such as an explicit `CenterPoint`): the renderer supersamples a device-resolution raster tile and mirrors it geometrically, while GDI+ mirrors its own already-rasterised tile with a small, undocumented sub-pixel lag at the seam. `Clamp` mode (no tiling) is closest to exact, typically under 1.2% (edge anti-aliasing only). See `src/gdi-parity.fixture.test.ts` for the exact, per-fixture tolerances this is regression-tested against.
122
+ - **Texture (image) brushes fall back to solid black**: brush type 2 (`EmfPlusTextureBrushData`, MS-EMFPLUS 2.2.2.45) is not parsed at all and falls through to the same default-black handling as any unrecognised brush type; the embedded bitmap and its `WrapMode` are not decoded into a Canvas pattern.
121
123
  - **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.
122
124
  - **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`.
123
125
  - **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.