emf-converter 4.8.7 → 4.8.9

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.mjs CHANGED
@@ -27796,10 +27796,10 @@ function parseEmfHeader(view) {
27796
27796
  };
27797
27797
  }
27798
27798
  function getRenderableEmfBounds(header) {
27799
- const boundsW = header.bounds.right - header.bounds.left;
27800
- const boundsH = header.bounds.bottom - header.bounds.top;
27799
+ const boundsW = header.bounds.right - header.bounds.left + 1;
27800
+ const boundsH = header.bounds.bottom - header.bounds.top + 1;
27801
27801
  if (boundsW > 0 && boundsH > 0) {
27802
- return header.bounds;
27802
+ return { ...header.bounds, right: header.bounds.right + 1, bottom: header.bounds.bottom + 1 };
27803
27803
  }
27804
27804
  if (header.frameW > 0 && header.frameH > 0) {
27805
27805
  emfLog(
@@ -30831,6 +30831,13 @@ function insetFixBox(box, inset) {
30831
30831
  }
30832
30832
  return framed;
30833
30833
  }
30834
+ function sameEllipseRay(l, t, r, b, sx, sy, ex, ey) {
30835
+ const ax = BigInt(2 * sx - l - r);
30836
+ const ay = BigInt(2 * sy - t - b);
30837
+ const bx = BigInt(2 * ex - l - r);
30838
+ const by = BigInt(2 * ey - t - b);
30839
+ return ax * by === ay * bx && ax * bx + ay * by > 0n;
30840
+ }
30834
30841
  function handleSetPixelV(rCtx, dataOff, recSize) {
30835
30842
  const { ctx, view } = rCtx;
30836
30843
  if (recSize >= 20) {
@@ -31188,7 +31195,9 @@ function handleArcFamily(rCtx, recType, dataOff, recSize) {
31188
31195
  const rx = Math.abs(r - l) / 2;
31189
31196
  const ry = Math.abs(b - t) / 2;
31190
31197
  const startAngle = Math.atan2((startY - cyA) / (ry || 1), (startX - cxA) / (rx || 1));
31191
- const endAngle = Math.atan2((endY - cyA) / (ry || 1), (endX - cxA) / (rx || 1));
31198
+ const clockwise = state.arcDirection === 2;
31199
+ const fullEllipse = recType !== EMR_PIE && rx > 0 && ry > 0 && sameEllipseRay(l, t, r, b, startX, startY, endX, endY);
31200
+ const endAngle = fullEllipse ? startAngle + (clockwise ? 2 : -2) * Math.PI : Math.atan2((endY - cyA) / (ry || 1), (endX - cxA) / (rx || 1));
31192
31201
  const rotated = hasWorldRotation(rCtx);
31193
31202
  const unframed = fixBox(rCtx, l, t, r, b);
31194
31203
  const inset = insideFrameInset(rCtx, unframed);
@@ -31202,13 +31211,18 @@ function handleArcFamily(rCtx, recType, dataOff, recSize) {
31202
31211
  };
31203
31212
  const isArcTo = recType === EMR_ARCTO;
31204
31213
  const needsFill = recType === EMR_PIE || recType === EMR_CHORD;
31205
- const clockwise = state.arcDirection === 2;
31206
31214
  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) };
31207
31215
  const build = (c) => {
31208
31216
  if (recType === EMR_PIE) {
31209
31217
  c.moveTo(params.cx, params.cy);
31218
+ } else if (!isArcTo && inPath) {
31219
+ c.moveTo(startPoint.x, startPoint.y);
31210
31220
  }
31211
31221
  if (isArcTo) {
31222
+ if (!inPath) {
31223
+ const from = currentFix(rCtx);
31224
+ c.moveTo(from[0] / 16, from[1] / 16);
31225
+ }
31212
31226
  c.lineTo(startPoint.x, startPoint.y);
31213
31227
  }
31214
31228
  c.ellipse(params.cx, params.cy, params.rx, params.ry, params.rotation, startAngle, endAngle, !clockwise);
@@ -31228,7 +31242,9 @@ function handleArcFamily(rCtx, recType, dataOff, recSize) {
31228
31242
  const rasterArgs = (immediate = false) => ({
31229
31243
  box: inset ? framed : immediate && needsFill ? curvedFixBox(rCtx, l, t, r, b) : unframed,
31230
31244
  s: radialOnBox(fixPoint(rCtx, startX, startY)),
31231
- e: radialOnBox(fixPoint(rCtx, endX, endY)),
31245
+ // Device FIX rounding can separate distinct radial points on the
31246
+ // same logical ray. Preserve their proven endpoint identity.
31247
+ e: fullEllipse ? radialOnBox(fixPoint(rCtx, startX, startY)) : radialOnBox(fixPoint(rCtx, endX, endY)),
31232
31248
  from: currentFix(rCtx)
31233
31249
  });
31234
31250
  if (inPath) {
@@ -35693,15 +35709,243 @@ function handleEmfObjectRecord(rCtx, recType, dataOff, recSize) {
35693
35709
  }
35694
35710
  }
35695
35711
 
35712
+ // src/wmf-mapping.ts
35713
+ var MM_TEXT = 1;
35714
+ var MM_LOMETRIC = 2;
35715
+ var MM_HIMETRIC = 3;
35716
+ var MM_LOENGLISH = 4;
35717
+ var MM_HIENGLISH = 5;
35718
+ var MM_TWIPS = 6;
35719
+ var MM_ISOTROPIC = 7;
35720
+ var MM_ANISOTROPIC = 8;
35721
+ var DEVICE_DPI = 96;
35722
+ function cloneMapping(m) {
35723
+ return {
35724
+ ...m.dpi ? { dpi: { ...m.dpi } } : {},
35725
+ mode: m.mode,
35726
+ winOrg: { ...m.winOrg },
35727
+ winExt: { ...m.winExt },
35728
+ vpOrg: { ...m.vpOrg },
35729
+ vpExt: { ...m.vpExt }
35730
+ };
35731
+ }
35732
+ var METRIC_UNITS_PER_INCH = {
35733
+ [MM_LOMETRIC]: 254,
35734
+ [MM_HIMETRIC]: 2540,
35735
+ [MM_LOENGLISH]: 100,
35736
+ [MM_HIENGLISH]: 1e3,
35737
+ [MM_TWIPS]: 1440
35738
+ };
35739
+ function fixIsotropic(m) {
35740
+ if (m.mode !== MM_ISOTROPIC || !m.winExt.cx || !m.winExt.cy) {
35741
+ return;
35742
+ }
35743
+ const xdim = Math.abs(m.vpExt.cx / m.winExt.cx) / (m.dpi?.x ?? DEVICE_DPI);
35744
+ const ydim = Math.abs(m.vpExt.cy / m.winExt.cy) / (m.dpi?.y ?? DEVICE_DPI);
35745
+ if (xdim > ydim) {
35746
+ const min = m.vpExt.cx >= 0 ? 1 : -1;
35747
+ m.vpExt.cx = Math.floor(m.vpExt.cx * ydim / xdim + 0.5) || min;
35748
+ } else if (ydim > xdim) {
35749
+ const min = m.vpExt.cy >= 0 ? 1 : -1;
35750
+ m.vpExt.cy = Math.floor(m.vpExt.cy * xdim / ydim + 0.5) || min;
35751
+ }
35752
+ }
35753
+ function setMapMode(m, mode) {
35754
+ if (mode < MM_TEXT || mode > MM_ANISOTROPIC) {
35755
+ return;
35756
+ }
35757
+ const units = METRIC_UNITS_PER_INCH[mode === MM_ISOTROPIC ? MM_LOMETRIC : mode];
35758
+ if (mode === MM_TEXT) {
35759
+ m.winExt = { cx: 1, cy: 1 };
35760
+ m.vpExt = { cx: 1, cy: 1 };
35761
+ } else if (units !== void 0 && (mode !== MM_ISOTROPIC || m.mode !== MM_ISOTROPIC)) {
35762
+ m.winExt = { cx: units, cy: units };
35763
+ m.vpExt = { cx: m.dpi?.x ?? DEVICE_DPI, cy: -(m.dpi?.y ?? DEVICE_DPI) };
35764
+ }
35765
+ m.mode = mode;
35766
+ }
35767
+ function extentsSettable(m) {
35768
+ return m.mode === MM_ISOTROPIC || m.mode === MM_ANISOTROPIC;
35769
+ }
35770
+ function setWindowExt(m, cx, cy) {
35771
+ if (!extentsSettable(m) || cx === 0 || cy === 0) {
35772
+ return;
35773
+ }
35774
+ m.winExt = { cx, cy };
35775
+ fixIsotropic(m);
35776
+ }
35777
+ function setViewportExt(m, cx, cy) {
35778
+ if (!extentsSettable(m) || cx === 0 || cy === 0) {
35779
+ return;
35780
+ }
35781
+ m.vpExt = { cx, cy };
35782
+ fixIsotropic(m);
35783
+ }
35784
+ function scaled(v, num, den) {
35785
+ return Math.trunc(v * num / den);
35786
+ }
35787
+ function scaleWindowExt(m, xNum, xDen, yNum, yDen) {
35788
+ if (!extentsSettable(m) || !xDen || !yDen) {
35789
+ return;
35790
+ }
35791
+ const cx = scaled(m.winExt.cx, xNum, xDen);
35792
+ const cy = scaled(m.winExt.cy, yNum, yDen);
35793
+ if (cx === 0 || cy === 0) {
35794
+ return;
35795
+ }
35796
+ m.winExt = { cx, cy };
35797
+ fixIsotropic(m);
35798
+ }
35799
+ function scaleViewportExt(m, xNum, xDen, yNum, yDen) {
35800
+ if (!extentsSettable(m) || !xDen || !yDen) {
35801
+ return;
35802
+ }
35803
+ const cx = scaled(m.vpExt.cx, xNum, xDen);
35804
+ const cy = scaled(m.vpExt.cy, yNum, yDen);
35805
+ if (cx === 0 || cy === 0) {
35806
+ return;
35807
+ }
35808
+ m.vpExt = { cx, cy };
35809
+ fixIsotropic(m);
35810
+ }
35811
+ function applyWmfMapping(rCtx, m, kx, ky, mirrorWidth = 0) {
35812
+ rCtx.wmfLayoutRtl = mirrorWidth > 0;
35813
+ rCtx.useMappingMode = true;
35814
+ rCtx.windowOrg = { x: m.winOrg.x, y: m.winOrg.y };
35815
+ rCtx.windowExt = { cx: m.winExt.cx || 1, cy: m.winExt.cy || 1 };
35816
+ rCtx.viewportOrg = { x: m.vpOrg.x * kx, y: m.vpOrg.y * ky };
35817
+ rCtx.viewportExt = { cx: (m.vpExt.cx || 1) * kx, cy: (m.vpExt.cy || 1) * ky };
35818
+ if (mirrorWidth > 0) {
35819
+ rCtx.viewportOrg.x = mirrorWidth * kx - rCtx.viewportOrg.x;
35820
+ rCtx.viewportExt.cx = -rCtx.viewportExt.cx;
35821
+ }
35822
+ }
35823
+ function scanMappingRecords(view, start) {
35824
+ const out = {
35825
+ mode: null,
35826
+ winExt: null,
35827
+ vpExt: null
35828
+ };
35829
+ let off = start;
35830
+ let guard = 0;
35831
+ while (off + 6 <= view.byteLength && guard++ < 1e6) {
35832
+ const size = view.getUint32(off, true) * 2;
35833
+ const type = view.getUint16(off + 4, true);
35834
+ if (size < 6 || off + size > view.byteLength || type === 0) {
35835
+ break;
35836
+ }
35837
+ const d = off + 6;
35838
+ if (type === 259 && size >= 8 && out.mode === null) {
35839
+ out.mode = view.getUint16(d, true);
35840
+ } else if (type === 524 && size >= 10 && !out.winExt) {
35841
+ out.winExt = { cy: view.getInt16(d, true), cx: view.getInt16(d + 2, true) };
35842
+ } else if (type === 526 && size >= 10 && !out.vpExt) {
35843
+ out.vpExt = { cy: view.getInt16(d, true), cx: view.getInt16(d + 2, true) };
35844
+ }
35845
+ off += size;
35846
+ }
35847
+ return out;
35848
+ }
35849
+ function wmfPlayback(view, header, referenceDpi) {
35850
+ const valid = (v) => typeof v === "number" && Number.isFinite(v) && v > 0 ? v : DEVICE_DPI;
35851
+ const dpi = { x: valid(typeof referenceDpi === "number" ? referenceDpi : referenceDpi?.x), y: valid(typeof referenceDpi === "number" ? referenceDpi : referenceDpi?.y) };
35852
+ const density = referenceDpi === void 0 ? {} : { dpi };
35853
+ const bw = header.boundsRight - header.boundsLeft;
35854
+ const bh = header.boundsBottom - header.boundsTop;
35855
+ if (header.placeable !== false) {
35856
+ const inch = header.unitsPerInch > 0 ? header.unitsPerInch : DEVICE_DPI;
35857
+ const width2 = Math.max(1, Math.round(Math.abs(bw) * dpi.x / inch));
35858
+ const height2 = Math.max(1, Math.round(Math.abs(bh) * dpi.y / inch));
35859
+ return {
35860
+ width: width2,
35861
+ height: height2,
35862
+ mapping: {
35863
+ ...density,
35864
+ mode: MM_ANISOTROPIC,
35865
+ winOrg: { x: header.boundsLeft, y: header.boundsTop },
35866
+ winExt: { cx: bw || 1, cy: bh || 1 },
35867
+ vpOrg: { x: 0, y: 0 },
35868
+ vpExt: { cx: width2, cy: height2 }
35869
+ }
35870
+ };
35871
+ }
35872
+ const scan = scanMappingRecords(view, header.headerSize);
35873
+ const scalable = scan.mode === MM_ISOTROPIC || scan.mode === MM_ANISOTROPIC;
35874
+ const ext = scalable && scan.vpExt ? scan.vpExt : scan.winExt;
35875
+ const width = ext ? Math.abs(ext.cx) : Math.abs(bw);
35876
+ const height = ext ? Math.abs(ext.cy) : Math.abs(bh);
35877
+ const w = Math.max(1, width || 1);
35878
+ const h = Math.max(1, height || 1);
35879
+ return {
35880
+ width: w,
35881
+ height: h,
35882
+ mapping: {
35883
+ ...density,
35884
+ mode: MM_ANISOTROPIC,
35885
+ winOrg: { x: 0, y: 0 },
35886
+ winExt: { cx: w, cy: h },
35887
+ vpOrg: { x: 0, y: 0 },
35888
+ vpExt: { cx: w, cy: h }
35889
+ }
35890
+ };
35891
+ }
35892
+
35696
35893
  // src/emf-gdi-transform-handlers.ts
35894
+ function emfDeviceDpi(view) {
35895
+ if (view.byteLength < 88 || view.getUint32(0, true) !== 1) {
35896
+ return void 0;
35897
+ }
35898
+ const devW = view.getInt32(72, true);
35899
+ const devH = view.getInt32(76, true);
35900
+ const mmW = view.getInt32(80, true);
35901
+ const mmH = view.getInt32(84, true);
35902
+ if (devW <= 0 || devH <= 0 || mmW <= 0 || mmH <= 0) {
35903
+ return void 0;
35904
+ }
35905
+ return { x: devW * 25.4 / mmW, y: devH * 25.4 / mmH };
35906
+ }
35907
+ function emfMapping(r) {
35908
+ const dpi = emfDeviceDpi(r.view);
35909
+ return {
35910
+ ...dpi ? { dpi } : {},
35911
+ mode: r.mapMode ?? MM_TEXT,
35912
+ winOrg: r.windowOrg,
35913
+ winExt: { ...r.windowExt },
35914
+ vpOrg: r.viewportOrg,
35915
+ vpExt: { ...r.viewportExt }
35916
+ };
35917
+ }
35918
+ function storeMapping(r, m) {
35919
+ r.mapMode = m.mode;
35920
+ r.windowExt = m.winExt;
35921
+ r.viewportExt = m.vpExt;
35922
+ }
35923
+ function setExtent(r, viewport, cx, cy) {
35924
+ const m = emfMapping(r);
35925
+ if (!extentsSettable(m) || cx === 0 || cy === 0) {
35926
+ return;
35927
+ }
35928
+ if (viewport) {
35929
+ m.vpExt = { cx, cy };
35930
+ } else {
35931
+ m.winExt = { cx, cy };
35932
+ }
35933
+ fixIsotropic(m);
35934
+ storeMapping(r, m);
35935
+ activateGdiMappingMode(r);
35936
+ }
35937
+ function scaleExtent(r, viewport, xNum, xDenom, yNum, yDenom) {
35938
+ const ext = viewport ? r.viewportExt : r.windowExt;
35939
+ const cx = xDenom !== 0 ? Math.round(ext.cx * xNum / xDenom) : ext.cx;
35940
+ const cy = yDenom !== 0 ? Math.round(ext.cy * yNum / yDenom) : ext.cy;
35941
+ setExtent(r, viewport, cx, cy);
35942
+ }
35697
35943
  function handleCoordinateRecord(rCtx, recType, dataOff, recSize) {
35698
35944
  const { view } = rCtx;
35699
35945
  switch (recType) {
35700
35946
  case EMR_SETWINDOWEXTEX: {
35701
35947
  if (recSize >= 16) {
35702
- rCtx.windowExt.cx = view.getInt32(dataOff, true);
35703
- rCtx.windowExt.cy = view.getInt32(dataOff + 4, true);
35704
- activateGdiMappingMode(rCtx);
35948
+ setExtent(rCtx, false, view.getInt32(dataOff, true), view.getInt32(dataOff + 4, true));
35705
35949
  }
35706
35950
  return true;
35707
35951
  }
@@ -35715,9 +35959,7 @@ function handleCoordinateRecord(rCtx, recType, dataOff, recSize) {
35715
35959
  }
35716
35960
  case EMR_SETVIEWPORTEXTEX: {
35717
35961
  if (recSize >= 16) {
35718
- rCtx.viewportExt.cx = view.getInt32(dataOff, true);
35719
- rCtx.viewportExt.cy = view.getInt32(dataOff + 4, true);
35720
- activateGdiMappingMode(rCtx);
35962
+ setExtent(rCtx, true, view.getInt32(dataOff, true), view.getInt32(dataOff + 4, true));
35721
35963
  }
35722
35964
  return true;
35723
35965
  }
@@ -35732,41 +35974,26 @@ function handleCoordinateRecord(rCtx, recType, dataOff, recSize) {
35732
35974
  case EMR_SETMAPMODE: {
35733
35975
  if (recSize >= 12) {
35734
35976
  const mode = view.getUint32(dataOff, true);
35735
- if (mode === 8 || mode === 7) {
35977
+ if (mode >= MM_TEXT && mode <= MM_ANISOTROPIC) {
35978
+ const m = emfMapping(rCtx);
35979
+ setMapMode(m, mode);
35980
+ storeMapping(rCtx, m);
35736
35981
  activateGdiMappingMode(rCtx);
35737
35982
  }
35738
35983
  }
35739
35984
  return true;
35740
35985
  }
35741
- case EMR_SCALEVIEWPORTEXTEX: {
35742
- if (recSize >= 24) {
35743
- const xNum = view.getInt32(dataOff, true);
35744
- const xDenom = view.getInt32(dataOff + 4, true);
35745
- const yNum = view.getInt32(dataOff + 8, true);
35746
- const yDenom = view.getInt32(dataOff + 12, true);
35747
- if (xDenom !== 0) {
35748
- rCtx.viewportExt.cx = Math.round(rCtx.viewportExt.cx * xNum / xDenom);
35749
- }
35750
- if (yDenom !== 0) {
35751
- rCtx.viewportExt.cy = Math.round(rCtx.viewportExt.cy * yNum / yDenom);
35752
- }
35753
- activateGdiMappingMode(rCtx);
35754
- }
35755
- return true;
35756
- }
35986
+ case EMR_SCALEVIEWPORTEXTEX:
35757
35987
  case EMR_SCALEWINDOWEXTEX: {
35758
35988
  if (recSize >= 24) {
35759
- const xNum = view.getInt32(dataOff, true);
35760
- const xDenom = view.getInt32(dataOff + 4, true);
35761
- const yNum = view.getInt32(dataOff + 8, true);
35762
- const yDenom = view.getInt32(dataOff + 12, true);
35763
- if (xDenom !== 0) {
35764
- rCtx.windowExt.cx = Math.round(rCtx.windowExt.cx * xNum / xDenom);
35765
- }
35766
- if (yDenom !== 0) {
35767
- rCtx.windowExt.cy = Math.round(rCtx.windowExt.cy * yNum / yDenom);
35768
- }
35769
- activateGdiMappingMode(rCtx);
35989
+ scaleExtent(
35990
+ rCtx,
35991
+ recType === EMR_SCALEVIEWPORTEXTEX,
35992
+ view.getInt32(dataOff, true),
35993
+ view.getInt32(dataOff + 4, true),
35994
+ view.getInt32(dataOff + 8, true),
35995
+ view.getInt32(dataOff + 12, true)
35996
+ );
35770
35997
  }
35771
35998
  return true;
35772
35999
  }
@@ -35906,6 +36133,15 @@ function handleEmfGdiStateRecord(rCtx, recType, _offset, dataOff, recSize) {
35906
36133
  }
35907
36134
  rCtx.clipStack ?? (rCtx.clipStack = []);
35908
36135
  rCtx.clipStack.push({ region: rCtx.clipRegion ?? null });
36136
+ rCtx.mappingStack ?? (rCtx.mappingStack = []);
36137
+ rCtx.mappingStack.push({
36138
+ mapMode: rCtx.mapMode,
36139
+ windowOrg: { ...rCtx.windowOrg },
36140
+ windowExt: { ...rCtx.windowExt },
36141
+ viewportOrg: { ...rCtx.viewportOrg },
36142
+ viewportExt: { ...rCtx.viewportExt },
36143
+ useMappingMode: rCtx.useMappingMode
36144
+ });
35909
36145
  rCtx.stateStack.push(cloneState(state));
35910
36146
  ctx.save();
35911
36147
  if (rCtx.clipRegion) {
@@ -35915,23 +36151,31 @@ function handleEmfGdiStateRecord(rCtx, recType, _offset, dataOff, recSize) {
35915
36151
  }
35916
36152
  case EMR_RESTOREDC: {
35917
36153
  if (recSize >= 12) {
35918
- while (rCtx.clipSaveDepth > 0) {
35919
- ctx.restore();
35920
- rCtx.clipSaveDepth--;
35921
- }
35922
36154
  let rel = view.getInt32(dataOff, true);
35923
36155
  if (rel < 0) {
35924
36156
  rel = rCtx.stateStack.length + rel + 1;
35925
36157
  }
35926
- while (rCtx.stateStack.length > rel && rCtx.stateStack.length > 0) {
36158
+ if (rel < 1 || rel > rCtx.stateStack.length) {
36159
+ return true;
36160
+ }
36161
+ while (rCtx.clipSaveDepth > 0) {
36162
+ ctx.restore();
36163
+ rCtx.clipSaveDepth--;
36164
+ }
36165
+ while (rCtx.stateStack.length > rel) {
35927
36166
  rCtx.stateStack.pop();
35928
36167
  rCtx.clipStack?.pop();
36168
+ rCtx.mappingStack?.pop();
35929
36169
  ctx.restore();
35930
36170
  }
35931
36171
  const restored = rCtx.stateStack.pop();
35932
36172
  if (restored) {
35933
36173
  const clipSnapshot = rCtx.clipStack?.pop();
36174
+ const mappingSnapshot = rCtx.mappingStack?.pop();
35934
36175
  Object.assign(state, restored);
36176
+ if (mappingSnapshot) {
36177
+ Object.assign(rCtx, mappingSnapshot);
36178
+ }
35935
36179
  ctx.restore();
35936
36180
  rCtx.clipRegion = clipSnapshot?.region ?? null;
35937
36181
  if (rCtx.clipRegion) {
@@ -36741,24 +36985,22 @@ function rgbaToHex(c) {
36741
36985
  }
36742
36986
  return /^#[0-9a-f]{6}$/i.test(c) ? c : null;
36743
36987
  }
36744
- function drawPlusStringWithEngine(rCtx, font, text, layoutX, layoutY, paint, alignment, format, brushSampler2 = null) {
36988
+ function realizePlusEngineFont(rCtx, font, paint, brushSampler2) {
36745
36989
  const fonts = rCtx.fonts;
36746
36990
  const color = typeof paint === "string" ? rgbaToHex(paint) : null;
36747
- const hintNow = rCtx.textRenderingHint ?? 0;
36748
- const graySmooth = hintNow === 3 || hintNow === 4 || hintNow === 5;
36991
+ const hint = rCtx.textRenderingHint ?? 0;
36992
+ const graySmooth = hint === 3 || hint === 4 || hint === 5;
36749
36993
  const solidViaCoverage = !!color && graySmooth && !isSvgContext(rCtx.ctx);
36750
36994
  const sampler = isSvgContext(rCtx.ctx) ? null : solidViaCoverage ? solidSampler(cssColorToArgb(paint) ?? 4278190080) : color ? null : brushSampler2;
36751
36995
  const unit = font.unit ?? 0;
36752
- if (!fonts || !color && !sampler || alignment !== 0 || unit !== 0 && unit !== 2) {
36753
- return false;
36996
+ if (!fonts || !color && !sampler || unit !== 0 && unit !== 2) {
36997
+ return null;
36754
36998
  }
36755
36999
  const m = plusWorldMatrix(rCtx);
36756
37000
  if (m[1] !== 0 || m[2] !== 0 || m[0] <= 0 || m[3] <= 0) {
36757
- return false;
37001
+ return null;
36758
37002
  }
36759
37003
  const emPx = font.emSize * m[3];
36760
- const hint = rCtx.textRenderingHint ?? 0;
36761
- const quality = HINT_QUALITY[hint] ?? NONANTIALIASED_QUALITY;
36762
37004
  const spec = {
36763
37005
  face: font.family,
36764
37006
  height: -Math.round(emPx),
@@ -36767,7 +37009,7 @@ function drawPlusStringWithEngine(rCtx, font, text, layoutX, layoutY, paint, ali
36767
37009
  italic: (font.flags & 2) !== 0,
36768
37010
  charSet: 1,
36769
37011
  pitchAndFamily: 0,
36770
- quality,
37012
+ quality: HINT_QUALITY[hint] ?? NONANTIALIASED_QUALITY,
36771
37013
  unhinted: hint === 2 || hint === 4,
36772
37014
  // AntiAlias ignores the font's gasp table (measured: Arial 16 px is
36773
37015
  // grayscale there, but single-bit under AntiAliasGridFit, as in GDI).
@@ -36775,31 +37017,32 @@ function drawPlusStringWithEngine(rCtx, font, text, layoutX, layoutY, paint, ali
36775
37017
  };
36776
37018
  const realized = fonts.realize(spec, rCtx.fontFamilyMap);
36777
37019
  if (!realized) {
36778
- return false;
36779
- }
36780
- const ttf = realized.ttf;
36781
- const unhinted = spec.unhinted === true;
36782
- const exactAscent = emPx * ttf.winAscent / ttf.unitsPerEm;
36783
- const ascent = unhinted ? exactAscent : Math.ceil(exactAscent);
36784
- const x = m[0] * layoutX + m[4] + emPx * (format?.leadingMargin ?? PLUS_LEADING_EM);
36785
- const y = m[3] * layoutY + m[5] + ascent;
36786
- const codes = [];
36787
- for (let i = 0; i < text.length; i++) {
36788
- codes.push(text.charCodeAt(i));
37020
+ return null;
36789
37021
  }
36790
- const tracking = format?.tracking ?? PLUS_DEFAULT_TRACKING;
36791
- const dx = unhinted ? codes.map((c) => realized.advance(realized.glyphIndex(c)) * tracking) : null;
36792
- if ((!color || solidViaCoverage) && sampler) {
36793
- const cov = gdiTextCoverage(realized, {
36794
- codes,
36795
- glyphIndices: false,
36796
- x,
36797
- y,
36798
- dx,
36799
- dy: null,
36800
- textAlign: 24,
36801
- options: 0,
36802
- matrix: null});
37022
+ return {
37023
+ realized,
37024
+ emPx,
37025
+ m,
37026
+ unhinted: spec.unhinted === true,
37027
+ color,
37028
+ sampler,
37029
+ solidViaCoverage,
37030
+ underline: (font.flags & 4) !== 0,
37031
+ strikeOut: (font.flags & 8) !== 0
37032
+ };
37033
+ }
37034
+ function paintPlusEngineRun(rCtx, ef, run) {
37035
+ const base = {
37036
+ ...run,
37037
+ textAlign: 24,
37038
+ bkColor: "#ffffff",
37039
+ bkMode: 1,
37040
+ options: 0,
37041
+ rect: null,
37042
+ matrix: null
37043
+ };
37044
+ if ((!ef.color || ef.solidViaCoverage) && ef.sampler) {
37045
+ const cov = gdiTextCoverage(ef.realized, { ...base});
36803
37046
  if (!cov) {
36804
37047
  return true;
36805
37048
  }
@@ -36809,7 +37052,7 @@ function drawPlusStringWithEngine(rCtx, font, text, layoutX, layoutY, paint, ali
36809
37052
  }
36810
37053
  return compositeBrushCoverage(
36811
37054
  rCtx,
36812
- sampler,
37055
+ ef.sampler,
36813
37056
  { x: cov.x, y: cov.y, w: cov.width, h: cov.height },
36814
37057
  cov.data,
36815
37058
  cov.channels,
@@ -36817,25 +37060,82 @@ function drawPlusStringWithEngine(rCtx, font, text, layoutX, layoutY, paint, ali
36817
37060
  cov.channels === 3 ? gamma : 1
36818
37061
  );
36819
37062
  }
36820
- paintGdiTextRun(rCtx.ctx, realized, {
36821
- codes,
36822
- glyphIndices: false,
36823
- x,
36824
- y,
36825
- dx,
36826
- dy: null,
36827
- textAlign: 24,
36828
- textColor: color,
36829
- bkColor: "#ffffff",
36830
- bkMode: 1,
36831
- options: 0,
36832
- rect: null,
36833
- matrix: null,
36834
- underline: (font.flags & 4) !== 0,
36835
- strikeOut: (font.flags & 8) !== 0
36836
- }, rCtx.fontFamilyMap);
37063
+ paintGdiTextRun(
37064
+ rCtx.ctx,
37065
+ ef.realized,
37066
+ { ...base, textColor: ef.color, underline: ef.underline, strikeOut: ef.strikeOut },
37067
+ rCtx.fontFamilyMap
37068
+ );
36837
37069
  return true;
36838
37070
  }
37071
+ function drawPlusStringWithEngine(rCtx, font, text, layoutX, layoutY, paint, alignment, format, brushSampler2 = null) {
37072
+ if (alignment !== 0) {
37073
+ return false;
37074
+ }
37075
+ const ef = realizePlusEngineFont(rCtx, font, paint, brushSampler2);
37076
+ if (!ef) {
37077
+ return false;
37078
+ }
37079
+ const { realized, emPx, m, unhinted } = ef;
37080
+ const ttf = realized.ttf;
37081
+ const exactAscent = emPx * ttf.winAscent / ttf.unitsPerEm;
37082
+ const ascent = unhinted ? exactAscent : Math.ceil(exactAscent);
37083
+ const x = m[0] * layoutX + m[4] + emPx * (format?.leadingMargin ?? PLUS_LEADING_EM);
37084
+ const y = m[3] * layoutY + m[5] + ascent;
37085
+ const codes = [];
37086
+ for (let i = 0; i < text.length; i++) {
37087
+ codes.push(text.charCodeAt(i));
37088
+ }
37089
+ const tracking = format?.tracking ?? PLUS_DEFAULT_TRACKING;
37090
+ const dx = unhinted ? codes.map((c) => realized.advance(realized.glyphIndex(c)) * tracking) : null;
37091
+ return paintPlusEngineRun(rCtx, ef, { codes, glyphIndices: false, x, y, dx, dy: null });
37092
+ }
37093
+ var DRIVER_STRING_CMAP_LOOKUP = 1;
37094
+ var DRIVER_STRING_VERTICAL = 2;
37095
+ var DRIVER_STRING_REALIZED_ADVANCE = 4;
37096
+ function drawPlusDriverString(rCtx, recFlags, brushVal, font, codes, positions, options2) {
37097
+ const cmap = (options2 & DRIVER_STRING_CMAP_LOOKUP) !== 0;
37098
+ const realizedAdvance = (options2 & DRIVER_STRING_REALIZED_ADVANCE) !== 0;
37099
+ if (!(options2 & DRIVER_STRING_VERTICAL)) {
37100
+ const paint = resolveBrushPaint(rCtx, recFlags, brushVal);
37101
+ const ef = realizePlusEngineFont(rCtx, font, paint, rCtx.fonts ? deviceBrushSampler(rCtx, recFlags, brushVal) : null);
37102
+ if (ef) {
37103
+ const { m } = ef;
37104
+ const dev = positions.map((p) => ({ x: m[0] * p.x + m[4], y: m[3] * p.y + m[5] }));
37105
+ const step = (axis) => dev.map((p, i) => i + 1 < dev.length ? dev[i + 1][axis] - p[axis] : 0);
37106
+ paintPlusEngineRun(rCtx, ef, {
37107
+ codes,
37108
+ glyphIndices: !cmap,
37109
+ x: dev[0].x,
37110
+ y: dev[0].y,
37111
+ dx: realizedAdvance ? null : step("x"),
37112
+ dy: realizedAdvance ? null : step("y")
37113
+ });
37114
+ return;
37115
+ }
37116
+ }
37117
+ if (!cmap) {
37118
+ return;
37119
+ }
37120
+ const { ctx } = rCtx;
37121
+ const bold = font.flags & 1 ? "bold " : "";
37122
+ const italic = font.flags & 2 ? "italic " : "";
37123
+ const family = mapFontFamily(font.family, rCtx.fontFamilyMap);
37124
+ const setFont = () => {
37125
+ ctx.font = `${italic}${bold}${font.emSize}px ${family}`;
37126
+ ctx.textBaseline = "alphabetic";
37127
+ ctx.textAlign = "left";
37128
+ };
37129
+ if (realizedAdvance) {
37130
+ setFont();
37131
+ fillPlusText(rCtx, recFlags, brushVal, String.fromCharCode(...codes), positions[0].x, positions[0].y, font.emSize);
37132
+ return;
37133
+ }
37134
+ for (let i = 0; i < codes.length; i++) {
37135
+ setFont();
37136
+ fillPlusText(rCtx, recFlags, brushVal, String.fromCharCode(codes[i]), positions[i].x, positions[i].y, font.emSize);
37137
+ }
37138
+ }
36839
37139
  function handleEmfPlusTextImageRecord(rCtx, recType, recFlags, dataOff, recDataSize) {
36840
37140
  const { ctx, view, objectTable } = rCtx;
36841
37141
  switch (recType) {
@@ -36919,24 +37219,31 @@ function handleEmfPlusTextImageRecord(rCtx, recType, recFlags, dataOff, recDataS
36919
37219
  case EMFPLUS_DRAWDRIVERSTRING: {
36920
37220
  if (recDataSize >= 16) {
36921
37221
  const brushVal = view.getUint32(dataOff, true);
37222
+ const options2 = view.getUint32(dataOff + 4, true);
37223
+ const matrixPresent = view.getUint32(dataOff + 8, true) !== 0;
36922
37224
  const glyphCount = view.getUint32(dataOff + 12, true);
36923
- const fontId = recFlags & 255;
36924
- const font = objectTable.get(fontId);
37225
+ const font = objectTable.get(recFlags & 255);
36925
37226
  const glyphsOff = dataOff + 16;
36926
37227
  const posOff = glyphsOff + glyphCount * 2;
36927
- const alignedPosOff = posOff + 3 & -4;
36928
- if (glyphCount > 0 && glyphCount < 1e5 && alignedPosOff + glyphCount * 8 <= dataOff + recDataSize && font && font.kind === "plus-font") {
36929
- const text = readUtf16LE(view, glyphsOff, glyphCount);
36930
- if (text.length > 0) {
36931
- const bold = font.flags & 1 ? "bold " : "";
36932
- const italic = font.flags & 2 ? "italic " : "";
36933
- const family = mapFontFamily(font.family, rCtx.fontFamilyMap);
36934
- ctx.font = `${italic}${bold}${font.emSize}px ${family}`;
36935
- ctx.textBaseline = "alphabetic";
36936
- ctx.textAlign = "left";
36937
- const gx = view.getFloat32(alignedPosOff, true);
36938
- const gy = view.getFloat32(alignedPosOff + 4, true);
36939
- fillPlusText(rCtx, recFlags, brushVal, text, gx, gy, font.emSize);
37228
+ const matrixOff = posOff + glyphCount * 8;
37229
+ if (glyphCount > 0 && glyphCount < 1e5 && matrixOff + (matrixPresent ? 24 : 0) <= dataOff + recDataSize && font && font.kind === "plus-font") {
37230
+ const codes = [];
37231
+ const positions = [];
37232
+ for (let i = 0; i < glyphCount; i++) {
37233
+ codes.push(view.getUint16(glyphsOff + i * 2, true));
37234
+ positions.push({ x: view.getFloat32(posOff + i * 8, true), y: view.getFloat32(posOff + i * 8 + 4, true) });
37235
+ }
37236
+ const world = rCtx.worldTransform;
37237
+ if (matrixPresent) {
37238
+ const xf = [0, 4, 8, 12, 16, 20].map((k) => view.getFloat32(matrixOff + k, true));
37239
+ if (xf.every(Number.isFinite)) {
37240
+ rCtx.worldTransform = mulMatrix(world, xf);
37241
+ }
37242
+ }
37243
+ try {
37244
+ drawPlusDriverString(rCtx, recFlags, brushVal, font, codes, positions, options2);
37245
+ } finally {
37246
+ rCtx.worldTransform = world;
36940
37247
  }
36941
37248
  }
36942
37249
  }
@@ -37715,7 +38022,7 @@ function parseEmfPlusRegionObject(view, off, maxLen) {
37715
38022
  }
37716
38023
  view.getUint32(off, true);
37717
38024
  const regionNodeCount = view.getUint32(off + 4, true);
37718
- if (regionNodeCount === 0 || regionNodeCount > 1e5) {
38025
+ if (regionNodeCount > 1e5) {
37719
38026
  return null;
37720
38027
  }
37721
38028
  const endOff = off + maxLen;
@@ -38030,9 +38337,11 @@ function replayEmfRecords(view, ctx, bounds, canvasW, canvasH, dpiScale = 1, rep
38030
38337
  // canvas like every other device coordinate (bounds' top-left to the
38031
38338
  // canvas origin, scaled by sx/sy); see `deviceToCanvas`.
38032
38339
  windowOrg: { x: 0, y: 0 },
38033
- windowExt: { cx: logicalW, cy: logicalH },
38340
+ windowExt: { cx: 1, cy: 1 },
38034
38341
  viewportOrg: { x: 0, y: 0 },
38035
- viewportExt: { cx: logicalW, cy: logicalH },
38342
+ viewportExt: { cx: 1, cy: 1 },
38343
+ mapMode: 1,
38344
+ // MM_TEXT: the default DC uses one-to-one page/device units.
38036
38345
  useMappingMode: false,
38037
38346
  deviceToCanvas: true,
38038
38347
  clipSaveDepth: 0,
@@ -38229,187 +38538,6 @@ function extractEmbeddedEmf(view, start) {
38229
38538
  return out.buffer;
38230
38539
  }
38231
38540
 
38232
- // src/wmf-mapping.ts
38233
- var MM_TEXT = 1;
38234
- var MM_LOMETRIC = 2;
38235
- var MM_HIMETRIC = 3;
38236
- var MM_LOENGLISH = 4;
38237
- var MM_HIENGLISH = 5;
38238
- var MM_TWIPS = 6;
38239
- var MM_ISOTROPIC = 7;
38240
- var MM_ANISOTROPIC = 8;
38241
- var DEVICE_DPI = 96;
38242
- function cloneMapping(m) {
38243
- return {
38244
- ...m.dpi ? { dpi: { ...m.dpi } } : {},
38245
- mode: m.mode,
38246
- winOrg: { ...m.winOrg },
38247
- winExt: { ...m.winExt },
38248
- vpOrg: { ...m.vpOrg },
38249
- vpExt: { ...m.vpExt }
38250
- };
38251
- }
38252
- var METRIC_UNITS_PER_INCH = {
38253
- [MM_LOMETRIC]: 254,
38254
- [MM_HIMETRIC]: 2540,
38255
- [MM_LOENGLISH]: 100,
38256
- [MM_HIENGLISH]: 1e3,
38257
- [MM_TWIPS]: 1440
38258
- };
38259
- function fixIsotropic(m) {
38260
- if (m.mode !== MM_ISOTROPIC || !m.winExt.cx || !m.winExt.cy) {
38261
- return;
38262
- }
38263
- const xdim = Math.abs(m.vpExt.cx / m.winExt.cx) / (m.dpi?.x ?? DEVICE_DPI);
38264
- const ydim = Math.abs(m.vpExt.cy / m.winExt.cy) / (m.dpi?.y ?? DEVICE_DPI);
38265
- if (xdim > ydim) {
38266
- const min = m.vpExt.cx >= 0 ? 1 : -1;
38267
- m.vpExt.cx = Math.floor(m.vpExt.cx * ydim / xdim + 0.5) || min;
38268
- } else if (ydim > xdim) {
38269
- const min = m.vpExt.cy >= 0 ? 1 : -1;
38270
- m.vpExt.cy = Math.floor(m.vpExt.cy * xdim / ydim + 0.5) || min;
38271
- }
38272
- }
38273
- function setMapMode(m, mode) {
38274
- if (mode < MM_TEXT || mode > MM_ANISOTROPIC) {
38275
- return;
38276
- }
38277
- const units = METRIC_UNITS_PER_INCH[mode === MM_ISOTROPIC ? MM_LOMETRIC : mode];
38278
- if (mode === MM_TEXT) {
38279
- m.winExt = { cx: 1, cy: 1 };
38280
- m.vpExt = { cx: 1, cy: 1 };
38281
- } else if (units !== void 0 && (mode !== MM_ISOTROPIC || m.mode !== MM_ISOTROPIC)) {
38282
- m.winExt = { cx: units, cy: units };
38283
- m.vpExt = { cx: m.dpi?.x ?? DEVICE_DPI, cy: -(m.dpi?.y ?? DEVICE_DPI) };
38284
- }
38285
- m.mode = mode;
38286
- }
38287
- function extentsSettable(m) {
38288
- return m.mode === MM_ISOTROPIC || m.mode === MM_ANISOTROPIC;
38289
- }
38290
- function setWindowExt(m, cx, cy) {
38291
- if (!extentsSettable(m) || cx === 0 || cy === 0) {
38292
- return;
38293
- }
38294
- m.winExt = { cx, cy };
38295
- fixIsotropic(m);
38296
- }
38297
- function setViewportExt(m, cx, cy) {
38298
- if (!extentsSettable(m) || cx === 0 || cy === 0) {
38299
- return;
38300
- }
38301
- m.vpExt = { cx, cy };
38302
- fixIsotropic(m);
38303
- }
38304
- function scaled(v, num, den) {
38305
- return Math.trunc(v * num / den);
38306
- }
38307
- function scaleWindowExt(m, xNum, xDen, yNum, yDen) {
38308
- if (!extentsSettable(m) || !xDen || !yDen) {
38309
- return;
38310
- }
38311
- const cx = scaled(m.winExt.cx, xNum, xDen);
38312
- const cy = scaled(m.winExt.cy, yNum, yDen);
38313
- if (cx === 0 || cy === 0) {
38314
- return;
38315
- }
38316
- m.winExt = { cx, cy };
38317
- fixIsotropic(m);
38318
- }
38319
- function scaleViewportExt(m, xNum, xDen, yNum, yDen) {
38320
- if (!extentsSettable(m) || !xDen || !yDen) {
38321
- return;
38322
- }
38323
- const cx = scaled(m.vpExt.cx, xNum, xDen);
38324
- const cy = scaled(m.vpExt.cy, yNum, yDen);
38325
- if (cx === 0 || cy === 0) {
38326
- return;
38327
- }
38328
- m.vpExt = { cx, cy };
38329
- fixIsotropic(m);
38330
- }
38331
- function applyWmfMapping(rCtx, m, kx, ky, mirrorWidth = 0) {
38332
- rCtx.wmfLayoutRtl = mirrorWidth > 0;
38333
- rCtx.useMappingMode = true;
38334
- rCtx.windowOrg = { x: m.winOrg.x, y: m.winOrg.y };
38335
- rCtx.windowExt = { cx: m.winExt.cx || 1, cy: m.winExt.cy || 1 };
38336
- rCtx.viewportOrg = { x: m.vpOrg.x * kx, y: m.vpOrg.y * ky };
38337
- rCtx.viewportExt = { cx: (m.vpExt.cx || 1) * kx, cy: (m.vpExt.cy || 1) * ky };
38338
- if (mirrorWidth > 0) {
38339
- rCtx.viewportOrg.x = mirrorWidth * kx - rCtx.viewportOrg.x;
38340
- rCtx.viewportExt.cx = -rCtx.viewportExt.cx;
38341
- }
38342
- }
38343
- function scanMappingRecords(view, start) {
38344
- const out = {
38345
- mode: null,
38346
- winExt: null,
38347
- vpExt: null
38348
- };
38349
- let off = start;
38350
- let guard = 0;
38351
- while (off + 6 <= view.byteLength && guard++ < 1e6) {
38352
- const size = view.getUint32(off, true) * 2;
38353
- const type = view.getUint16(off + 4, true);
38354
- if (size < 6 || off + size > view.byteLength || type === 0) {
38355
- break;
38356
- }
38357
- const d = off + 6;
38358
- if (type === 259 && size >= 8 && out.mode === null) {
38359
- out.mode = view.getUint16(d, true);
38360
- } else if (type === 524 && size >= 10 && !out.winExt) {
38361
- out.winExt = { cy: view.getInt16(d, true), cx: view.getInt16(d + 2, true) };
38362
- } else if (type === 526 && size >= 10 && !out.vpExt) {
38363
- out.vpExt = { cy: view.getInt16(d, true), cx: view.getInt16(d + 2, true) };
38364
- }
38365
- off += size;
38366
- }
38367
- return out;
38368
- }
38369
- function wmfPlayback(view, header, referenceDpi) {
38370
- const valid = (v) => typeof v === "number" && Number.isFinite(v) && v > 0 ? v : DEVICE_DPI;
38371
- const dpi = { x: valid(typeof referenceDpi === "number" ? referenceDpi : referenceDpi?.x), y: valid(typeof referenceDpi === "number" ? referenceDpi : referenceDpi?.y) };
38372
- const density = referenceDpi === void 0 ? {} : { dpi };
38373
- const bw = header.boundsRight - header.boundsLeft;
38374
- const bh = header.boundsBottom - header.boundsTop;
38375
- if (header.placeable !== false) {
38376
- const inch = header.unitsPerInch > 0 ? header.unitsPerInch : DEVICE_DPI;
38377
- const width2 = Math.max(1, Math.round(Math.abs(bw) * dpi.x / inch));
38378
- const height2 = Math.max(1, Math.round(Math.abs(bh) * dpi.y / inch));
38379
- return {
38380
- width: width2,
38381
- height: height2,
38382
- mapping: {
38383
- ...density,
38384
- mode: MM_ANISOTROPIC,
38385
- winOrg: { x: header.boundsLeft, y: header.boundsTop },
38386
- winExt: { cx: bw || 1, cy: bh || 1 },
38387
- vpOrg: { x: 0, y: 0 },
38388
- vpExt: { cx: width2, cy: height2 }
38389
- }
38390
- };
38391
- }
38392
- const scan = scanMappingRecords(view, header.headerSize);
38393
- const scalable = scan.mode === MM_ISOTROPIC || scan.mode === MM_ANISOTROPIC;
38394
- const ext = scalable && scan.vpExt ? scan.vpExt : scan.winExt;
38395
- const width = ext ? Math.abs(ext.cx) : Math.abs(bw);
38396
- const height = ext ? Math.abs(ext.cy) : Math.abs(bh);
38397
- const w = Math.max(1, width || 1);
38398
- const h = Math.max(1, height || 1);
38399
- return {
38400
- width: w,
38401
- height: h,
38402
- mapping: {
38403
- ...density,
38404
- mode: MM_ANISOTROPIC,
38405
- winOrg: { x: 0, y: 0 },
38406
- winExt: { cx: w, cy: h },
38407
- vpOrg: { x: 0, y: 0 },
38408
- vpExt: { cx: w, cy: h }
38409
- }
38410
- };
38411
- }
38412
-
38413
38541
  // src/wmf-emf-bridge.ts
38414
38542
  var EmfRecordWriter = class {
38415
38543
  constructor(type, capacity = 64) {