emf-converter 4.8.1 → 4.8.2

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
@@ -217,7 +217,7 @@ Everything is measured against output painted by Windows itself; `src/gdi-parity
217
217
  - **Colour adjustment and image effects**: all eleven EMF+ image effects are supported. Complete native tables cover every legal ColorCurve intensity. Deferred draws retain their effects. JPEG, GIF and TIFF have bundled decoders for environments without Canvas. HSL, tint, red-eye, blur, sharpen and GDI colour adjustment retain measured differences; see [the detailed limitations](docs/limitations.md) and the native fixture bounds.
218
218
  - **Pen transforms**: EMF+ pen transforms (uniform, nonuniform and skewed) match GDI+ exactly on the 17 pen-transform fixtures, including dashes, which GDI+ lays out along the path in world space at the untransformed pen width. Native fixtures verify dashes under both uniform and non-uniform scales. GDI+ rejects singular pen transforms; the converter ignores such malformed transform data. Square, round, diamond and arrow anchor caps use their native shapes; the new centered-cap fixtures match every pixel with and without antialiasing.
219
219
  - **Text**: ANSI, multi-string and small-text records now render; the Windows text fixtures retain a few glyph-edge differences and a `PolyTextOut` C1 control-glyph difference (under 0.1% of pixels; bounds in `src/emf-text-records.fixture.test.ts`). ANSI decoding uses the host's `TextDecoder` for common Windows code pages; Johab and OEM CP437 use bundled Windows mappings. Vertical `ETO_PDY` advances work with and without the `fonts` engine. Without `fonts`, SVG text measurements use deterministic estimates, so precise justification requires supplying the matching fonts.
220
- - **Wide pens and paths**: flat-capped GDI pens 7 px and wider can differ by a few pixels at round joins, and dashed wide Bezier curves follow `WidenPath` (which Windows' direct drawing does not quite match); at most 0.2% of pixels on the fixtures. `EMR_WIDENPATH` does not reproduce the extra inner join triangles GDI's own `WidenPath` emits (visible only when the widened outline is itself stroked). EMF+ 1-pixel antialiased lines can differ by one antialiasing sample at their ends, some closed widened outlines by one sample along an edge, and Inset or compound pens on closed figures are approximate.
220
+ - **Wide pens and paths**: flat-capped GDI pens 7 px and wider can differ by a few pixels at round joins, and dashed wide curves follow `WidenPath` (exact for round and flat caps at one logical unit per pixel; square caps differ slightly at path ends); at most 0.2% of pixels on the fixtures. `EMR_WIDENPATH` does not reproduce the extra inner join triangles GDI's own `WidenPath` emits (visible only when the widened outline is itself stroked). EMF+ 1-pixel antialiased lines can differ by one antialiasing sample at their ends, some closed widened outlines by one sample along an edge, and Inset or compound pens on closed figures are approximate.
221
221
  - **GM_COMPATIBLE recordings**: EMF files do not record the graphics mode, and Windows plays RoundRect, Arc, Chord, Pie and null-pen Ellipse records back differently from how a GM_COMPATIBLE application drew them on screen; the converter follows Windows' playback.
222
222
  - **Small EMF+ residuals**: rotated `HighQualityBicubic` `DrawImage` edge pixels (0.14%), one-level differences at exact half-level `Blend` knots, and a few pixels of a metafile nested in `DrawImage` under a scale.
223
223
  - **WMF**: `PS_INSIDEFRAME` boxes can come out a pixel short at non-integer scales, right-to-left (`LAYOUT_RTL`) layouts differ by single pixels on mirrored diagonals, and metric map modes assume a 96 dpi reference device (Windows derives them from the physical display, so its own output varies per machine).
package/dist/browser.js CHANGED
@@ -29228,9 +29228,9 @@ function perpendicularVectors(width, dx0, dy0) {
29228
29228
  const vx = rounded[0], vy = rounded[1];
29229
29229
  return { v: flip ? [-vx, -vy] : [vx, vy], ray: flip ? [-x, -y] : [x, y] };
29230
29230
  }
29231
- function squareExtension(width, dx, dy) {
29231
+ function squareExtension(width, dx, dy, scale = 1) {
29232
29232
  const len = Math.hypot(dx, dy);
29233
- const r = width / 2;
29233
+ const r = width / 2 * scale;
29234
29234
  return [Math.floor(dx / len * r + 0.5), Math.floor(dy / len * r + 0.5)];
29235
29235
  }
29236
29236
  function turnSign(ax, ay, bx, by) {
@@ -29269,7 +29269,7 @@ var Outliner = class {
29269
29269
  const [L, R] = drawVertices(this.pen, d[0], d[1]);
29270
29270
  const perpendicular = curve && drawDir ? drawDir : [dx, dy];
29271
29271
  const vectors = perpendicularVectors(this.opts.width, perpendicular[0], perpendicular[1]);
29272
- return { dx, dy, L, R, v: vectors.v, vRaw: vectors.ray, e: squareExtension(this.opts.width, perpendicular[0], perpendicular[1]), curveEnd: !!drawDir, tangentLength: drawDir ? drawDir[0] * drawDir[0] + drawDir[1] * drawDir[1] : 0, curve };
29272
+ return { dx, dy, L, R, v: vectors.v, vRaw: vectors.ray, e: squareExtension(this.opts.width, perpendicular[0], perpendicular[1]), pe: [perpendicular[0], perpendicular[1]], curveEnd: !!drawDir, tangentLength: drawDir ? drawDir[0] * drawDir[0] + drawDir[1] * drawDir[1] : 0, curve };
29273
29273
  }
29274
29274
  push(p, v) {
29275
29275
  this.pts.push([p[0] + v[0], p[1] + v[1]]);
@@ -29364,7 +29364,7 @@ var Outliner = class {
29364
29364
  return side === "R" ? s.v : [-s.v[0], -s.v[1]];
29365
29365
  }
29366
29366
  /** A cap at `p` from the `from` side of `s` round to the other side (`start`: around the back). */
29367
- cap(p, s, start) {
29367
+ cap(p, s, start, scale = 1) {
29368
29368
  const from = start ? "L" : "R";
29369
29369
  const to = start ? "R" : "L";
29370
29370
  const { cap } = this.opts;
@@ -29382,7 +29382,8 @@ var Outliner = class {
29382
29382
  const sv = this.capSide(s, from);
29383
29383
  const ev = this.capSide(s, to);
29384
29384
  if (cap === "square") {
29385
- const e = start ? [-s.e[0], -s.e[1]] : s.e;
29385
+ const e0 = scale === 1 ? s.e : squareExtension(this.opts.width, s.pe[0], s.pe[1], scale);
29386
+ const e = start ? [-e0[0], -e0[1]] : e0;
29386
29387
  this.push(p, [sv[0] + e[0], sv[1] + e[1]]);
29387
29388
  this.push(p, [ev[0] + e[0], ev[1] + e[1]]);
29388
29389
  } else {
@@ -29478,7 +29479,7 @@ var Outliner = class {
29478
29479
  * lets a single point stand for a zero-length dash along `dirs[0]`;
29479
29480
  * `draws` (curve end tangents) picks draw vertices only.
29480
29481
  */
29481
- open(P, dirs, draws, curves) {
29482
+ open(P, dirs, draws, curves, startScale = 1, endScale = 1) {
29482
29483
  if (P.length < 2 && !dirs?.[0]) {
29483
29484
  this.dot(P[0]);
29484
29485
  return;
@@ -29489,12 +29490,12 @@ var Outliner = class {
29489
29490
  }
29490
29491
  if (segs2.length === 0) {
29491
29492
  const s = this.seg(P[0], P[0], dirs?.[0], draws?.[0], curves?.[0]);
29492
- this.cap(P[0], s, true);
29493
- this.cap(P[0], s, false);
29493
+ this.cap(P[0], s, true, startScale);
29494
+ this.cap(P[0], s, false, endScale);
29494
29495
  this.flush();
29495
29496
  return;
29496
29497
  }
29497
- this.cap(P[0], segs2[0], true);
29498
+ this.cap(P[0], segs2[0], true, startScale);
29498
29499
  for (let i = 0; i + 1 < segs2.length; i++) {
29499
29500
  const s = segs2[i];
29500
29501
  const t = segs2[i + 1];
@@ -29505,7 +29506,7 @@ var Outliner = class {
29505
29506
  this.join(P[i + 1], s, t, "R", tr < 0);
29506
29507
  }
29507
29508
  }
29508
- this.cap(P[P.length - 1], segs2[segs2.length - 1], false);
29509
+ this.cap(P[P.length - 1], segs2[segs2.length - 1], false, endScale);
29509
29510
  for (let i = segs2.length - 1; i > 0; i--) {
29510
29511
  const s = segs2[i];
29511
29512
  const t = segs2[i - 1];
@@ -29561,7 +29562,7 @@ var Outliner = class {
29561
29562
  this.flush();
29562
29563
  }
29563
29564
  };
29564
- function dashPieces(P, tangents, pattern, shorten) {
29565
+ function dashPieces(P, tangents, curveFlags, pattern, shorten, wholePixelVectors) {
29565
29566
  const out = [];
29566
29567
  const dashes = [];
29567
29568
  for (let i = 0; i < pattern.length; i += 2) {
@@ -29576,12 +29577,22 @@ function dashPieces(P, tangents, pattern, shorten) {
29576
29577
  let idx = 0;
29577
29578
  let left = dashes[0];
29578
29579
  let on = true;
29579
- let cur = { pts: [P[0]], dirs: [], draws: [] };
29580
+ let cur = { pts: [P[0]], dirs: [], draws: [], curves: [], startScale: 1, endScale: 1 };
29580
29581
  for (let i = 0; i + 1 < P.length; i++) {
29581
29582
  const [x0, y0] = P[i];
29582
29583
  const [x1, y1] = P[i + 1];
29583
29584
  const dir = [x1 - x0, y1 - y0];
29584
- const len = Math.hypot(dir[0], dir[1]);
29585
+ const real = Math.hypot(dir[0], dir[1]);
29586
+ const len = wholePixelVectors ? Math.hypot(Math.floor(dir[0] / 16), Math.floor(dir[1] / 16)) * 16 : Math.hypot(dir[0], dir[1]);
29587
+ if (len === 0) {
29588
+ if (on && cur) {
29589
+ cur.pts.push(P[i + 1]);
29590
+ cur.dirs.push(dir);
29591
+ cur.draws.push(tangents[i]);
29592
+ cur.curves.push(curveFlags[i]);
29593
+ }
29594
+ continue;
29595
+ }
29585
29596
  let t = 0;
29586
29597
  while (len - t > left || left === 0 && on) {
29587
29598
  t += left;
@@ -29590,10 +29601,12 @@ function dashPieces(P, tangents, pattern, shorten) {
29590
29601
  cur.pts.push(q);
29591
29602
  cur.dirs.push(dir);
29592
29603
  cur.draws.push(tangents[i]);
29604
+ cur.curves.push(curveFlags[i]);
29605
+ cur.endScale = cur.pts[0] === P[0] && q[0] === P[0][0] && q[1] === P[0][1] ? 1 : real / len;
29593
29606
  out.push(cur);
29594
29607
  cur = null;
29595
29608
  } else {
29596
- cur = { pts: [q], dirs: [], draws: [] };
29609
+ cur = { pts: [q], dirs: [], draws: [], curves: [], startScale: real / len, endScale: 1 };
29597
29610
  }
29598
29611
  on = !on;
29599
29612
  idx = (idx + 1) % dashes.length;
@@ -29604,9 +29617,12 @@ function dashPieces(P, tangents, pattern, shorten) {
29604
29617
  cur.pts.push(P[i + 1]);
29605
29618
  cur.dirs.push(dir);
29606
29619
  cur.draws.push(tangents[i]);
29620
+ cur.curves.push(curveFlags[i]);
29621
+ cur.endScale = real / len;
29607
29622
  }
29608
29623
  }
29609
29624
  if (on && cur && cur.dirs.length > 0) {
29625
+ cur.endScale = 1;
29610
29626
  out.push(cur);
29611
29627
  }
29612
29628
  return out;
@@ -29641,10 +29657,13 @@ function widenPath(path, opts) {
29641
29657
  if (dashed) {
29642
29658
  const run = closed ? [...P, P[0]] : P;
29643
29659
  const shorten = opts.cap === "flat" || opts.shortenDashes === false ? 0 : opts.width;
29644
- for (const piece of dashPieces(run, closed ? [...dirs, void 0] : dirs, opts.dashes, shorten)) {
29660
+ const runTangents = closed ? [...dirs, void 0] : dirs;
29661
+ const runCurves = closed ? [...curves, !!fig.roundWiden] : curves;
29662
+ for (const piece of dashPieces(run, runTangents, runCurves, opts.dashes, shorten, !!opts.wholePixelDashVectors)) {
29645
29663
  const pts = [piece.pts[0]];
29646
29664
  const pdirs = [];
29647
29665
  const pdraws = [];
29666
+ const pcurves = [];
29648
29667
  for (let i = 1; i < piece.pts.length; i++) {
29649
29668
  const q = piece.pts[i];
29650
29669
  const last = pts[pts.length - 1];
@@ -29652,9 +29671,10 @@ function widenPath(path, opts) {
29652
29671
  pts.push(q);
29653
29672
  pdirs.push(piece.dirs[i - 1]);
29654
29673
  pdraws.push(piece.draws[i - 1]);
29674
+ pcurves.push(piece.curves[i - 1]);
29655
29675
  }
29656
29676
  }
29657
- outliner.open(pts, pdirs.length > 0 ? pdirs : [piece.dirs[0]], pdirs.length > 0 ? pdraws : [piece.draws[0]], pts.map(() => !!fig.roundWiden));
29677
+ outliner.open(pts, pdirs.length > 0 ? pdirs : [piece.dirs[0]], pdirs.length > 0 ? pdraws : [piece.draws[0]], pdirs.length > 0 ? pcurves : [piece.curves[0]], piece.startScale, piece.endScale);
29658
29678
  }
29659
29679
  } else if (closed && P.length >= 3) {
29660
29680
  outliner.closed(P, dirs, curves);
@@ -29862,7 +29882,10 @@ function penWidenOptions(rCtx, opts = {}) {
29862
29882
  join: opts.roundPen ? "round" : opts.rectangle && !state.penExtended ? "miter" : !state.penExtended || joinBits === 0 ? "round" : joinBits === 4096 ? "bevel" : "miter",
29863
29883
  miterLimit: state.miterLimit ?? 10,
29864
29884
  dashes: dashes ? dashes.map((v) => v * 16) : null,
29865
- shortenDashes: (flags & 15) !== 7
29885
+ shortenDashes: (flags & 15) !== 7,
29886
+ // One logical unit per device pixel, unrotated: GDI measures dash
29887
+ // segments in whole pixels.
29888
+ wholePixelDashVectors: Math.abs(matrix[0] - 1) < 1e-6 && Math.abs(matrix[3] - 1) < 1e-6 && Math.abs(matrix[1]) < 1e-6 && Math.abs(matrix[2]) < 1e-6
29866
29889
  };
29867
29890
  }
29868
29891
  function hatchBackgroundMask(hatch) {
package/dist/browser.mjs CHANGED
@@ -29226,9 +29226,9 @@ function perpendicularVectors(width, dx0, dy0) {
29226
29226
  const vx = rounded[0], vy = rounded[1];
29227
29227
  return { v: flip ? [-vx, -vy] : [vx, vy], ray: flip ? [-x, -y] : [x, y] };
29228
29228
  }
29229
- function squareExtension(width, dx, dy) {
29229
+ function squareExtension(width, dx, dy, scale = 1) {
29230
29230
  const len = Math.hypot(dx, dy);
29231
- const r = width / 2;
29231
+ const r = width / 2 * scale;
29232
29232
  return [Math.floor(dx / len * r + 0.5), Math.floor(dy / len * r + 0.5)];
29233
29233
  }
29234
29234
  function turnSign(ax, ay, bx, by) {
@@ -29267,7 +29267,7 @@ var Outliner = class {
29267
29267
  const [L, R] = drawVertices(this.pen, d[0], d[1]);
29268
29268
  const perpendicular = curve && drawDir ? drawDir : [dx, dy];
29269
29269
  const vectors = perpendicularVectors(this.opts.width, perpendicular[0], perpendicular[1]);
29270
- return { dx, dy, L, R, v: vectors.v, vRaw: vectors.ray, e: squareExtension(this.opts.width, perpendicular[0], perpendicular[1]), curveEnd: !!drawDir, tangentLength: drawDir ? drawDir[0] * drawDir[0] + drawDir[1] * drawDir[1] : 0, curve };
29270
+ return { dx, dy, L, R, v: vectors.v, vRaw: vectors.ray, e: squareExtension(this.opts.width, perpendicular[0], perpendicular[1]), pe: [perpendicular[0], perpendicular[1]], curveEnd: !!drawDir, tangentLength: drawDir ? drawDir[0] * drawDir[0] + drawDir[1] * drawDir[1] : 0, curve };
29271
29271
  }
29272
29272
  push(p, v) {
29273
29273
  this.pts.push([p[0] + v[0], p[1] + v[1]]);
@@ -29362,7 +29362,7 @@ var Outliner = class {
29362
29362
  return side === "R" ? s.v : [-s.v[0], -s.v[1]];
29363
29363
  }
29364
29364
  /** A cap at `p` from the `from` side of `s` round to the other side (`start`: around the back). */
29365
- cap(p, s, start) {
29365
+ cap(p, s, start, scale = 1) {
29366
29366
  const from = start ? "L" : "R";
29367
29367
  const to = start ? "R" : "L";
29368
29368
  const { cap } = this.opts;
@@ -29380,7 +29380,8 @@ var Outliner = class {
29380
29380
  const sv = this.capSide(s, from);
29381
29381
  const ev = this.capSide(s, to);
29382
29382
  if (cap === "square") {
29383
- const e = start ? [-s.e[0], -s.e[1]] : s.e;
29383
+ const e0 = scale === 1 ? s.e : squareExtension(this.opts.width, s.pe[0], s.pe[1], scale);
29384
+ const e = start ? [-e0[0], -e0[1]] : e0;
29384
29385
  this.push(p, [sv[0] + e[0], sv[1] + e[1]]);
29385
29386
  this.push(p, [ev[0] + e[0], ev[1] + e[1]]);
29386
29387
  } else {
@@ -29476,7 +29477,7 @@ var Outliner = class {
29476
29477
  * lets a single point stand for a zero-length dash along `dirs[0]`;
29477
29478
  * `draws` (curve end tangents) picks draw vertices only.
29478
29479
  */
29479
- open(P, dirs, draws, curves) {
29480
+ open(P, dirs, draws, curves, startScale = 1, endScale = 1) {
29480
29481
  if (P.length < 2 && !dirs?.[0]) {
29481
29482
  this.dot(P[0]);
29482
29483
  return;
@@ -29487,12 +29488,12 @@ var Outliner = class {
29487
29488
  }
29488
29489
  if (segs2.length === 0) {
29489
29490
  const s = this.seg(P[0], P[0], dirs?.[0], draws?.[0], curves?.[0]);
29490
- this.cap(P[0], s, true);
29491
- this.cap(P[0], s, false);
29491
+ this.cap(P[0], s, true, startScale);
29492
+ this.cap(P[0], s, false, endScale);
29492
29493
  this.flush();
29493
29494
  return;
29494
29495
  }
29495
- this.cap(P[0], segs2[0], true);
29496
+ this.cap(P[0], segs2[0], true, startScale);
29496
29497
  for (let i = 0; i + 1 < segs2.length; i++) {
29497
29498
  const s = segs2[i];
29498
29499
  const t = segs2[i + 1];
@@ -29503,7 +29504,7 @@ var Outliner = class {
29503
29504
  this.join(P[i + 1], s, t, "R", tr < 0);
29504
29505
  }
29505
29506
  }
29506
- this.cap(P[P.length - 1], segs2[segs2.length - 1], false);
29507
+ this.cap(P[P.length - 1], segs2[segs2.length - 1], false, endScale);
29507
29508
  for (let i = segs2.length - 1; i > 0; i--) {
29508
29509
  const s = segs2[i];
29509
29510
  const t = segs2[i - 1];
@@ -29559,7 +29560,7 @@ var Outliner = class {
29559
29560
  this.flush();
29560
29561
  }
29561
29562
  };
29562
- function dashPieces(P, tangents, pattern, shorten) {
29563
+ function dashPieces(P, tangents, curveFlags, pattern, shorten, wholePixelVectors) {
29563
29564
  const out = [];
29564
29565
  const dashes = [];
29565
29566
  for (let i = 0; i < pattern.length; i += 2) {
@@ -29574,12 +29575,22 @@ function dashPieces(P, tangents, pattern, shorten) {
29574
29575
  let idx = 0;
29575
29576
  let left = dashes[0];
29576
29577
  let on = true;
29577
- let cur = { pts: [P[0]], dirs: [], draws: [] };
29578
+ let cur = { pts: [P[0]], dirs: [], draws: [], curves: [], startScale: 1, endScale: 1 };
29578
29579
  for (let i = 0; i + 1 < P.length; i++) {
29579
29580
  const [x0, y0] = P[i];
29580
29581
  const [x1, y1] = P[i + 1];
29581
29582
  const dir = [x1 - x0, y1 - y0];
29582
- const len = Math.hypot(dir[0], dir[1]);
29583
+ const real = Math.hypot(dir[0], dir[1]);
29584
+ const len = wholePixelVectors ? Math.hypot(Math.floor(dir[0] / 16), Math.floor(dir[1] / 16)) * 16 : Math.hypot(dir[0], dir[1]);
29585
+ if (len === 0) {
29586
+ if (on && cur) {
29587
+ cur.pts.push(P[i + 1]);
29588
+ cur.dirs.push(dir);
29589
+ cur.draws.push(tangents[i]);
29590
+ cur.curves.push(curveFlags[i]);
29591
+ }
29592
+ continue;
29593
+ }
29583
29594
  let t = 0;
29584
29595
  while (len - t > left || left === 0 && on) {
29585
29596
  t += left;
@@ -29588,10 +29599,12 @@ function dashPieces(P, tangents, pattern, shorten) {
29588
29599
  cur.pts.push(q);
29589
29600
  cur.dirs.push(dir);
29590
29601
  cur.draws.push(tangents[i]);
29602
+ cur.curves.push(curveFlags[i]);
29603
+ cur.endScale = cur.pts[0] === P[0] && q[0] === P[0][0] && q[1] === P[0][1] ? 1 : real / len;
29591
29604
  out.push(cur);
29592
29605
  cur = null;
29593
29606
  } else {
29594
- cur = { pts: [q], dirs: [], draws: [] };
29607
+ cur = { pts: [q], dirs: [], draws: [], curves: [], startScale: real / len, endScale: 1 };
29595
29608
  }
29596
29609
  on = !on;
29597
29610
  idx = (idx + 1) % dashes.length;
@@ -29602,9 +29615,12 @@ function dashPieces(P, tangents, pattern, shorten) {
29602
29615
  cur.pts.push(P[i + 1]);
29603
29616
  cur.dirs.push(dir);
29604
29617
  cur.draws.push(tangents[i]);
29618
+ cur.curves.push(curveFlags[i]);
29619
+ cur.endScale = real / len;
29605
29620
  }
29606
29621
  }
29607
29622
  if (on && cur && cur.dirs.length > 0) {
29623
+ cur.endScale = 1;
29608
29624
  out.push(cur);
29609
29625
  }
29610
29626
  return out;
@@ -29639,10 +29655,13 @@ function widenPath(path, opts) {
29639
29655
  if (dashed) {
29640
29656
  const run = closed ? [...P, P[0]] : P;
29641
29657
  const shorten = opts.cap === "flat" || opts.shortenDashes === false ? 0 : opts.width;
29642
- for (const piece of dashPieces(run, closed ? [...dirs, void 0] : dirs, opts.dashes, shorten)) {
29658
+ const runTangents = closed ? [...dirs, void 0] : dirs;
29659
+ const runCurves = closed ? [...curves, !!fig.roundWiden] : curves;
29660
+ for (const piece of dashPieces(run, runTangents, runCurves, opts.dashes, shorten, !!opts.wholePixelDashVectors)) {
29643
29661
  const pts = [piece.pts[0]];
29644
29662
  const pdirs = [];
29645
29663
  const pdraws = [];
29664
+ const pcurves = [];
29646
29665
  for (let i = 1; i < piece.pts.length; i++) {
29647
29666
  const q = piece.pts[i];
29648
29667
  const last = pts[pts.length - 1];
@@ -29650,9 +29669,10 @@ function widenPath(path, opts) {
29650
29669
  pts.push(q);
29651
29670
  pdirs.push(piece.dirs[i - 1]);
29652
29671
  pdraws.push(piece.draws[i - 1]);
29672
+ pcurves.push(piece.curves[i - 1]);
29653
29673
  }
29654
29674
  }
29655
- outliner.open(pts, pdirs.length > 0 ? pdirs : [piece.dirs[0]], pdirs.length > 0 ? pdraws : [piece.draws[0]], pts.map(() => !!fig.roundWiden));
29675
+ outliner.open(pts, pdirs.length > 0 ? pdirs : [piece.dirs[0]], pdirs.length > 0 ? pdraws : [piece.draws[0]], pdirs.length > 0 ? pcurves : [piece.curves[0]], piece.startScale, piece.endScale);
29656
29676
  }
29657
29677
  } else if (closed && P.length >= 3) {
29658
29678
  outliner.closed(P, dirs, curves);
@@ -29860,7 +29880,10 @@ function penWidenOptions(rCtx, opts = {}) {
29860
29880
  join: opts.roundPen ? "round" : opts.rectangle && !state.penExtended ? "miter" : !state.penExtended || joinBits === 0 ? "round" : joinBits === 4096 ? "bevel" : "miter",
29861
29881
  miterLimit: state.miterLimit ?? 10,
29862
29882
  dashes: dashes ? dashes.map((v) => v * 16) : null,
29863
- shortenDashes: (flags & 15) !== 7
29883
+ shortenDashes: (flags & 15) !== 7,
29884
+ // One logical unit per device pixel, unrotated: GDI measures dash
29885
+ // segments in whole pixels.
29886
+ wholePixelDashVectors: Math.abs(matrix[0] - 1) < 1e-6 && Math.abs(matrix[3] - 1) < 1e-6 && Math.abs(matrix[1]) < 1e-6 && Math.abs(matrix[2]) < 1e-6
29864
29887
  };
29865
29888
  }
29866
29889
  function hatchBackgroundMask(hatch) {
package/dist/index.js CHANGED
@@ -29229,9 +29229,9 @@ function perpendicularVectors(width, dx0, dy0) {
29229
29229
  const vx = rounded[0], vy = rounded[1];
29230
29230
  return { v: flip ? [-vx, -vy] : [vx, vy], ray: flip ? [-x, -y] : [x, y] };
29231
29231
  }
29232
- function squareExtension(width, dx, dy) {
29232
+ function squareExtension(width, dx, dy, scale = 1) {
29233
29233
  const len = Math.hypot(dx, dy);
29234
- const r = width / 2;
29234
+ const r = width / 2 * scale;
29235
29235
  return [Math.floor(dx / len * r + 0.5), Math.floor(dy / len * r + 0.5)];
29236
29236
  }
29237
29237
  function turnSign(ax, ay, bx, by) {
@@ -29270,7 +29270,7 @@ var Outliner = class {
29270
29270
  const [L, R] = drawVertices(this.pen, d[0], d[1]);
29271
29271
  const perpendicular = curve && drawDir ? drawDir : [dx, dy];
29272
29272
  const vectors = perpendicularVectors(this.opts.width, perpendicular[0], perpendicular[1]);
29273
- return { dx, dy, L, R, v: vectors.v, vRaw: vectors.ray, e: squareExtension(this.opts.width, perpendicular[0], perpendicular[1]), curveEnd: !!drawDir, tangentLength: drawDir ? drawDir[0] * drawDir[0] + drawDir[1] * drawDir[1] : 0, curve };
29273
+ return { dx, dy, L, R, v: vectors.v, vRaw: vectors.ray, e: squareExtension(this.opts.width, perpendicular[0], perpendicular[1]), pe: [perpendicular[0], perpendicular[1]], curveEnd: !!drawDir, tangentLength: drawDir ? drawDir[0] * drawDir[0] + drawDir[1] * drawDir[1] : 0, curve };
29274
29274
  }
29275
29275
  push(p, v) {
29276
29276
  this.pts.push([p[0] + v[0], p[1] + v[1]]);
@@ -29365,7 +29365,7 @@ var Outliner = class {
29365
29365
  return side === "R" ? s.v : [-s.v[0], -s.v[1]];
29366
29366
  }
29367
29367
  /** A cap at `p` from the `from` side of `s` round to the other side (`start`: around the back). */
29368
- cap(p, s, start) {
29368
+ cap(p, s, start, scale = 1) {
29369
29369
  const from = start ? "L" : "R";
29370
29370
  const to = start ? "R" : "L";
29371
29371
  const { cap } = this.opts;
@@ -29383,7 +29383,8 @@ var Outliner = class {
29383
29383
  const sv = this.capSide(s, from);
29384
29384
  const ev = this.capSide(s, to);
29385
29385
  if (cap === "square") {
29386
- const e = start ? [-s.e[0], -s.e[1]] : s.e;
29386
+ const e0 = scale === 1 ? s.e : squareExtension(this.opts.width, s.pe[0], s.pe[1], scale);
29387
+ const e = start ? [-e0[0], -e0[1]] : e0;
29387
29388
  this.push(p, [sv[0] + e[0], sv[1] + e[1]]);
29388
29389
  this.push(p, [ev[0] + e[0], ev[1] + e[1]]);
29389
29390
  } else {
@@ -29479,7 +29480,7 @@ var Outliner = class {
29479
29480
  * lets a single point stand for a zero-length dash along `dirs[0]`;
29480
29481
  * `draws` (curve end tangents) picks draw vertices only.
29481
29482
  */
29482
- open(P, dirs, draws, curves) {
29483
+ open(P, dirs, draws, curves, startScale = 1, endScale = 1) {
29483
29484
  if (P.length < 2 && !dirs?.[0]) {
29484
29485
  this.dot(P[0]);
29485
29486
  return;
@@ -29490,12 +29491,12 @@ var Outliner = class {
29490
29491
  }
29491
29492
  if (segs2.length === 0) {
29492
29493
  const s = this.seg(P[0], P[0], dirs?.[0], draws?.[0], curves?.[0]);
29493
- this.cap(P[0], s, true);
29494
- this.cap(P[0], s, false);
29494
+ this.cap(P[0], s, true, startScale);
29495
+ this.cap(P[0], s, false, endScale);
29495
29496
  this.flush();
29496
29497
  return;
29497
29498
  }
29498
- this.cap(P[0], segs2[0], true);
29499
+ this.cap(P[0], segs2[0], true, startScale);
29499
29500
  for (let i = 0; i + 1 < segs2.length; i++) {
29500
29501
  const s = segs2[i];
29501
29502
  const t = segs2[i + 1];
@@ -29506,7 +29507,7 @@ var Outliner = class {
29506
29507
  this.join(P[i + 1], s, t, "R", tr < 0);
29507
29508
  }
29508
29509
  }
29509
- this.cap(P[P.length - 1], segs2[segs2.length - 1], false);
29510
+ this.cap(P[P.length - 1], segs2[segs2.length - 1], false, endScale);
29510
29511
  for (let i = segs2.length - 1; i > 0; i--) {
29511
29512
  const s = segs2[i];
29512
29513
  const t = segs2[i - 1];
@@ -29562,7 +29563,7 @@ var Outliner = class {
29562
29563
  this.flush();
29563
29564
  }
29564
29565
  };
29565
- function dashPieces(P, tangents, pattern, shorten) {
29566
+ function dashPieces(P, tangents, curveFlags, pattern, shorten, wholePixelVectors) {
29566
29567
  const out = [];
29567
29568
  const dashes = [];
29568
29569
  for (let i = 0; i < pattern.length; i += 2) {
@@ -29577,12 +29578,22 @@ function dashPieces(P, tangents, pattern, shorten) {
29577
29578
  let idx = 0;
29578
29579
  let left = dashes[0];
29579
29580
  let on = true;
29580
- let cur = { pts: [P[0]], dirs: [], draws: [] };
29581
+ let cur = { pts: [P[0]], dirs: [], draws: [], curves: [], startScale: 1, endScale: 1 };
29581
29582
  for (let i = 0; i + 1 < P.length; i++) {
29582
29583
  const [x0, y0] = P[i];
29583
29584
  const [x1, y1] = P[i + 1];
29584
29585
  const dir = [x1 - x0, y1 - y0];
29585
- const len = Math.hypot(dir[0], dir[1]);
29586
+ const real = Math.hypot(dir[0], dir[1]);
29587
+ const len = wholePixelVectors ? Math.hypot(Math.floor(dir[0] / 16), Math.floor(dir[1] / 16)) * 16 : Math.hypot(dir[0], dir[1]);
29588
+ if (len === 0) {
29589
+ if (on && cur) {
29590
+ cur.pts.push(P[i + 1]);
29591
+ cur.dirs.push(dir);
29592
+ cur.draws.push(tangents[i]);
29593
+ cur.curves.push(curveFlags[i]);
29594
+ }
29595
+ continue;
29596
+ }
29586
29597
  let t = 0;
29587
29598
  while (len - t > left || left === 0 && on) {
29588
29599
  t += left;
@@ -29591,10 +29602,12 @@ function dashPieces(P, tangents, pattern, shorten) {
29591
29602
  cur.pts.push(q);
29592
29603
  cur.dirs.push(dir);
29593
29604
  cur.draws.push(tangents[i]);
29605
+ cur.curves.push(curveFlags[i]);
29606
+ cur.endScale = cur.pts[0] === P[0] && q[0] === P[0][0] && q[1] === P[0][1] ? 1 : real / len;
29594
29607
  out.push(cur);
29595
29608
  cur = null;
29596
29609
  } else {
29597
- cur = { pts: [q], dirs: [], draws: [] };
29610
+ cur = { pts: [q], dirs: [], draws: [], curves: [], startScale: real / len, endScale: 1 };
29598
29611
  }
29599
29612
  on = !on;
29600
29613
  idx = (idx + 1) % dashes.length;
@@ -29605,9 +29618,12 @@ function dashPieces(P, tangents, pattern, shorten) {
29605
29618
  cur.pts.push(P[i + 1]);
29606
29619
  cur.dirs.push(dir);
29607
29620
  cur.draws.push(tangents[i]);
29621
+ cur.curves.push(curveFlags[i]);
29622
+ cur.endScale = real / len;
29608
29623
  }
29609
29624
  }
29610
29625
  if (on && cur && cur.dirs.length > 0) {
29626
+ cur.endScale = 1;
29611
29627
  out.push(cur);
29612
29628
  }
29613
29629
  return out;
@@ -29642,10 +29658,13 @@ function widenPath(path, opts) {
29642
29658
  if (dashed) {
29643
29659
  const run = closed ? [...P, P[0]] : P;
29644
29660
  const shorten = opts.cap === "flat" || opts.shortenDashes === false ? 0 : opts.width;
29645
- for (const piece of dashPieces(run, closed ? [...dirs, void 0] : dirs, opts.dashes, shorten)) {
29661
+ const runTangents = closed ? [...dirs, void 0] : dirs;
29662
+ const runCurves = closed ? [...curves, !!fig.roundWiden] : curves;
29663
+ for (const piece of dashPieces(run, runTangents, runCurves, opts.dashes, shorten, !!opts.wholePixelDashVectors)) {
29646
29664
  const pts = [piece.pts[0]];
29647
29665
  const pdirs = [];
29648
29666
  const pdraws = [];
29667
+ const pcurves = [];
29649
29668
  for (let i = 1; i < piece.pts.length; i++) {
29650
29669
  const q = piece.pts[i];
29651
29670
  const last = pts[pts.length - 1];
@@ -29653,9 +29672,10 @@ function widenPath(path, opts) {
29653
29672
  pts.push(q);
29654
29673
  pdirs.push(piece.dirs[i - 1]);
29655
29674
  pdraws.push(piece.draws[i - 1]);
29675
+ pcurves.push(piece.curves[i - 1]);
29656
29676
  }
29657
29677
  }
29658
- outliner.open(pts, pdirs.length > 0 ? pdirs : [piece.dirs[0]], pdirs.length > 0 ? pdraws : [piece.draws[0]], pts.map(() => !!fig.roundWiden));
29678
+ outliner.open(pts, pdirs.length > 0 ? pdirs : [piece.dirs[0]], pdirs.length > 0 ? pdraws : [piece.draws[0]], pdirs.length > 0 ? pcurves : [piece.curves[0]], piece.startScale, piece.endScale);
29659
29679
  }
29660
29680
  } else if (closed && P.length >= 3) {
29661
29681
  outliner.closed(P, dirs, curves);
@@ -29863,7 +29883,10 @@ function penWidenOptions(rCtx, opts = {}) {
29863
29883
  join: opts.roundPen ? "round" : opts.rectangle && !state.penExtended ? "miter" : !state.penExtended || joinBits === 0 ? "round" : joinBits === 4096 ? "bevel" : "miter",
29864
29884
  miterLimit: state.miterLimit ?? 10,
29865
29885
  dashes: dashes ? dashes.map((v) => v * 16) : null,
29866
- shortenDashes: (flags & 15) !== 7
29886
+ shortenDashes: (flags & 15) !== 7,
29887
+ // One logical unit per device pixel, unrotated: GDI measures dash
29888
+ // segments in whole pixels.
29889
+ wholePixelDashVectors: Math.abs(matrix[0] - 1) < 1e-6 && Math.abs(matrix[3] - 1) < 1e-6 && Math.abs(matrix[1]) < 1e-6 && Math.abs(matrix[2]) < 1e-6
29867
29890
  };
29868
29891
  }
29869
29892
  function hatchBackgroundMask(hatch) {
package/dist/index.mjs CHANGED
@@ -29227,9 +29227,9 @@ function perpendicularVectors(width, dx0, dy0) {
29227
29227
  const vx = rounded[0], vy = rounded[1];
29228
29228
  return { v: flip ? [-vx, -vy] : [vx, vy], ray: flip ? [-x, -y] : [x, y] };
29229
29229
  }
29230
- function squareExtension(width, dx, dy) {
29230
+ function squareExtension(width, dx, dy, scale = 1) {
29231
29231
  const len = Math.hypot(dx, dy);
29232
- const r = width / 2;
29232
+ const r = width / 2 * scale;
29233
29233
  return [Math.floor(dx / len * r + 0.5), Math.floor(dy / len * r + 0.5)];
29234
29234
  }
29235
29235
  function turnSign(ax, ay, bx, by) {
@@ -29268,7 +29268,7 @@ var Outliner = class {
29268
29268
  const [L, R] = drawVertices(this.pen, d[0], d[1]);
29269
29269
  const perpendicular = curve && drawDir ? drawDir : [dx, dy];
29270
29270
  const vectors = perpendicularVectors(this.opts.width, perpendicular[0], perpendicular[1]);
29271
- return { dx, dy, L, R, v: vectors.v, vRaw: vectors.ray, e: squareExtension(this.opts.width, perpendicular[0], perpendicular[1]), curveEnd: !!drawDir, tangentLength: drawDir ? drawDir[0] * drawDir[0] + drawDir[1] * drawDir[1] : 0, curve };
29271
+ return { dx, dy, L, R, v: vectors.v, vRaw: vectors.ray, e: squareExtension(this.opts.width, perpendicular[0], perpendicular[1]), pe: [perpendicular[0], perpendicular[1]], curveEnd: !!drawDir, tangentLength: drawDir ? drawDir[0] * drawDir[0] + drawDir[1] * drawDir[1] : 0, curve };
29272
29272
  }
29273
29273
  push(p, v) {
29274
29274
  this.pts.push([p[0] + v[0], p[1] + v[1]]);
@@ -29363,7 +29363,7 @@ var Outliner = class {
29363
29363
  return side === "R" ? s.v : [-s.v[0], -s.v[1]];
29364
29364
  }
29365
29365
  /** A cap at `p` from the `from` side of `s` round to the other side (`start`: around the back). */
29366
- cap(p, s, start) {
29366
+ cap(p, s, start, scale = 1) {
29367
29367
  const from = start ? "L" : "R";
29368
29368
  const to = start ? "R" : "L";
29369
29369
  const { cap } = this.opts;
@@ -29381,7 +29381,8 @@ var Outliner = class {
29381
29381
  const sv = this.capSide(s, from);
29382
29382
  const ev = this.capSide(s, to);
29383
29383
  if (cap === "square") {
29384
- const e = start ? [-s.e[0], -s.e[1]] : s.e;
29384
+ const e0 = scale === 1 ? s.e : squareExtension(this.opts.width, s.pe[0], s.pe[1], scale);
29385
+ const e = start ? [-e0[0], -e0[1]] : e0;
29385
29386
  this.push(p, [sv[0] + e[0], sv[1] + e[1]]);
29386
29387
  this.push(p, [ev[0] + e[0], ev[1] + e[1]]);
29387
29388
  } else {
@@ -29477,7 +29478,7 @@ var Outliner = class {
29477
29478
  * lets a single point stand for a zero-length dash along `dirs[0]`;
29478
29479
  * `draws` (curve end tangents) picks draw vertices only.
29479
29480
  */
29480
- open(P, dirs, draws, curves) {
29481
+ open(P, dirs, draws, curves, startScale = 1, endScale = 1) {
29481
29482
  if (P.length < 2 && !dirs?.[0]) {
29482
29483
  this.dot(P[0]);
29483
29484
  return;
@@ -29488,12 +29489,12 @@ var Outliner = class {
29488
29489
  }
29489
29490
  if (segs2.length === 0) {
29490
29491
  const s = this.seg(P[0], P[0], dirs?.[0], draws?.[0], curves?.[0]);
29491
- this.cap(P[0], s, true);
29492
- this.cap(P[0], s, false);
29492
+ this.cap(P[0], s, true, startScale);
29493
+ this.cap(P[0], s, false, endScale);
29493
29494
  this.flush();
29494
29495
  return;
29495
29496
  }
29496
- this.cap(P[0], segs2[0], true);
29497
+ this.cap(P[0], segs2[0], true, startScale);
29497
29498
  for (let i = 0; i + 1 < segs2.length; i++) {
29498
29499
  const s = segs2[i];
29499
29500
  const t = segs2[i + 1];
@@ -29504,7 +29505,7 @@ var Outliner = class {
29504
29505
  this.join(P[i + 1], s, t, "R", tr < 0);
29505
29506
  }
29506
29507
  }
29507
- this.cap(P[P.length - 1], segs2[segs2.length - 1], false);
29508
+ this.cap(P[P.length - 1], segs2[segs2.length - 1], false, endScale);
29508
29509
  for (let i = segs2.length - 1; i > 0; i--) {
29509
29510
  const s = segs2[i];
29510
29511
  const t = segs2[i - 1];
@@ -29560,7 +29561,7 @@ var Outliner = class {
29560
29561
  this.flush();
29561
29562
  }
29562
29563
  };
29563
- function dashPieces(P, tangents, pattern, shorten) {
29564
+ function dashPieces(P, tangents, curveFlags, pattern, shorten, wholePixelVectors) {
29564
29565
  const out = [];
29565
29566
  const dashes = [];
29566
29567
  for (let i = 0; i < pattern.length; i += 2) {
@@ -29575,12 +29576,22 @@ function dashPieces(P, tangents, pattern, shorten) {
29575
29576
  let idx = 0;
29576
29577
  let left = dashes[0];
29577
29578
  let on = true;
29578
- let cur = { pts: [P[0]], dirs: [], draws: [] };
29579
+ let cur = { pts: [P[0]], dirs: [], draws: [], curves: [], startScale: 1, endScale: 1 };
29579
29580
  for (let i = 0; i + 1 < P.length; i++) {
29580
29581
  const [x0, y0] = P[i];
29581
29582
  const [x1, y1] = P[i + 1];
29582
29583
  const dir = [x1 - x0, y1 - y0];
29583
- const len = Math.hypot(dir[0], dir[1]);
29584
+ const real = Math.hypot(dir[0], dir[1]);
29585
+ const len = wholePixelVectors ? Math.hypot(Math.floor(dir[0] / 16), Math.floor(dir[1] / 16)) * 16 : Math.hypot(dir[0], dir[1]);
29586
+ if (len === 0) {
29587
+ if (on && cur) {
29588
+ cur.pts.push(P[i + 1]);
29589
+ cur.dirs.push(dir);
29590
+ cur.draws.push(tangents[i]);
29591
+ cur.curves.push(curveFlags[i]);
29592
+ }
29593
+ continue;
29594
+ }
29584
29595
  let t = 0;
29585
29596
  while (len - t > left || left === 0 && on) {
29586
29597
  t += left;
@@ -29589,10 +29600,12 @@ function dashPieces(P, tangents, pattern, shorten) {
29589
29600
  cur.pts.push(q);
29590
29601
  cur.dirs.push(dir);
29591
29602
  cur.draws.push(tangents[i]);
29603
+ cur.curves.push(curveFlags[i]);
29604
+ cur.endScale = cur.pts[0] === P[0] && q[0] === P[0][0] && q[1] === P[0][1] ? 1 : real / len;
29592
29605
  out.push(cur);
29593
29606
  cur = null;
29594
29607
  } else {
29595
- cur = { pts: [q], dirs: [], draws: [] };
29608
+ cur = { pts: [q], dirs: [], draws: [], curves: [], startScale: real / len, endScale: 1 };
29596
29609
  }
29597
29610
  on = !on;
29598
29611
  idx = (idx + 1) % dashes.length;
@@ -29603,9 +29616,12 @@ function dashPieces(P, tangents, pattern, shorten) {
29603
29616
  cur.pts.push(P[i + 1]);
29604
29617
  cur.dirs.push(dir);
29605
29618
  cur.draws.push(tangents[i]);
29619
+ cur.curves.push(curveFlags[i]);
29620
+ cur.endScale = real / len;
29606
29621
  }
29607
29622
  }
29608
29623
  if (on && cur && cur.dirs.length > 0) {
29624
+ cur.endScale = 1;
29609
29625
  out.push(cur);
29610
29626
  }
29611
29627
  return out;
@@ -29640,10 +29656,13 @@ function widenPath(path, opts) {
29640
29656
  if (dashed) {
29641
29657
  const run = closed ? [...P, P[0]] : P;
29642
29658
  const shorten = opts.cap === "flat" || opts.shortenDashes === false ? 0 : opts.width;
29643
- for (const piece of dashPieces(run, closed ? [...dirs, void 0] : dirs, opts.dashes, shorten)) {
29659
+ const runTangents = closed ? [...dirs, void 0] : dirs;
29660
+ const runCurves = closed ? [...curves, !!fig.roundWiden] : curves;
29661
+ for (const piece of dashPieces(run, runTangents, runCurves, opts.dashes, shorten, !!opts.wholePixelDashVectors)) {
29644
29662
  const pts = [piece.pts[0]];
29645
29663
  const pdirs = [];
29646
29664
  const pdraws = [];
29665
+ const pcurves = [];
29647
29666
  for (let i = 1; i < piece.pts.length; i++) {
29648
29667
  const q = piece.pts[i];
29649
29668
  const last = pts[pts.length - 1];
@@ -29651,9 +29670,10 @@ function widenPath(path, opts) {
29651
29670
  pts.push(q);
29652
29671
  pdirs.push(piece.dirs[i - 1]);
29653
29672
  pdraws.push(piece.draws[i - 1]);
29673
+ pcurves.push(piece.curves[i - 1]);
29654
29674
  }
29655
29675
  }
29656
- outliner.open(pts, pdirs.length > 0 ? pdirs : [piece.dirs[0]], pdirs.length > 0 ? pdraws : [piece.draws[0]], pts.map(() => !!fig.roundWiden));
29676
+ outliner.open(pts, pdirs.length > 0 ? pdirs : [piece.dirs[0]], pdirs.length > 0 ? pdraws : [piece.draws[0]], pdirs.length > 0 ? pcurves : [piece.curves[0]], piece.startScale, piece.endScale);
29657
29677
  }
29658
29678
  } else if (closed && P.length >= 3) {
29659
29679
  outliner.closed(P, dirs, curves);
@@ -29861,7 +29881,10 @@ function penWidenOptions(rCtx, opts = {}) {
29861
29881
  join: opts.roundPen ? "round" : opts.rectangle && !state.penExtended ? "miter" : !state.penExtended || joinBits === 0 ? "round" : joinBits === 4096 ? "bevel" : "miter",
29862
29882
  miterLimit: state.miterLimit ?? 10,
29863
29883
  dashes: dashes ? dashes.map((v) => v * 16) : null,
29864
- shortenDashes: (flags & 15) !== 7
29884
+ shortenDashes: (flags & 15) !== 7,
29885
+ // One logical unit per device pixel, unrotated: GDI measures dash
29886
+ // segments in whole pixels.
29887
+ wholePixelDashVectors: Math.abs(matrix[0] - 1) < 1e-6 && Math.abs(matrix[3] - 1) < 1e-6 && Math.abs(matrix[1]) < 1e-6 && Math.abs(matrix[2]) < 1e-6
29865
29888
  };
29866
29889
  }
29867
29890
  function hatchBackgroundMask(hatch) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "emf-converter",
3
- "version": "4.8.1",
3
+ "version": "4.8.2",
4
4
  "description": "Convert EMF/WMF metafile binaries to PNG data URLs or SVG (markup, data URL, React/JSX)",
5
5
  "keywords": [
6
6
  "canvas",