emf-converter 4.8.8 → 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/index.mjs CHANGED
@@ -35710,21 +35710,243 @@ function handleEmfObjectRecord(rCtx, recType, dataOff, recSize) {
35710
35710
  }
35711
35711
  }
35712
35712
 
35713
+ // src/wmf-mapping.ts
35714
+ var MM_TEXT = 1;
35715
+ var MM_LOMETRIC = 2;
35716
+ var MM_HIMETRIC = 3;
35717
+ var MM_LOENGLISH = 4;
35718
+ var MM_HIENGLISH = 5;
35719
+ var MM_TWIPS = 6;
35720
+ var MM_ISOTROPIC = 7;
35721
+ var MM_ANISOTROPIC = 8;
35722
+ var DEVICE_DPI = 96;
35723
+ function cloneMapping(m) {
35724
+ return {
35725
+ ...m.dpi ? { dpi: { ...m.dpi } } : {},
35726
+ mode: m.mode,
35727
+ winOrg: { ...m.winOrg },
35728
+ winExt: { ...m.winExt },
35729
+ vpOrg: { ...m.vpOrg },
35730
+ vpExt: { ...m.vpExt }
35731
+ };
35732
+ }
35733
+ var METRIC_UNITS_PER_INCH = {
35734
+ [MM_LOMETRIC]: 254,
35735
+ [MM_HIMETRIC]: 2540,
35736
+ [MM_LOENGLISH]: 100,
35737
+ [MM_HIENGLISH]: 1e3,
35738
+ [MM_TWIPS]: 1440
35739
+ };
35740
+ function fixIsotropic(m) {
35741
+ if (m.mode !== MM_ISOTROPIC || !m.winExt.cx || !m.winExt.cy) {
35742
+ return;
35743
+ }
35744
+ const xdim = Math.abs(m.vpExt.cx / m.winExt.cx) / (m.dpi?.x ?? DEVICE_DPI);
35745
+ const ydim = Math.abs(m.vpExt.cy / m.winExt.cy) / (m.dpi?.y ?? DEVICE_DPI);
35746
+ if (xdim > ydim) {
35747
+ const min = m.vpExt.cx >= 0 ? 1 : -1;
35748
+ m.vpExt.cx = Math.floor(m.vpExt.cx * ydim / xdim + 0.5) || min;
35749
+ } else if (ydim > xdim) {
35750
+ const min = m.vpExt.cy >= 0 ? 1 : -1;
35751
+ m.vpExt.cy = Math.floor(m.vpExt.cy * xdim / ydim + 0.5) || min;
35752
+ }
35753
+ }
35754
+ function setMapMode(m, mode) {
35755
+ if (mode < MM_TEXT || mode > MM_ANISOTROPIC) {
35756
+ return;
35757
+ }
35758
+ const units = METRIC_UNITS_PER_INCH[mode === MM_ISOTROPIC ? MM_LOMETRIC : mode];
35759
+ if (mode === MM_TEXT) {
35760
+ m.winExt = { cx: 1, cy: 1 };
35761
+ m.vpExt = { cx: 1, cy: 1 };
35762
+ } else if (units !== void 0 && (mode !== MM_ISOTROPIC || m.mode !== MM_ISOTROPIC)) {
35763
+ m.winExt = { cx: units, cy: units };
35764
+ m.vpExt = { cx: m.dpi?.x ?? DEVICE_DPI, cy: -(m.dpi?.y ?? DEVICE_DPI) };
35765
+ }
35766
+ m.mode = mode;
35767
+ }
35768
+ function extentsSettable(m) {
35769
+ return m.mode === MM_ISOTROPIC || m.mode === MM_ANISOTROPIC;
35770
+ }
35771
+ function setWindowExt(m, cx, cy) {
35772
+ if (!extentsSettable(m) || cx === 0 || cy === 0) {
35773
+ return;
35774
+ }
35775
+ m.winExt = { cx, cy };
35776
+ fixIsotropic(m);
35777
+ }
35778
+ function setViewportExt(m, cx, cy) {
35779
+ if (!extentsSettable(m) || cx === 0 || cy === 0) {
35780
+ return;
35781
+ }
35782
+ m.vpExt = { cx, cy };
35783
+ fixIsotropic(m);
35784
+ }
35785
+ function scaled(v, num, den) {
35786
+ return Math.trunc(v * num / den);
35787
+ }
35788
+ function scaleWindowExt(m, xNum, xDen, yNum, yDen) {
35789
+ if (!extentsSettable(m) || !xDen || !yDen) {
35790
+ return;
35791
+ }
35792
+ const cx = scaled(m.winExt.cx, xNum, xDen);
35793
+ const cy = scaled(m.winExt.cy, yNum, yDen);
35794
+ if (cx === 0 || cy === 0) {
35795
+ return;
35796
+ }
35797
+ m.winExt = { cx, cy };
35798
+ fixIsotropic(m);
35799
+ }
35800
+ function scaleViewportExt(m, xNum, xDen, yNum, yDen) {
35801
+ if (!extentsSettable(m) || !xDen || !yDen) {
35802
+ return;
35803
+ }
35804
+ const cx = scaled(m.vpExt.cx, xNum, xDen);
35805
+ const cy = scaled(m.vpExt.cy, yNum, yDen);
35806
+ if (cx === 0 || cy === 0) {
35807
+ return;
35808
+ }
35809
+ m.vpExt = { cx, cy };
35810
+ fixIsotropic(m);
35811
+ }
35812
+ function applyWmfMapping(rCtx, m, kx, ky, mirrorWidth = 0) {
35813
+ rCtx.wmfLayoutRtl = mirrorWidth > 0;
35814
+ rCtx.useMappingMode = true;
35815
+ rCtx.windowOrg = { x: m.winOrg.x, y: m.winOrg.y };
35816
+ rCtx.windowExt = { cx: m.winExt.cx || 1, cy: m.winExt.cy || 1 };
35817
+ rCtx.viewportOrg = { x: m.vpOrg.x * kx, y: m.vpOrg.y * ky };
35818
+ rCtx.viewportExt = { cx: (m.vpExt.cx || 1) * kx, cy: (m.vpExt.cy || 1) * ky };
35819
+ if (mirrorWidth > 0) {
35820
+ rCtx.viewportOrg.x = mirrorWidth * kx - rCtx.viewportOrg.x;
35821
+ rCtx.viewportExt.cx = -rCtx.viewportExt.cx;
35822
+ }
35823
+ }
35824
+ function scanMappingRecords(view, start) {
35825
+ const out = {
35826
+ mode: null,
35827
+ winExt: null,
35828
+ vpExt: null
35829
+ };
35830
+ let off = start;
35831
+ let guard = 0;
35832
+ while (off + 6 <= view.byteLength && guard++ < 1e6) {
35833
+ const size = view.getUint32(off, true) * 2;
35834
+ const type = view.getUint16(off + 4, true);
35835
+ if (size < 6 || off + size > view.byteLength || type === 0) {
35836
+ break;
35837
+ }
35838
+ const d = off + 6;
35839
+ if (type === 259 && size >= 8 && out.mode === null) {
35840
+ out.mode = view.getUint16(d, true);
35841
+ } else if (type === 524 && size >= 10 && !out.winExt) {
35842
+ out.winExt = { cy: view.getInt16(d, true), cx: view.getInt16(d + 2, true) };
35843
+ } else if (type === 526 && size >= 10 && !out.vpExt) {
35844
+ out.vpExt = { cy: view.getInt16(d, true), cx: view.getInt16(d + 2, true) };
35845
+ }
35846
+ off += size;
35847
+ }
35848
+ return out;
35849
+ }
35850
+ function wmfPlayback(view, header, referenceDpi) {
35851
+ const valid = (v) => typeof v === "number" && Number.isFinite(v) && v > 0 ? v : DEVICE_DPI;
35852
+ const dpi = { x: valid(typeof referenceDpi === "number" ? referenceDpi : referenceDpi?.x), y: valid(typeof referenceDpi === "number" ? referenceDpi : referenceDpi?.y) };
35853
+ const density = referenceDpi === void 0 ? {} : { dpi };
35854
+ const bw = header.boundsRight - header.boundsLeft;
35855
+ const bh = header.boundsBottom - header.boundsTop;
35856
+ if (header.placeable !== false) {
35857
+ const inch = header.unitsPerInch > 0 ? header.unitsPerInch : DEVICE_DPI;
35858
+ const width2 = Math.max(1, Math.round(Math.abs(bw) * dpi.x / inch));
35859
+ const height2 = Math.max(1, Math.round(Math.abs(bh) * dpi.y / inch));
35860
+ return {
35861
+ width: width2,
35862
+ height: height2,
35863
+ mapping: {
35864
+ ...density,
35865
+ mode: MM_ANISOTROPIC,
35866
+ winOrg: { x: header.boundsLeft, y: header.boundsTop },
35867
+ winExt: { cx: bw || 1, cy: bh || 1 },
35868
+ vpOrg: { x: 0, y: 0 },
35869
+ vpExt: { cx: width2, cy: height2 }
35870
+ }
35871
+ };
35872
+ }
35873
+ const scan = scanMappingRecords(view, header.headerSize);
35874
+ const scalable = scan.mode === MM_ISOTROPIC || scan.mode === MM_ANISOTROPIC;
35875
+ const ext = scalable && scan.vpExt ? scan.vpExt : scan.winExt;
35876
+ const width = ext ? Math.abs(ext.cx) : Math.abs(bw);
35877
+ const height = ext ? Math.abs(ext.cy) : Math.abs(bh);
35878
+ const w = Math.max(1, width || 1);
35879
+ const h = Math.max(1, height || 1);
35880
+ return {
35881
+ width: w,
35882
+ height: h,
35883
+ mapping: {
35884
+ ...density,
35885
+ mode: MM_ANISOTROPIC,
35886
+ winOrg: { x: 0, y: 0 },
35887
+ winExt: { cx: w, cy: h },
35888
+ vpOrg: { x: 0, y: 0 },
35889
+ vpExt: { cx: w, cy: h }
35890
+ }
35891
+ };
35892
+ }
35893
+
35713
35894
  // src/emf-gdi-transform-handlers.ts
