emf-converter 4.8.6 → 4.8.8

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/dist/index.mjs CHANGED
@@ -27797,10 +27797,10 @@ function parseEmfHeader(view) {
27797
27797
  };
27798
27798
  }
27799
27799
  function getRenderableEmfBounds(header) {
27800
- const boundsW = header.bounds.right - header.bounds.left;
27801
- const boundsH = header.bounds.bottom - header.bounds.top;
27800
+ const boundsW = header.bounds.right - header.bounds.left + 1;
27801
+ const boundsH = header.bounds.bottom - header.bounds.top + 1;
27802
27802
  if (boundsW > 0 && boundsH > 0) {
27803
- return header.bounds;
27803
+ return { ...header.bounds, right: header.bounds.right + 1, bottom: header.bounds.bottom + 1 };
27804
27804
  }
27805
27805
  if (header.frameW > 0 && header.frameH > 0) {
27806
27806
  emfLog(
@@ -30832,6 +30832,13 @@ function insetFixBox(box, inset) {
30832
30832
  }
30833
30833
  return framed;
30834
30834
  }
30835
+ function sameEllipseRay(l, t, r, b, sx, sy, ex, ey) {
30836
+ const ax = BigInt(2 * sx - l - r);
30837
+ const ay = BigInt(2 * sy - t - b);
30838
+ const bx = BigInt(2 * ex - l - r);
30839
+ const by = BigInt(2 * ey - t - b);
30840
+ return ax * by === ay * bx && ax * bx + ay * by > 0n;
30841
+ }
30835
30842
  function handleSetPixelV(rCtx, dataOff, recSize) {
30836
30843
  const { ctx, view } = rCtx;
30837
30844
  if (recSize >= 20) {
@@ -31189,7 +31196,9 @@ function handleArcFamily(rCtx, recType, dataOff, recSize) {
31189
31196
  const rx = Math.abs(r - l) / 2;
31190
31197
  const ry = Math.abs(b - t) / 2;
31191
31198
  const startAngle = Math.atan2((startY - cyA) / (ry || 1), (startX - cxA) / (rx || 1));
31192
- const endAngle = Math.atan2((endY - cyA) / (ry || 1), (endX - cxA) / (rx || 1));
31199
+ const clockwise = state.arcDirection === 2;
31200
+ const fullEllipse = recType !== EMR_PIE && rx > 0 && ry > 0 && sameEllipseRay(l, t, r, b, startX, startY, endX, endY);
31201
+ const endAngle = fullEllipse ? startAngle + (clockwise ? 2 : -2) * Math.PI : Math.atan2((endY - cyA) / (ry || 1), (endX - cxA) / (rx || 1));
31193
31202
  const rotated = hasWorldRotation(rCtx);
31194
31203
  const unframed = fixBox(rCtx, l, t, r, b);
31195
31204
  const inset = insideFrameInset(rCtx, unframed);
@@ -31203,13 +31212,18 @@ function handleArcFamily(rCtx, recType, dataOff, recSize) {
31203
31212
  };
31204
31213
  const isArcTo = recType === EMR_ARCTO;
31205
31214
  const needsFill = recType === EMR_PIE || recType === EMR_CHORD;
31206
- const clockwise = state.arcDirection === 2;
31207
31215
  const startPoint = rotated ? gmapPoint(rCtx, cxA + rx * Math.cos(startAngle), cyA + ry * Math.sin(startAngle)) : { x: params.cx + params.rx * Math.cos(startAngle), y: params.cy + params.ry * Math.sin(startAngle) };
31208
31216
  const build = (c) => {
31209
31217
  if (recType === EMR_PIE) {
31210
31218
  c.moveTo(params.cx, params.cy);
31219
+ } else if (!isArcTo && inPath) {
31220
+ c.moveTo(startPoint.x, startPoint.y);
31211
31221
  }
31212
31222
  if (isArcTo) {
31223
+ if (!inPath) {
31224
+ const from = currentFix(rCtx);
31225
+ c.moveTo(from[0] / 16, from[1] / 16);
31226
+ }
31213
31227
  c.lineTo(startPoint.x, startPoint.y);
31214
31228
  }
31215
31229
  c.ellipse(params.cx, params.cy, params.rx, params.ry, params.rotation, startAngle, endAngle, !clockwise);
@@ -31229,7 +31243,9 @@ function handleArcFamily(rCtx, recType, dataOff, recSize) {
31229
31243
  const rasterArgs = (immediate = false) => ({
31230
31244
  box: inset ? framed : immediate && needsFill ? curvedFixBox(rCtx, l, t, r, b) : unframed,
31231
31245
  s: radialOnBox(fixPoint(rCtx, startX, startY)),
31232
- e: radialOnBox(fixPoint(rCtx, endX, endY)),
31246
+ // Device FIX rounding can separate distinct radial points on the
31247
+ // same logical ray. Preserve their proven endpoint identity.
31248
+ e: fullEllipse ? radialOnBox(fixPoint(rCtx, startX, startY)) : radialOnBox(fixPoint(rCtx, endX, endY)),
31233
31249
  from: currentFix(rCtx)
31234
31250
  });
31235
31251
  if (inPath) {
@@ -31817,40 +31833,121 @@ function halftoneReduceTaps(srcLen, dstLen, reverse) {
31817
31833
  if (reverse) taps.reverse();
31818
31834
  return taps;
31819
31835
  }
31820
- var ENLARGE_KERNEL = [
31821
- 0.7598,
31822
- 0.7276,
31823
- 0.6829,
31824
- 0.6528,
31825
- 0.6106,
31826
- 0.5745,
31827
- 0.5362,
31828
- 0.4955,
31829
- 0.457,
31830
- 0.4121,
31831
- 0.3762,
31832
- 0.3233,
31833
- 0.2741,
31834
- 0.226,
31835
- 0.1822,
31836
- 0.1412,
31837
- 0.1021,
31838
- 0.0685,
31839
- 0.0382,
31840
- 0.0138,
31836
+ var ENLARGE_INNER = [
31837
+ 1e3,
31838
+ 992.47,
31839
+ 984.9399,
31840
+ 977.4099,
31841
+ 969.8798,
31842
+ 962.3498,
31843
+ 954.8198,
31844
+ 947.2897,
31845
+ 939.7597,
31846
+ 932.2296,
31847
+ 924.6996,
31848
+ 917.1695,
31849
+ 909.6395,
31850
+ 902.1095,
31851
+ 894.5665,
31852
+ 886.8087,
31853
+ 879.045,
31854
+ 871.2813,
31855
+ 863.4349,
31856
+ 855.5885,
31857
+ 847.7421,
31858
+ 839.8269,
31859
+ 831.9046,
31860
+ 823.912,
31861
+ 815.9193,
31862
+ 807.9266,
31863
+ 799.847,
31864
+ 791.7283,
31865
+ 783.6096,
31866
+ 775.449,
31867
+ 767.3204,
31868
+ 758.9233,
31869
+ 750.5263,
31870
+ 742.4271,
31871
+ 734.0547,
31872
+ 725.6823,
31873
+ 717.232,
31874
+ 708.7675,
31875
+ 700.2393,
31876
+ 691.7111,
31877
+ 683.0734,
31878
+ 674.4357,
31879
+ 665.7105,
31880
+ 656.9854,
31881
+ 648.2603,
31882
+ 639.3747,
31883
+ 630.4873,
31884
+ 621.5157,
31885
+ 612.5442
31886
+ ];
31887
+ var ENLARGE_OUTER = [
31888
+ 375.1388,
31889
+ 364.2093,
31890
+ 353.2797,
31891
+ 342.4714,
31892
+ 331.7615,
31893
+ 321.1462,
31894
+ 310.6456,
31895
+ 300.2252,
31896
+ 289.9229,
31897
+ 279.7227,
31898
+ 269.6439,
31899
+ 259.6535,
31900
+ 249.7825,
31901
+ 240.0362,
31902
+ 230.3856,
31903
+ 220.8799,
31904
+ 211.4594,
31905
+ 202.1844,
31906
+ 192.9996,
31907
+ 183.9679,
31908
+ 175.1064,
31909
+ 166.2864,
31910
+ 157.6436,
31911
+ 149.1447,
31912
+ 140.7609,
31913
+ 132.5474,
31914
+ 124.4802,
31915
+ 116.5636,
31916
+ 108.778,
31917
+ 101.1602,
31918
+ 93.7098,
31919
+ 86.4419,
31920
+ 79.3251,
31921
+ 72.4091,
31922
+ 65.6853,
31923
+ 59.1472,
31924
+ 52.812,
31925
+ 46.6978,
31926
+ 40.8001,
31927
+ 35.1449,
31928
+ 29.7494,
31929
+ 24.6096,
31930
+ 19.7977,
31931
+ 15.2801,
31932
+ 11.1442,
31933
+ 7.3916,
31934
+ 4.1415,
31935
+ 1.4039,
31841
31936
  0
31842
31937
  ];
31843
- var ENLARGE_BOX = 0.2429;
31844
- var ENLARGE_STEP = 0.05;
31938
+ var ENLARGE_HALF = 500.0106;
31939
+ var ENLARGE_KNOTS = 96;
31845
31940
  function enlargeKernel(u) {
31846
- const edge = u / ENLARGE_STEP;
31847
- const i = Math.floor(edge);
31848
- const smooth = i >= ENLARGE_KERNEL.length - 1 ? 0 : ENLARGE_KERNEL[i] + (ENLARGE_KERNEL[i + 1] - ENLARGE_KERNEL[i]) * (edge - i);
31849
- return smooth + ENLARGE_BOX * (Math.abs(u - 0.5) < 1e-9 ? 0.5 : u < 0.5 ? 1 : 0);
31941
+ if (u >= 1) return 0;
31942
+ if (Math.abs(u - 0.5) < 1e-9) return ENLARGE_HALF;
31943
+ const table = u < 0.5 ? ENLARGE_INNER : ENLARGE_OUTER;
31944
+ const edge = u * ENLARGE_KNOTS - (u < 0.5 ? 0 : ENLARGE_KNOTS / 2);
31945
+ const i = Math.min(table.length - 2, Math.floor(edge + 1e-9));
31946
+ return table[i] + (table[i + 1] - table[i]) * (edge - i);
31850
31947
  }
31851
31948
  function halftoneEnlargeTaps(src, dst) {
31852
31949
  const ratio = dst / src;
31853
- const reach = Math.ceil((ENLARGE_KERNEL.length - 1) * ENLARGE_STEP * ratio);
31950
+ const reach = Math.ceil(ratio) - 1;
31854
31951
  const fir = [];
31855
31952
  let total = 0;
31856
31953
  for (let k = 0; k <= reach; k++) {
@@ -31879,7 +31976,17 @@ function halftoneEnlargeTaps(src, dst) {
31879
31976
  weights.set(j, (weights.get(j) ?? 0) + w * v);
31880
31977
  }
31881
31978
  }
31882
- taps.push([...weights.entries()]);
31979
+ const sorted = [...weights.entries()].sort((a, b) => a[0] - b[0]);
31980
+ let prev = 0;
31981
+ let cum = 0;
31982
+ const row2 = [];
31983
+ for (let i = 0; i < sorted.length; i++) {
31984
+ cum += sorted[i][1] * 8192;
31985
+ const c = i === sorted.length - 1 ? 8192 : Math.ceil(cum - 1e-7);
31986
+ row2.push([sorted[i][0], (c - prev) / 8192]);
31987
+ prev = c;
31988
+ }
31989
+ taps.push(row2);
31883
31990
  }
31884
31991
  return taps;
31885
31992
  }
@@ -32096,8 +32203,10 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
32096
32203
  const sampleW = nativeMixed ? Math.min(SW, W) : W;
32097
32204
  const sampleH = nativeMixed ? Math.min(SH, H) : H;
32098
32205
  const axis = nativeMixed ? (n, d, r) => halftoneReduceTaps(n, d, r) : (n, d, r) => halftoneAxis(0, n, d, r);
32099
- const cols = axis(SW, sampleW, flipX);
32100
- const rows = axis(SH, sampleH, flipY);
32206
+ const enlargeX = nativeMixed && W > SW;
32207
+ const enlargeY = nativeMixed && H > SH;
32208
+ const cols = axis(SW, sampleW, flipX && !enlargeX);
32209
+ const rows = axis(SH, sampleH, flipY && !enlargeY);
32101
32210
  if (nearestMixed) {
32102
32211
  const nearest = (taps, src2, dst, reverse) => {
32103
32212
  for (let i = 0; i < dst; i++) {
@@ -32206,6 +32315,8 @@ function stretchHalftone(src, sx, sy, sw, sh, dw, dh, adjust, adjustAfterSamplin
32206
32315
  return halftoneEnlargeTaps(src2, dst);
32207
32316
  };
32208
32317
  const xt = axisTaps(sampleW, W), yt = axisTaps(sampleH, H);
32318
+ if (enlargeX && flipX) xt.reverse();
32319
+ if (enlargeY && flipY) yt.reverse();
32209
32320
  for (let y = 0; y < H; y++) {
32210
32321
  for (let x = 0; x < W; x++) {
32211
32322
  for (let c = 0; c < 3; c++) {
@@ -35600,14 +35711,20 @@ function handleEmfObjectRecord(rCtx, recType, dataOff, recSize) {
35600
35711
  }
35601
35712
 
35602
35713
  // src/emf-gdi-transform-handlers.ts
35714
+ function extentsSettable(rCtx) {
35715
+ return rCtx.mapMode === 7 || rCtx.mapMode === 8;
35716
+ }
35603
35717
  function handleCoordinateRecord(rCtx, recType, dataOff, recSize) {
35604
35718
  const { view } = rCtx;
35605
35719
  switch (recType) {
35606
35720
  case EMR_SETWINDOWEXTEX: {
35607
- if (recSize >= 16) {
35608
- rCtx.windowExt.cx = view.getInt32(dataOff, true);
35609
- rCtx.windowExt.cy = view.getInt32(dataOff + 4, true);
35610
- activateGdiMappingMode(rCtx);
35721
+ if (recSize >= 16 && extentsSettable(rCtx)) {
35722
+ const cx = view.getInt32(dataOff, true);
35723
+ const cy = view.getInt32(dataOff + 4, true);
35724
+ if (cx !== 0 && cy !== 0) {
35725
+ rCtx.windowExt = { cx, cy };
35726
+ activateGdiMappingMode(rCtx);
35727
+ }
35611
35728
  }
35612
35729
  return true;
35613
35730
  }
@@ -35620,10 +35737,13 @@ function handleCoordinateRecord(rCtx, recType, dataOff, recSize) {
35620
35737
  return true;
35621
35738
  }
35622
35739
  case EMR_SETVIEWPORTEXTEX: {
35623
- if (recSize >= 16) {
35624
- rCtx.viewportExt.cx = view.getInt32(dataOff, true);
35625
- rCtx.viewportExt.cy = view.getInt32(dataOff + 4, true);
35626
- activateGdiMappingMode(rCtx);
35740
+ if (recSize >= 16 && extentsSettable(rCtx)) {
35741
+ const cx = view.getInt32(dataOff, true);
35742
+ const cy = view.getInt32(dataOff + 4, true);
35743
+ if (cx !== 0 && cy !== 0) {
35744
+ rCtx.viewportExt = { cx, cy };
35745
+ activateGdiMappingMode(rCtx);
35746
+ }
35627
35747
  }
35628
35748
  return true;
35629
35749
  }
@@ -35638,14 +35758,21 @@ function handleCoordinateRecord(rCtx, recType, dataOff, recSize) {
35638
35758
  case EMR_SETMAPMODE: {
35639
35759
  if (recSize >= 12) {
35640
35760
  const mode = view.getUint32(dataOff, true);
35641
- if (mode === 8 || mode === 7) {
35642
- activateGdiMappingMode(rCtx);
35761
+ if (mode >= 1 && mode <= 8) {
35762
+ rCtx.mapMode = mode;
35763
+ if (mode === 1) {
35764
+ rCtx.windowExt = { cx: 1, cy: 1 };
35765
+ rCtx.viewportExt = { cx: 1, cy: 1 };
35766
+ }
35767
+ if (mode === 1 || mode === 8 || mode === 7) {
35768
+ activateGdiMappingMode(rCtx);
35769
+ }
35643
35770
  }
35644
35771
  }
35645
35772
  return true;
35646
35773
  }
35647
35774
  case EMR_SCALEVIEWPORTEXTEX: {
35648
- if (recSize >= 24) {
35775
+ if (recSize >= 24 && extentsSettable(rCtx)) {
35649
35776
  const xNum = view.getInt32(dataOff, true);
35650
35777
  const xDenom = view.getInt32(dataOff + 4, true);
35651
35778
  const yNum = view.getInt32(dataOff + 8, true);
@@ -35661,7 +35788,7 @@ function handleCoordinateRecord(rCtx, recType, dataOff, recSize) {
35661
35788
  return true;
35662
35789
  }
35663
35790
  case EMR_SCALEWINDOWEXTEX: {
35664
- if (recSize >= 24) {
35791
+ if (recSize >= 24 && extentsSettable(rCtx)) {
35665
35792
  const xNum = view.getInt32(dataOff, true);
35666
35793
  const xDenom = view.getInt32(dataOff + 4, true);
35667
35794
  const yNum = view.getInt32(dataOff + 8, true);
@@ -35812,6 +35939,15 @@ function handleEmfGdiStateRecord(rCtx, recType, _offset, dataOff, recSize) {
35812
35939
  }
35813
35940
  rCtx.clipStack ?? (rCtx.clipStack = []);
35814
35941
  rCtx.clipStack.push({ region: rCtx.clipRegion ?? null });
35942
+ rCtx.mappingStack ?? (rCtx.mappingStack = []);
35943
+ rCtx.mappingStack.push({
35944
+ mapMode: rCtx.mapMode,
35945
+ windowOrg: { ...rCtx.windowOrg },
35946
+ windowExt: { ...rCtx.windowExt },
35947
+ viewportOrg: { ...rCtx.viewportOrg },
35948
+ viewportExt: { ...rCtx.viewportExt },
35949
+ useMappingMode: rCtx.useMappingMode
35950
+ });
35815
35951
  rCtx.stateStack.push(cloneState(state));
35816
35952
  ctx.save();
35817
35953
  if (rCtx.clipRegion) {
@@ -35821,23 +35957,31 @@ function handleEmfGdiStateRecord(rCtx, recType, _offset, dataOff, recSize) {
35821
35957
  }
35822
35958
  case EMR_RESTOREDC: {
35823
35959
  if (recSize >= 12) {
35824
- while (rCtx.clipSaveDepth > 0) {
35825
- ctx.restore();
35826
- rCtx.clipSaveDepth--;
35827
- }
35828
35960
  let rel = view.getInt32(dataOff, true);
35829
35961
  if (rel < 0) {
35830
35962
  rel = rCtx.stateStack.length + rel + 1;
35831
35963
  }
35832
- while (rCtx.stateStack.length > rel && rCtx.stateStack.length > 0) {
35964
+ if (rel < 1 || rel > rCtx.stateStack.length) {
35965
+ return true;
35966
+ }
35967
+ while (rCtx.clipSaveDepth > 0) {
35968
+ ctx.restore();
35969
+ rCtx.clipSaveDepth--;
35970
+ }
35971
+ while (rCtx.stateStack.length > rel) {
35833
35972
  rCtx.stateStack.pop();
35834
35973
  rCtx.clipStack?.pop();
35974
+ rCtx.mappingStack?.pop();
35835
35975
  ctx.restore();
35836
35976
  }
35837
35977
  const restored = rCtx.stateStack.pop();
35838
35978
  if (restored) {
35839
35979
  const clipSnapshot = rCtx.clipStack?.pop();
35980
+ const mappingSnapshot = rCtx.mappingStack?.pop();
35840
35981
  Object.assign(state, restored);
35982
+ if (mappingSnapshot) {
35983
+ Object.assign(rCtx, mappingSnapshot);
35984
+ }
35841
35985
  ctx.restore();
35842
35986
  rCtx.clipRegion = clipSnapshot?.region ?? null;
35843
35987
  if (rCtx.clipRegion) {
@@ -37936,9 +38080,11 @@ function replayEmfRecords(view, ctx, bounds, canvasW, canvasH, dpiScale = 1, rep
37936
38080
  // canvas like every other device coordinate (bounds' top-left to the
37937
38081
  // canvas origin, scaled by sx/sy); see `deviceToCanvas`.
37938
38082
  windowOrg: { x: 0, y: 0 },
37939
- windowExt: { cx: logicalW, cy: logicalH },
38083
+ windowExt: { cx: 1, cy: 1 },
37940
38084
  viewportOrg: { x: 0, y: 0 },
37941
- viewportExt: { cx: logicalW, cy: logicalH },
38085
+ viewportExt: { cx: 1, cy: 1 },
38086
+ mapMode: 1,
38087
+ // MM_TEXT: the default DC uses one-to-one page/device units.
37942
38088
  useMappingMode: false,
37943
38089
  deviceToCanvas: true,
37944
38090
  clipSaveDepth: 0,
@@ -38190,18 +38336,18 @@ function setMapMode(m, mode) {
38190
38336
  }
38191
38337
  m.mode = mode;
38192
38338
  }
38193
- function extentsSettable(m) {
38339
+ function extentsSettable2(m) {
38194
38340
  return m.mode === MM_ISOTROPIC || m.mode === MM_ANISOTROPIC;
38195
38341
  }
38196
38342
  function setWindowExt(m, cx, cy) {
38197
- if (!extentsSettable(m) || cx === 0 || cy === 0) {
38343
+ if (!extentsSettable2(m) || cx === 0 || cy === 0) {
38198
38344
  return;
38199
38345
  }
38200
38346
  m.winExt = { cx, cy };
38201
38347
  fixIsotropic(m);
38202
38348
  }
38203
38349
  function setViewportExt(m, cx, cy) {
38204
- if (!extentsSettable(m) || cx === 0 || cy === 0) {
38350
+ if (!extentsSettable2(m) || cx === 0 || cy === 0) {
38205
38351
  return;
38206
38352
  }
38207
38353
  m.vpExt = { cx, cy };
@@ -38211,7 +38357,7 @@ function scaled(v, num, den) {
38211
38357
  return Math.trunc(v * num / den);
38212
38358
  }
38213
38359
  function scaleWindowExt(m, xNum, xDen, yNum, yDen) {
38214
- if (!extentsSettable(m) || !xDen || !yDen) {
38360
+ if (!extentsSettable2(m) || !xDen || !yDen) {
38215
38361
  return;
38216
38362
  }
38217
38363
  const cx = scaled(m.winExt.cx, xNum, xDen);
@@ -38223,7 +38369,7 @@ function scaleWindowExt(m, xNum, xDen, yNum, yDen) {
38223
38369
  fixIsotropic(m);
38224
38370
  }
38225
38371
  function scaleViewportExt(m, xNum, xDen, yNum, yDen) {
38226
- if (!extentsSettable(m) || !xDen || !yDen) {
38372
+ if (!extentsSettable2(m) || !xDen || !yDen) {
38227
38373
  return;
38228
38374
  }
38229
38375
  const cx = scaled(m.vpExt.cx, xNum, xDen);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "emf-converter",
3
- "version": "4.8.6",
3
+ "version": "4.8.8",
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",