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