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