35714
- function extentsSettable(rCtx) {
35715
- return rCtx.mapMode === 7 || rCtx.mapMode === 8;
35895
+ function emfDeviceDpi(view) {
35896
+ if (view.byteLength < 88 || view.getUint32(0, true) !== 1) {
35897
+ return void 0;
35898
+ }
35899
+ const devW = view.getInt32(72, true);
35900
+ const devH = view.getInt32(76, true);
35901
+ const mmW = view.getInt32(80, true);
35902
+ const mmH = view.getInt32(84, true);
35903
+ if (devW <= 0 || devH <= 0 || mmW <= 0 || mmH <= 0) {
35904
+ return void 0;
35905
+ }
35906
+ return { x: devW * 25.4 / mmW, y: devH * 25.4 / mmH };
35907
+ }
35908
+ function emfMapping(r) {
35909
+ const dpi = emfDeviceDpi(r.view);
35910
+ return {
35911
+ ...dpi ? { dpi } : {},
35912
+ mode: r.mapMode ?? MM_TEXT,
35913
+ winOrg: r.windowOrg,
35914
+ winExt: { ...r.windowExt },
35915
+ vpOrg: r.viewportOrg,
35916
+ vpExt: { ...r.viewportExt }
35917
+ };
35918
+ }
35919
+ function storeMapping(r, m) {
35920
+ r.mapMode = m.mode;
35921
+ r.windowExt = m.winExt;
35922
+ r.viewportExt = m.vpExt;
35923
+ }
35924
+ function setExtent(r, viewport, cx, cy) {
35925
+ const m = emfMapping(r);
35926
+ if (!extentsSettable(m) || cx === 0 || cy === 0) {
35927
+ return;
35928
+ }
35929
+ if (viewport) {
35930
+ m.vpExt = { cx, cy };
35931
+ } else {
35932
+ m.winExt = { cx, cy };
35933
+ }
35934
+ fixIsotropic(m);
35935
+ storeMapping(r, m);
35936
+ activateGdiMappingMode(r);
35937
+ }
35938
+ function scaleExtent(r, viewport, xNum, xDenom, yNum, yDenom) {
35939
+ const ext = viewport ? r.viewportExt : r.windowExt;
35940
+ const cx = xDenom !== 0 ? Math.round(ext.cx * xNum / xDenom) : ext.cx;
35941
+ const cy = yDenom !== 0 ? Math.round(ext.cy * yNum / yDenom) : ext.cy;
35942
+ setExtent(r, viewport, cx, cy);
35716
35943
  }
35717
35944
  function handleCoordinateRecord(rCtx, recType, dataOff, recSize) {
35718
35945
  const { view } = rCtx;
35719
35946
  switch (recType) {
35720
35947
  case EMR_SETWINDOWEXTEX: {
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
- }
35948
+ if (recSize >= 16) {
35949
+ setExtent(rCtx, false, view.getInt32(dataOff, true), view.getInt32(dataOff + 4, true));
35728
35950
  }
35729
35951
  return true;
35730
35952
  }
@@ -35737,13 +35959,8 @@ function handleCoordinateRecord(rCtx, recType, dataOff, recSize) {
35737
35959
  return true;
35738
35960
  }
35739
35961
  case EMR_SETVIEWPORTEXTEX: {
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
- }
35962
+ if (recSize >= 16) {
35963
+ setExtent(rCtx, true, view.getInt32(dataOff, true), view.getInt32(dataOff + 4, true));
35747
35964
  }
35748
35965
  return true;
35749
35966
  }
@@ -35758,48 +35975,26 @@ function handleCoordinateRecord(rCtx, recType, dataOff, recSize) {
35758
35975
  case EMR_SETMAPMODE: {
35759
35976
  if (recSize >= 12) {
35760
35977
  const mode = view.getUint32(dataOff, true);
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
- }
35770
- }
35771
- }
35772
- return true;
35773
- }
35774
- case EMR_SCALEVIEWPORTEXTEX: {
35775
- if (recSize >= 24 && extentsSettable(rCtx)) {
35776
- const xNum = view.getInt32(dataOff, true);
35777
- const xDenom = view.getInt32(dataOff + 4, true);
35778
- const yNum = view.getInt32(dataOff + 8, true);
35779
- const yDenom = view.getInt32(dataOff + 12, true);
35780
- if (xDenom !== 0) {
35781
- rCtx.viewportExt.cx = Math.round(rCtx.viewportExt.cx * xNum / xDenom);
35782
- }
35783
- if (yDenom !== 0) {
35784
- rCtx.viewportExt.cy = Math.round(rCtx.viewportExt.cy * yNum / yDenom);
35978
+ if (mode >= MM_TEXT && mode <= MM_ANISOTROPIC) {
35979
+ const m = emfMapping(rCtx);
35980
+ setMapMode(m, mode);
35981
+ storeMapping(rCtx, m);
35982
+ activateGdiMappingMode(rCtx);
35785
35983
  }
35786
- activateGdiMappingMode(rCtx);
35787
35984
  }
35788
35985
  return true;
35789
35986
  }
35987
+ case EMR_SCALEVIEWPORTEXTEX:
35790
35988
  case EMR_SCALEWINDOWEXTEX: {
35791
- if (recSize >= 24 && extentsSettable(rCtx)) {
35792
- const xNum = view.getInt32(dataOff, true);
35793
- const xDenom = view.getInt32(dataOff + 4, true);
35794
- const yNum = view.getInt32(dataOff + 8, true);
35795
- const yDenom = view.getInt32(dataOff + 12, true);
35796
- if (xDenom !== 0) {
35797
- rCtx.windowExt.cx = Math.round(rCtx.windowExt.cx * xNum / xDenom);
35798
- }
35799
- if (yDenom !== 0) {
35800
- rCtx.windowExt.cy = Math.round(rCtx.windowExt.cy * yNum / yDenom);
35801
- }
35802
- activateGdiMappingMode(rCtx);
35989
+ if (recSize >= 24) {
35990
+ scaleExtent(
35991
+ rCtx,
35992
+ recType === EMR_SCALEVIEWPORTEXTEX,
35993
+ view.getInt32(dataOff, true),
35994
+ view.getInt32(dataOff + 4, true),
35995
+ view.getInt32(dataOff + 8, true),
35996
+ view.getInt32(dataOff + 12, true)
35997
+ );
35803
35998
  }
35804
35999
  return true;
35805
36000
  }
@@ -36791,24 +36986,22 @@ function rgbaToHex(c) {
36791
36986
  }
36792
36987
  return /^#[0-9a-f]{6}$/i.test(c) ? c : null;
36793
36988
  }
36794
- function drawPlusStringWithEngine(rCtx, font, text, layoutX, layoutY, paint, alignment, format, brushSampler2 = null) {
36989
+ function realizePlusEngineFont(rCtx, font, paint, brushSampler2) {
36795
36990
  const fonts = rCtx.fonts;
36796
36991
  const color = typeof paint === "string" ? rgbaToHex(paint) : null;
36797
- const hintNow = rCtx.textRenderingHint ?? 0;
36798
- const graySmooth = hintNow === 3 || hintNow === 4 || hintNow === 5;
36992
+ const hint = rCtx.textRenderingHint ?? 0;
36993
+ const graySmooth = hint === 3 || hint === 4 || hint === 5;
36799
36994
  const solidViaCoverage = !!color && graySmooth && !isSvgContext(rCtx.ctx);
36800
36995
  const sampler = isSvgContext(rCtx.ctx) ? null : solidViaCoverage ? solidSampler(cssColorToArgb(paint) ?? 4278190080) : color ? null : brushSampler2;
36801
36996
  const unit = font.unit ?? 0;
36802
- if (!fonts || !color && !sampler || alignment !== 0 || unit !== 0 && unit !== 2) {
36803
- return false;
36997
+ if (!fonts || !color && !sampler || unit !== 0 && unit !== 2) {
36998
+ return null;
36804
36999
  }
36805
37000
  const m = plusWorldMatrix(rCtx);
36806
37001
  if (m[1] !== 0 || m[2] !== 0 || m[0] <= 0 || m[3] <= 0) {
36807
- return false;
37002
+ return null;
36808
37003
  }
36809
37004
  const emPx = font.emSize * m[3];
36810
- const hint = rCtx.textRenderingHint ?? 0;
36811
- const quality = HINT_QUALITY[hint] ?? NONANTIALIASED_QUALITY;
36812
37005
  const spec = {
36813
37006
  face: font.family,
36814
37007
  height: -Math.round(emPx),
@@ -36817,7 +37010,7 @@ function drawPlusStringWithEngine(rCtx, font, text, layoutX, layoutY, paint, ali
36817
37010
  italic: (font.flags & 2) !== 0,
36818
37011
  charSet: 1,
36819
37012
  pitchAndFamily: 0,
36820
- quality,
37013
+ quality: HINT_QUALITY[hint] ?? NONANTIALIASED_QUALITY,
36821
37014
  unhinted: hint === 2 || hint === 4,
36822
37015
  // AntiAlias ignores the font's gasp table (measured: Arial 16 px is
36823
37016
  // grayscale there, but single-bit under AntiAliasGridFit, as in GDI).
@@ -36825,31 +37018,32 @@ function drawPlusStringWithEngine(rCtx, font, text, layoutX, layoutY, paint, ali
36825
37018
  };
36826
37019
  const realized = fonts.realize(spec, rCtx.fontFamilyMap);
36827
37020
  if (!realized) {
36828
- return false;
36829
- }
36830
- const ttf = realized.ttf;
36831
- const unhinted = spec.unhinted === true;
36832
- const exactAscent = emPx * ttf.winAscent / ttf.unitsPerEm;
36833
- const ascent = unhinted ? exactAscent : Math.ceil(exactAscent);
36834
- const x = m[0] * layoutX + m[4] + emPx * (format?.leadingMargin ?? PLUS_LEADING_EM);
36835
- const y = m[3] * layoutY + m[5] + ascent;
36836
- const codes = [];
36837
- for (let i = 0; i < text.length; i++) {
36838
- codes.push(text.charCodeAt(i));
37021
+ return null;
36839
37022
  }
36840
- const tracking = format?.tracking ?? PLUS_DEFAULT_TRACKING;
36841
- const dx = unhinted ? codes.map((c) => realized.advance(realized.glyphIndex(c)) * tracking) : null;
36842
- if ((!color || solidViaCoverage) && sampler) {
36843
- const cov = gdiTextCoverage(realized, {
36844
- codes,
36845
- glyphIndices: false,
36846
- x,
36847
- y,
36848
- dx,
36849
- dy: null,
36850
- textAlign: 24,
36851
- options: 0,
36852
- matrix: null});
37023
+ return {
37024
+ realized,
37025
+ emPx,
37026
+ m,
37027
+ unhinted: spec.unhinted === true,
37028
+ color,
37029
+ sampler,
37030
+ solidViaCoverage,
37031
+ underline: (font.flags & 4) !== 0,
37032
+ strikeOut: (font.flags & 8) !== 0
37033
+ };
37034
+ }
37035
+ function paintPlusEngineRun(rCtx, ef, run) {
37036
+ const base = {
37037
+ ...run,
37038
+ textAlign: 24,
37039
+ bkColor: "#ffffff",
37040
+ bkMode: 1,
37041
+ options: 0,
37042
+ rect: null,
37043
+ matrix: null
37044
+ };
37045
+ if ((!ef.color || ef.solidViaCoverage) && ef.sampler) {
37046
+ const cov = gdiTextCoverage(ef.realized, { ...base});
36853
37047
  if (!cov) {
36854
37048
  return true;
36855
37049
  }
@@ -36859,7 +37053,7 @@ function drawPlusStringWithEngine(rCtx, font, text, layoutX, layoutY, paint, ali
36859
37053
  }
36860
37054
  return compositeBrushCoverage(
36861
37055
  rCtx,
36862
- sampler,
37056
+ ef.sampler,
36863
37057
  { x: cov.x, y: cov.y, w: cov.width, h: cov.height },
36864
37058
  cov.data,
36865
37059
  cov.channels,
@@ -36867,25 +37061,82 @@ function drawPlusStringWithEngine(rCtx, font, text, layoutX, layoutY, paint, ali
36867
37061
  cov.channels === 3 ? gamma : 1
36868
37062
  );
36869
37063
  }
36870
- paintGdiTextRun(rCtx.ctx, realized, {
36871
- codes,
36872
- glyphIndices: false,
36873
- x,
36874
- y,
36875
- dx,
36876
- dy: null,
36877
- textAlign: 24,
36878
- textColor: color,
36879
- bkColor: "#ffffff",
36880
- bkMode: 1,
36881
- options: 0,
36882
- rect: null,
36883
- matrix: null,
36884
- underline: (font.flags & 4) !== 0,
36885
- strikeOut: (font.flags & 8) !== 0
36886
- }, rCtx.fontFamilyMap);
37064
+ paintGdiTextRun(
37065
+ rCtx.ctx,
37066
+ ef.realized,
37067
+ { ...base, textColor: ef.color, underline: ef.underline, strikeOut: ef.strikeOut },
37068
+ rCtx.fontFamilyMap
37069
+ );
36887
37070
  return true;
36888
37071
  }
37072
+ function drawPlusStringWithEngine(rCtx, font, text, layoutX, layoutY, paint, alignment, format, brushSampler2 = null) {
37073
+ if (alignment !== 0) {
37074
+ return false;
37075
+ }
37076
+ const ef = realizePlusEngineFont(rCtx, font, paint, brushSampler2);
37077
+ if (!ef) {
37078
+ return false;
37079
+ }
37080
+ const { realized, emPx, m, unhinted } = ef;
37081
+ const ttf = realized.ttf;
37082
+ const exactAscent = emPx * ttf.winAscent / ttf.unitsPerEm;
37083
+ const ascent = unhinted ? exactAscent : Math.ceil(exactAscent);
37084
+ const x = m[0] * layoutX + m[4] + emPx * (format?.leadingMargin ?? PLUS_LEADING_EM);
37085
+ const y = m[3] * layoutY + m[5] + ascent;
37086
+ const codes = [];
37087
+ for (let i = 0; i < text.length; i++) {
37088
+ codes.push(text.charCodeAt(i));
37089
+ }
37090
+ const tracking = format?.tracking ?? PLUS_DEFAULT_TRACKING;
37091
+ const dx = unhinted ? codes.map((c) => realized.advance(realized.glyphIndex(c)) * tracking) : null;
37092
+ return paintPlusEngineRun(rCtx, ef, { codes, glyphIndices: false, x, y, dx, dy: null });
37093
+ }
37094
+ var DRIVER_STRING_CMAP_LOOKUP = 1;
37095
+ var DRIVER_STRING_VERTICAL = 2;
37096
+ var DRIVER_STRING_REALIZED_ADVANCE = 4;
37097
+ function drawPlusDriverString(rCtx, recFlags, brushVal, font, codes, positions, options2) {
37098
+ const cmap = (options2 & DRIVER_STRING_CMAP_LOOKUP) !== 0;
37099
+ const realizedAdvance = (options2 & DRIVER_STRING_REALIZED_ADVANCE) !== 0;
37100
+ if (!(options2 & DRIVER_STRING_VERTICAL)) {
37101
+ const paint = resolveBrushPaint(rCtx, recFlags, brushVal);
37102
+ const ef = realizePlusEngineFont(rCtx, font, paint, rCtx.fonts ? deviceBrushSampler(rCtx, recFlags, brushVal) : null);
37103
+ if (ef) {
37104
+ const { m } = ef;
37105
+ const dev = positions.map((p) => ({ x: m[0] * p.x + m[4], y: m[3] * p.y + m[5] }));
37106
+ const step = (axis) => dev.map((p, i) => i + 1 < dev.length ? dev[i + 1][axis] - p[axis] : 0);
37107
+ paintPlusEngineRun(rCtx, ef, {
37108
+ codes,
37109
+ glyphIndices: !cmap,
37110
+ x: dev[0].x,
37111
+ y: dev[0].y,
37112
+ dx: realizedAdvance ? null : step("x"),
37113
+ dy: realizedAdvance ? null : step("y")
37114
+ });
37115
+ return;
37116
+ }
37117
+ }
37118
+ if (!cmap) {
37119
+ return;
37120
+ }
37121
+ const { ctx } = rCtx;
37122
+ const bold = font.flags & 1 ? "bold " : "";
37123
+ const italic = font.flags & 2 ? "italic " : "";
37124
+ const family = mapFontFamily(font.family, rCtx.fontFamilyMap);
37125
+ const setFont = () => {
37126
+ ctx.font = `${italic}${bold}${font.emSize}px ${family}`;
37127
+ ctx.textBaseline = "alphabetic";
37128
+ ctx.textAlign = "left";
37129
+ };
37130
+ if (realizedAdvance) {
37131
+ setFont();
37132
+ fillPlusText(rCtx, recFlags, brushVal, String.fromCharCode(...codes), positions[0].x, positions[0].y, font.emSize);
37133
+ return;
37134
+ }
37135
+ for (let i = 0; i < codes.length; i++) {
37136
+ setFont();
37137
+ fillPlusText(rCtx, recFlags, brushVal, String.fromCharCode(codes[i]), positions[i].x, positions[i].y, font.emSize);
37138
+ }
37139
+ }
36889
37140
  function handleEmfPlusTextImageRecord(rCtx, recType, recFlags, dataOff, recDataSize) {
36890
37141
  const { ctx, view, objectTable } = rCtx;
36891
37142
  switch (recType) {
@@ -36969,24 +37220,31 @@ function handleEmfPlusTextImageRecord(rCtx, recType, recFlags, dataOff, recDataS
36969
37220
  case EMFPLUS_DRAWDRIVERSTRING: {
36970
37221
  if (recDataSize >= 16) {
36971
37222
  const brushVal = view.getUint32(dataOff, true);
37223
+ const options2 = view.getUint32(dataOff + 4, true);
37224
+ const matrixPresent = view.getUint32(dataOff + 8, true) !== 0;
36972
37225
  const glyphCount = view.getUint32(dataOff + 12, true);
36973
- const fontId = recFlags & 255;
36974
- const font = objectTable.get(fontId);
37226
+ const font = objectTable.get(recFlags & 255);
36975
37227
  const glyphsOff = dataOff + 16;
36976
37228
  const posOff = glyphsOff + glyphCount * 2;
36977
- const alignedPosOff = posOff + 3 & -4;
36978
- if (glyphCount > 0 && glyphCount < 1e5 && alignedPosOff + glyphCount * 8 <= dataOff + recDataSize && font && font.kind === "plus-font") {
36979
- const text = readUtf16LE(view, glyphsOff, glyphCount);
36980
- if (text.length > 0) {
36981
- const bold = font.flags & 1 ? "bold " : "";
36982
- const italic = font.flags & 2 ? "italic " : "";
36983
- const family = mapFontFamily(font.family, rCtx.fontFamilyMap);
36984
- ctx.font = `${italic}${bold}${font.emSize}px ${family}`;
36985
- ctx.textBaseline = "alphabetic";
36986
- ctx.textAlign = "left";
36987
- const gx = view.getFloat32(alignedPosOff, true);
36988
- const gy = view.getFloat32(alignedPosOff + 4, true);
36989
- fillPlusText(rCtx, recFlags, brushVal, text, gx, gy, font.emSize);
37229
+ const matrixOff = posOff + glyphCount * 8;
37230
+ if (glyphCount > 0 && glyphCount < 1e5 && matrixOff + (matrixPresent ? 24 : 0) <= dataOff + recDataSize && font && font.kind === "plus-font") {
37231
+ const codes = [];
37232
+ const positions = [];
37233
+ for (let i = 0; i < glyphCount; i++) {
37234
+ codes.push(view.getUint16(glyphsOff + i * 2, true));
37235
+ positions.push({ x: view.getFloat32(posOff + i * 8, true), y: view.getFloat32(posOff + i * 8 + 4, true) });
37236
+ }
37237
+ const world = rCtx.worldTransform;
37238
+ if (matrixPresent) {
37239
+ const xf = [0, 4, 8, 12, 16, 20].map((k) => view.getFloat32(matrixOff + k, true));
37240
+ if (xf.every(Number.isFinite)) {
37241
+ rCtx.worldTransform = mulMatrix(world, xf);
37242
+ }
37243
+ }
37244
+ try {
37245
+ drawPlusDriverString(rCtx, recFlags, brushVal, font, codes, positions, options2);
37246
+ } finally {
37247
+ rCtx.worldTransform = world;
36990
37248
  }
36991
37249
  }
36992
37250
  }
@@ -37765,7 +38023,7 @@ function parseEmfPlusRegionObject(view, off, maxLen) {
37765
38023
  }
37766
38024
  view.getUint32(off, true);
37767
38025
  const regionNodeCount = view.getUint32(off + 4, true);
37768
- if (regionNodeCount === 0 || regionNodeCount > 1e5) {
38026
+ if (regionNodeCount > 1e5) {
37769
38027
  return null;
37770
38028
  }
37771
38029
  const endOff = off + maxLen;
@@ -38281,187 +38539,6 @@ function extractEmbeddedEmf(view, start) {
38281
38539
  return out.buffer;
38282
38540
  }
38283
38541
 
38284
- // src/wmf-mapping.ts
38285
- var MM_TEXT = 1;
38286
- var MM_LOMETRIC = 2;
38287
- var MM_HIMETRIC = 3;
38288
- var MM_LOENGLISH = 4;
38289
- var MM_HIENGLISH = 5;
38290
- var MM_TWIPS = 6;
38291
- var MM_ISOTROPIC = 7;
38292
- var MM_ANISOTROPIC = 8;
38293
- var DEVICE_DPI = 96;
38294
- function cloneMapping(m) {
38295
- return {
38296
- ...m.dpi ? { dpi: { ...m.dpi } } : {},
38297
- mode: m.mode,
38298
- winOrg: { ...m.winOrg },
38299
- winExt: { ...m.winExt },
38300
- vpOrg: { ...m.vpOrg },
38301
- vpExt: { ...m.vpExt }
38302
- };
38303
- }
38304
- var METRIC_UNITS_PER_INCH = {
38305
- [MM_LOMETRIC]: 254,
38306
- [MM_HIMETRIC]: 2540,
38307
- [MM_LOENGLISH]: 100,
38308
- [MM_HIENGLISH]: 1e3,
38309
- [MM_TWIPS]: 1440
38310
- };
38311
- function fixIsotropic(m) {
38312
- if (m.mode !== MM_ISOTROPIC || !m.winExt.cx || !m.winExt.cy) {
38313
- return;
38314
- }
38315
- const xdim = Math.abs(m.vpExt.cx / m.winExt.cx) / (m.dpi?.x ?? DEVICE_DPI);
38316
- const ydim = Math.abs(m.vpExt.cy / m.winExt.cy) / (m.dpi?.y ?? DEVICE_DPI);
38317
- if (xdim > ydim) {
38318
- const min = m.vpExt.cx >= 0 ? 1 : -1;
38319
- m.vpExt.cx = Math.floor(m.vpExt.cx * ydim / xdim + 0.5) || min;
38320
- } else if (ydim > xdim) {
38321
- const min = m.vpExt.cy >= 0 ? 1 : -1;
38322
- m.vpExt.cy = Math.floor(m.vpExt.cy * xdim / ydim + 0.5) || min;
38323
- }
38324
- }
38325
- function setMapMode(m, mode) {
38326
- if (mode < MM_TEXT || mode > MM_ANISOTROPIC) {
38327
- return;
38328
- }
38329
- const units = METRIC_UNITS_PER_INCH[mode === MM_ISOTROPIC ? MM_LOMETRIC : mode];
38330
- if (mode === MM_TEXT) {
38331
- m.winExt = { cx: 1, cy: 1 };
38332
- m.vpExt = { cx: 1, cy: 1 };
38333
- } else if (units !== void 0 && (mode !== MM_ISOTROPIC || m.mode !== MM_ISOTROPIC)) {
38334
- m.winExt = { cx: units, cy: units };
38335
- m.vpExt = { cx: m.dpi?.x ?? DEVICE_DPI, cy: -(m.dpi?.y ?? DEVICE_DPI) };
38336
- }
38337
- m.mode = mode;
38338
- }
38339
- function extentsSettable2(m) {
38340
- return m.mode === MM_ISOTROPIC || m.mode === MM_ANISOTROPIC;
38341
- }
38342
- function setWindowExt(m, cx, cy) {
38343
- if (!extentsSettable2(m) || cx === 0 || cy === 0) {
38344
- return;
38345
- }
38346
- m.winExt = { cx, cy };
38347
- fixIsotropic(m);
38348
- }
38349
- function setViewportExt(m, cx, cy) {
38350
- if (!extentsSettable2(m) || cx === 0 || cy === 0) {
38351
- return;
38352
- }
38353
- m.vpExt = { cx, cy };
38354
- fixIsotropic(m);
38355
- }
38356
- function scaled(v, num, den) {
38357
- return Math.trunc(v * num / den);
38358
- }
38359
- function scaleWindowExt(m, xNum, xDen, yNum, yDen) {
38360
- if (!extentsSettable2(m) || !xDen || !yDen) {
38361
- return;
38362
- }
38363
- const cx = scaled(m.winExt.cx, xNum, xDen);
38364
- const cy = scaled(m.winExt.cy, yNum, yDen);
38365
- if (cx === 0 || cy === 0) {
38366
- return;
38367
- }
38368
- m.winExt = { cx, cy };
38369
- fixIsotropic(m);
38370
- }
38371
- function scaleViewportExt(m, xNum, xDen, yNum, yDen) {
38372
- if (!extentsSettable2(m) || !xDen || !yDen) {
38373
- return;
38374
- }
38375
- const cx = scaled(m.vpExt.cx, xNum, xDen);
38376
- const cy = scaled(m.vpExt.cy, yNum, yDen);
38377
- if (cx === 0 || cy === 0) {
38378
- return;
38379
- }
38380
- m.vpExt = { cx, cy };
38381
- fixIsotropic(m);
38382
- }
38383
- function applyWmfMapping(rCtx, m, kx, ky, mirrorWidth = 0) {
38384
- rCtx.wmfLayoutRtl = mirrorWidth > 0;
38385
- rCtx.useMappingMode = true;
38386
- rCtx.windowOrg = { x: m.winOrg.x, y: m.winOrg.y };
38387
- rCtx.windowExt = { cx: m.winExt.cx || 1, cy: m.winExt.cy || 1 };
38388
- rCtx.viewportOrg = { x: m.vpOrg.x * kx, y: m.vpOrg.y * ky };
38389
- rCtx.viewportExt = { cx: (m.vpExt.cx || 1) * kx, cy: (m.vpExt.cy || 1) * ky };
38390
- if (mirrorWidth > 0) {
38391
- rCtx.viewportOrg.x = mirrorWidth * kx - rCtx.viewportOrg.x;
38392
- rCtx.viewportExt.cx = -rCtx.viewportExt.cx;
38393
- }
38394
- }
38395
- function scanMappingRecords(view, start) {
38396
- const out = {
38397
- mode: null,
38398
- winExt: null,
38399
- vpExt: null
38400
- };
38401
- let off = start;
38402
- let guard = 0;
38403
- while (off + 6 <= view.byteLength && guard++ < 1e6) {
38404
- const size = view.getUint32(off, true) * 2;
38405
- const type = view.getUint16(off + 4, true);
38406
- if (size < 6 || off + size > view.byteLength || type === 0) {
38407
- break;
38408
- }
38409
- const d = off + 6;
38410
- if (type === 259 && size >= 8 && out.mode === null) {
38411
- out.mode = view.getUint16(d, true);
38412
- } else if (type === 524 && size >= 10 && !out.winExt) {
38413
- out.winExt = { cy: view.getInt16(d, true), cx: view.getInt16(d + 2, true) };
38414
- } else if (type === 526 && size >= 10 && !out.vpExt) {
38415
- out.vpExt = { cy: view.getInt16(d, true), cx: view.getInt16(d + 2, true) };
38416
- }
38417
- off += size;
38418
- }
38419
- return out;
38420
- }
38421
- function wmfPlayback(view, header, referenceDpi) {
38422
- const valid = (v) => typeof v === "number" && Number.isFinite(v) && v > 0 ? v : DEVICE_DPI;
38423
- const dpi = { x: valid(typeof referenceDpi === "number" ? referenceDpi : referenceDpi?.x), y: valid(typeof referenceDpi === "number" ? referenceDpi : referenceDpi?.y) };
38424
- const density = referenceDpi === void 0 ? {} : { dpi };
38425
- const bw = header.boundsRight - header.boundsLeft;
38426
- const bh = header.boundsBottom - header.boundsTop;
38427
- if (header.placeable !== false) {
38428
- const inch = header.unitsPerInch > 0 ? header.unitsPerInch : DEVICE_DPI;
38429
- const width2 = Math.max(1, Math.round(Math.abs(bw) * dpi.x / inch));
38430
- const height2 = Math.max(1, Math.round(Math.abs(bh) * dpi.y / inch));
38431
- return {
38432
- width: width2,
38433
- height: height2,
38434
- mapping: {
38435
- ...density,
38436
- mode: MM_ANISOTROPIC,
38437
- winOrg: { x: header.boundsLeft, y: header.boundsTop },
38438
- winExt: { cx: bw || 1, cy: bh || 1 },
38439
- vpOrg: { x: 0, y: 0 },
38440
- vpExt: { cx: width2, cy: height2 }
38441
- }
38442
- };
38443
- }
38444
- const scan = scanMappingRecords(view, header.headerSize);
38445
- const scalable = scan.mode === MM_ISOTROPIC || scan.mode === MM_ANISOTROPIC;
38446
- const ext = scalable && scan.vpExt ? scan.vpExt : scan.winExt;
38447
- const width = ext ? Math.abs(ext.cx) : Math.abs(bw);
38448
- const height = ext ? Math.abs(ext.cy) : Math.abs(bh);
38449
- const w = Math.max(1, width || 1);
38450
- const h = Math.max(1, height || 1);
38451
- return {
38452
- width: w,
38453
- height: h,
38454
- mapping: {
38455
- ...density,
38456
- mode: MM_ANISOTROPIC,
38457
- winOrg: { x: 0, y: 0 },
38458
- winExt: { cx: w, cy: h },
38459
- vpOrg: { x: 0, y: 0 },
38460
- vpExt: { cx: w, cy: h }
38461
- }
38462
- };
38463
- }
38464
-
38465
38542
  // src/wmf-emf-bridge.ts
38466
38543
  var EmfRecordWriter = class {
38467
38544
  constructor(type, capacity = 64) {