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