emf-converter 4.20.0 → 4.20.2

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
@@ -21483,7 +21483,7 @@ function axisFilter(kernel, scale, copy) {
21483
21483
  return { radius: 0.5, weight: (i, c) => Math.floor(c + 0.5) === i ? 1 : 0 };
21484
21484
  }
21485
21485
  }
21486
- function blendSeparable(rgba, width, box, iu0, wu, iv0, wv, verticalFirst, edge = { wrap: void 0, clamp: null }, integerBicubic = false, premult = false) {
21486
+ function blendSeparable(rgba, width, box, iu0, wu, iv0, wv, verticalFirst, edge = { wrap: void 0, clamp: null }, integerBicubic = false, premult = false, integerPremult = false) {
21487
21487
  const [outer0, outerW, inner0, innerW] = verticalFirst ? [iu0, wu, iv0, wv] : [iv0, wv, iu0, wu];
21488
21488
  const [oMin, oMax, iMin, iMax] = verticalFirst ? [box.x0, box.x1, box.y0, box.y1] : [box.y0, box.y1, box.x0, box.x1];
21489
21489
  let r = 0;
@@ -21524,9 +21524,9 @@ function blendSeparable(rgba, width, box, iu0, wu, iv0, wv, verticalFirst, edge
21524
21524
  }
21525
21525
  const alpha = px[s + 3];
21526
21526
  const wa = wi * alpha / 255;
21527
- lr += premult ? wi * px[s] : integerBicubic ? wi * Math.round(alpha * px[s] / 255) : wa * px[s];
21528
- lg += premult ? wi * px[s + 1] : integerBicubic ? wi * Math.round(alpha * px[s + 1] / 255) : wa * px[s + 1];
21529
- lb += premult ? wi * px[s + 2] : integerBicubic ? wi * Math.round(alpha * px[s + 2] / 255) : wa * px[s + 2];
21527
+ lr += premult ? wi * px[s] : integerBicubic || integerPremult ? wi * Math.round(alpha * px[s] / 255) : wa * px[s];
21528
+ lg += premult ? wi * px[s + 1] : integerBicubic || integerPremult ? wi * Math.round(alpha * px[s + 1] / 255) : wa * px[s + 1];
21529
+ lb += premult ? wi * px[s + 2] : integerBicubic || integerPremult ? wi * Math.round(alpha * px[s + 2] / 255) : wa * px[s + 2];
21530
21530
  la += wi * alpha;
21531
21531
  }
21532
21532
  if (integerBicubic) {
@@ -21718,8 +21718,25 @@ function resampleImage(rgba, width, height, spec, surface, options2 = {}) {
21718
21718
  for (let t = iv0; t <= iv1; t++) {
21719
21719
  wv.push(integerBicubic ? Math.round(fv.weight(t, cv) * 65536) / 65536 : fv.weight(t, cv));
21720
21720
  }
21721
- return blendSeparable(rgba, width, box, iu0, wu, iv0, wv, kernel === "bicubic", edge, integerBicubic, premultSource);
21721
+ return blendSeparable(
21722
+ rgba,
21723
+ width,
21724
+ box,
21725
+ iu0,
21726
+ wu,
21727
+ iv0,
21728
+ wv,
21729
+ kernel === "bicubic",
21730
+ edge,
21731
+ integerBicubic,
21732
+ premultSource,
21733
+ kernel === "bilinear"
21734
+ );
21722
21735
  };
21736
+ if (kernel === "bilinear") {
21737
+ u = Math.floor(u) + Math.round((u - Math.floor(u)) * 2048) / 2048;
21738
+ v = Math.floor(v) + Math.round((v - Math.floor(v)) * 2048) / 2048;
21739
+ }
21723
21740
  let [r, g, b, a] = sampleAt(u, v);
21724
21741
  if (a <= 0) {
21725
21742
  continue;
@@ -21730,7 +21747,7 @@ function resampleImage(rgba, width, height, spec, surface, options2 = {}) {
21730
21747
  b = Math.trunc(b);
21731
21748
  a = Math.trunc(a);
21732
21749
  if (a <= 0) continue;
21733
- } else if (premultSource) {
21750
+ } else if (premultSource || kernel === "bilinear") {
21734
21751
  r = Math.round(r);
21735
21752
  g = Math.round(g);
21736
21753
  b = Math.round(b);
@@ -22628,8 +22645,8 @@ function writeTextureColor(inv2, width, height, rgba, wrap, halfPixel2, dx, dy,
22628
22645
  const cv = inv2[1] * px + inv2[3] * py + inv2[5] - off;
22629
22646
  const iu = Math.floor(cu + 1e-9);
22630
22647
  const iv = Math.floor(cv + 1e-9);
22631
- const fu = Math.max(0, cu - iu);
22632
- const fv = Math.max(0, cv - iv);
22648
+ const fu = Math.round(Math.max(0, cu - iu) * 2048) / 2048;
22649
+ const fv = Math.round(Math.max(0, cv - iv) * 2048) / 2048;
22633
22650
  const clamp2 = wrap === "clamp";
22634
22651
  const mirrorX = wrap === "tile-flip-x" || wrap === "tile-flip-xy";
22635
22652
  const mirrorY = wrap === "tile-flip-y" || wrap === "tile-flip-xy";
@@ -22657,18 +22674,19 @@ function writeTextureColor(inv2, width, height, rgba, wrap, halfPixel2, dx, dy,
22657
22674
  y = wrapTexel(tv, height, mirrorY);
22658
22675
  }
22659
22676
  const s = (y * width + x) * 4;
22660
- const wa = w * rgba[s + 3];
22661
- r += wa * rgba[s];
22662
- g += wa * rgba[s + 1];
22663
- b += wa * rgba[s + 2];
22664
- a += wa;
22677
+ const alpha = rgba[s + 3];
22678
+ r += w * Math.round(rgba[s] * alpha / 255);
22679
+ g += w * Math.round(rgba[s + 1] * alpha / 255);
22680
+ b += w * Math.round(rgba[s + 2] * alpha / 255);
22681
+ a += w * alpha;
22665
22682
  }
22683
+ a = Math.round(a);
22666
22684
  if (a <= 0) {
22667
22685
  return;
22668
22686
  }
22669
- out[o] = r / a;
22670
- out[o + 1] = g / a;
22671
- out[o + 2] = b / a;
22687
+ out[o] = Math.round(Math.round(r) * 255 / a);
22688
+ out[o + 1] = Math.round(Math.round(g) * 255 / a);
22689
+ out[o + 2] = Math.round(Math.round(b) * 255 / a);
22672
22690
  out[o + 3] = a;
22673
22691
  }
22674
22692
  var IDENTITY2 = [1, 0, 0, 1, 0, 0];
@@ -28996,6 +29014,46 @@ function mulDivRound(a, b, c) {
28996
29014
  const d = c > 0 ? Math.floor((a * b + Math.floor(c / 2)) / c) : 2147483647;
28997
29015
  return s < 0 ? -d : d;
28998
29016
  }
29017
+ var MonoStubContour = class {
29018
+ constructor(o) {
29019
+ this.points = Array.from(o.xs, (x, i) => [x / 64 - 0.5, o.ys[i] / 64 - 0.5]);
29020
+ }
29021
+ isStub(flipped, scan, crossing, lo, hi, precision) {
29022
+ const points = this.points;
29023
+ const n = points.length;
29024
+ const point = (i) => flipped ? [points[i][1], points[i][0]] : points[i];
29025
+ let edge = -1;
29026
+ let distance = Infinity;
29027
+ for (let i = 0; i < n; i++) {
29028
+ const a2 = point(i), b2 = point((i + 1) % n);
29029
+ if (a2[1] === b2[1] || scan < Math.min(a2[1], b2[1]) || scan > Math.max(a2[1], b2[1])) continue;
29030
+ const x = a2[0] + (b2[0] - a2[0]) * (scan - a2[1]) / (b2[1] - a2[1]);
29031
+ if (Math.abs(x - crossing) < distance) {
29032
+ distance = Math.abs(x - crossing);
29033
+ edge = i;
29034
+ }
29035
+ }
29036
+ if (edge < 0 || distance > 2 / precision) return null;
29037
+ const trace = (step) => {
29038
+ let i = step === 1 ? (edge + 1) % n : edge;
29039
+ let previous = [crossing, scan];
29040
+ let side = 0;
29041
+ for (let k = 0; k < n; k++, i = (i + step + n) % n) {
29042
+ const p = point(i), dy = p[1] - scan;
29043
+ if (!side && dy) side = Math.sign(dy);
29044
+ if (side && dy * side < 0) {
29045
+ const x = previous[0] + (p[0] - previous[0]) * (scan - previous[1]) / (p[1] - previous[1]);
29046
+ return { side, continues: x <= lo || x >= hi };
29047
+ }
29048
+ if (p[0] <= lo || p[0] >= hi || dy * side >= 1) return { side, continues: true };
29049
+ previous = p;
29050
+ }
29051
+ return { side, continues: false };
29052
+ };
29053
+ const a = trace(1), b = trace(-1);
29054
+ return !(a.side * b.side < 0 && a.continues && b.continues);
29055
+ }
29056
+ };
28999
29057
  function monoBox(o) {
29000
29058
  const n = o.xs.length;
29001
29059
  if (n === 0) {
@@ -29017,18 +29075,13 @@ function monoBox(o) {
29017
29075
  let xMax = Math.floor((cxMax + 32) / 64);
29018
29076
  let yMin = Math.floor((cyMin + 31) / 64);
29019
29077
  let yMax = Math.floor((cyMax + 32) / 64);
29020
- const rem = (v, add) => (v + add & 63) - add;
29021
- if (xMin === xMax) {
29022
- if (rem(cxMin, 31) + rem(cxMax, 32) < 0) xMin -= 1;
29023
- else xMax += 1;
29024
- }
29025
- if (yMin === yMax) {
29026
- if (rem(cyMin, 31) + rem(cyMax, 32) < 0) yMin -= 1;
29027
- else yMax += 1;
29028
- }
29029
- return { xMin, xMax, yMin, yMax };
29078
+ const collapsedX = xMin === xMax;
29079
+ const collapsedY = yMin === yMax;
29080
+ if (collapsedX) xMax += 1;
29081
+ if (collapsedY) yMax += 1;
29082
+ return { xMin, xMax, yMin, yMax, collapsedX, collapsedY };
29030
29083
  }
29031
- function rasterizeMono(o, dropout, fixedBox, stubOvershoot = true) {
29084
+ function rasterizeMono(o, dropout, fixedBox, stubOvershoot = true, smartCrossings26Dot6 = false) {
29032
29085
  const box = fixedBox ?? monoBox(o);
29033
29086
  if (!box) {
29034
29087
  return null;
@@ -29050,6 +29103,15 @@ function rasterizeMono(o, dropout, fixedBox, stubOvershoot = true) {
29050
29103
  const at = (col, row2) => (height - 1 - row2) * width + col;
29051
29104
  const r = new Raster(dropout);
29052
29105
  const P = r.precision;
29106
+ const monoContour = stubOvershoot && (dropout === 1 || dropout === 4 || dropout === 5) && local.endPts.length === 1 && Array.from(local.onCurve).every(Boolean) ? new MonoStubContour(local) : null;
29107
+ const smartPixel = (raster, x1, x2) => {
29108
+ if (monoContour && smartCrossings26Dot6) {
29109
+ const step = P / 64;
29110
+ x1 = Math.round(x1 / step) * step;
29111
+ x2 = Math.round(x2 / step) * step;
29112
+ }
29113
+ return raster.floorPub(Math.floor((x1 + x2 + Math.floor(P * 63 / 64)) / 2));
29114
+ };
29053
29115
  if (r.convert(local, false, 0, (height - 1) * P)) {
29054
29116
  r.sweep(
29055
29117
  (y, x1, x2) => {
@@ -29080,17 +29142,19 @@ function rasterizeMono(o, dropout, fixedBox, stubOvershoot = true) {
29080
29142
  pxl = e2;
29081
29143
  break;
29082
29144
  case 4:
29083
- pxl = r.floorPub(Math.floor((x1 + x2 + Math.floor(P * 63 / 64)) / 2));
29145
+ pxl = smartPixel(r, x1, x2);
29084
29146
  break;
29085
29147
  case 1:
29086
29148
  case 5:
29087
- if (left.next === right && left.height <= 0 && !(stubOvershoot && left.flags & OVERSHOOT_TOP && x2 - x1 >= r.half)) {
29149
+ const stub = monoContour && box.collapsedX ? false : monoContour?.isStub(false, y, x1 / P, e2 / P, e1 / P, P);
29150
+ if (stub === true) return;
29151
+ if (stub == null && left.next === right && left.height <= 0 && !(stubOvershoot && left.flags & OVERSHOOT_TOP && x2 - x1 >= r.half)) {
29088
29152
  return;
29089
29153
  }
29090
- if (right.next === left && left.start === y && !(stubOvershoot && left.flags & OVERSHOOT_BOTTOM && x2 - x1 >= r.half)) {
29154
+ if (stub == null && right.next === left && left.start === y && !(stubOvershoot && left.flags & OVERSHOOT_BOTTOM && x2 - x1 >= r.half)) {
29091
29155
  return;
29092
29156
  }
29093
- pxl = mode === 1 ? e2 : r.floorPub(Math.floor((x1 + x2 + Math.floor(P * 63 / 64)) / 2));
29157
+ pxl = mode === 1 ? e2 : smartPixel(r, x1, x2);
29094
29158
  break;
29095
29159
  default:
29096
29160
  return;
@@ -29139,17 +29203,19 @@ function rasterizeMono(o, dropout, fixedBox, stubOvershoot = true) {
29139
29203
  pxl = e2;
29140
29204
  break;
29141
29205
  case 4:
29142
- pxl = h.floorPub(Math.floor((x1 + x2 + Math.floor(P * 63 / 64)) / 2));
29206
+ pxl = smartPixel(h, x1, x2);
29143
29207
  break;
29144
29208
  case 1:
29145
29209
  case 5:
29146
- if (left.next === right && left.height <= 0 && !(stubOvershoot && left.flags & OVERSHOOT_TOP && x2 - x1 >= h.half)) {
29210
+ const stub = monoContour && box.collapsedY ? false : monoContour?.isStub(true, col, x1 / P, e2 / P, e1 / P, P);
29211
+ if (stub === true) return;
29212
+ if (stub == null && left.next === right && left.height <= 0 && !(stubOvershoot && left.flags & OVERSHOOT_TOP && x2 - x1 >= h.half)) {
29147
29213
  return;
29148
29214
  }
29149
- if (right.next === left && left.start === col && !(stubOvershoot && left.flags & OVERSHOOT_BOTTOM && x2 - x1 >= h.half)) {
29215
+ if (stub == null && right.next === left && left.start === col && !(stubOvershoot && left.flags & OVERSHOOT_BOTTOM && x2 - x1 >= h.half)) {
29150
29216
  return;
29151
29217
  }
29152
- pxl = mode === 1 ? e2 : h.floorPub(Math.floor((x1 + x2 + Math.floor(P * 63 / 64)) / 2));
29218
+ pxl = mode === 1 ? e2 : smartPixel(h, x1, x2);
29153
29219
  break;
29154
29220
  default:
29155
29221
  return;
@@ -29505,6 +29571,7 @@ var RasterRealizedFont = class {
29505
29571
  fsSelection: face.italic ? 1 : 0,
29506
29572
  macStyle: 0,
29507
29573
  winAscent: this.ascent,
29574
+ winDescent: this.descent,
29508
29575
  unitsPerEm: this.ppem || 1
29509
29576
  };
29510
29577
  }
@@ -29692,6 +29759,11 @@ var RealizedFont = class {
29692
29759
  }
29693
29760
  return base + (this.syntheticBold ? 1 : 0);
29694
29761
  }
29762
+ /** GDI+ rounds tracked hmtx advances to font units before device scaling. */
29763
+ designAdvance(index, tracking) {
29764
+ const { advance } = this.ttf.hMetrics(index < this.ttf.numGlyphs ? index : NOTDEF);
29765
+ return Math.round(advance * tracking) * this.ppemX / this.ttf.unitsPerEm + (this.syntheticBold ? 1 : 0);
29766
+ }
29695
29767
  /**
29696
29768
  * Advance of glyph `index` in a font realised with an escapement: GDI
29697
29769
  * does not use the hinted/hdmx widths there but the linearly scaled
@@ -29742,12 +29814,14 @@ var RealizedFont = class {
29742
29814
  }
29743
29815
  let bitmap;
29744
29816
  if (this.mode === "mono") {
29745
- bitmap = rasterizeMono(o, dropoutMode(src.scanSet ? src.scanControl : this.ppem <= DEFAULT_DROPOUT_PPEM, src.scanType));
29817
+ const smartGrid = this.ppemX === this.ppem && !m && !this.syntheticItalic && !subX && !subY;
29818
+ bitmap = rasterizeMono(o, dropoutMode(src.scanSet ? src.scanControl : this.ppem <= DEFAULT_DROPOUT_PPEM, src.scanType), void 0, true, smartGrid);
29746
29819
  } else if (this.mode === "cleartype") {
29747
29820
  const dropout = src.scanSet ? dropoutMode(src.scanControl, src.scanType) : 2;
29748
29821
  bitmap = rasterizeClearType(o, this.gdiPlus, this.gdiPlus ? clearTypeSpread(this.ttf) : void 0, dropout);
29749
29822
  } else {
29750
- bitmap = rasterizeGray(o, src.scanSet ? dropoutMode(src.scanControl, src.scanType) : 2);
29823
+ const dropout = src.scanSet ? dropoutMode(src.scanControl, src.scanType) : 2;
29824
+ bitmap = this.gdiPlus && !this.gridFit && !m && !this.syntheticBold && !this.syntheticItalic && this.ttf.weightClass < 600 && this.ttf.family.toLowerCase() === "courier new" ? rasterizeCourierGray(o, dropout) : rasterizeGray(o, dropout);
29751
29825
  }
29752
29826
  if (bitmap && this.syntheticBold) {
29753
29827
  bitmap = embolden(bitmap, this.mode === "mono" ? 1 : 16);
@@ -30059,6 +30133,23 @@ function resolvePpem(font, height) {
30059
30133
  return ppem;
30060
30134
  }
30061
30135
  var CLEARTYPE_OVERSAMPLE = 6;
30136
+ function rasterizeCourierGray(o, dropout) {
30137
+ const samples = rasterizeSamples(o, 4, 4, 1, dropout, false);
30138
+ if (!samples) return null;
30139
+ const { box, hi } = samples;
30140
+ const width = box.xMax - box.xMin;
30141
+ const height = box.yMax - box.yMin;
30142
+ const data = new Uint8Array(width * height);
30143
+ for (let y = 0; y < hi.height; y++) {
30144
+ const row2 = y * hi.width;
30145
+ for (let x = 0; x < hi.width; x++) {
30146
+ if (hi.data[row2 + x] || x > 0 && hi.data[row2 + x - 1]) {
30147
+ data[Math.floor(y / 4) * width + Math.floor(x / 4)]++;
30148
+ }
30149
+ }
30150
+ }
30151
+ return { width, height, left: box.xMin, top: box.yMax, data };
30152
+ }
30062
30153
  function clearTypeSpread(ttf) {
30063
30154
  if (ttf.family.toLowerCase() !== "courier new") {
30064
30155
  return void 0;
@@ -37314,7 +37405,7 @@ function emitSvgRun(ctx, font, run, glyphs, origins, fontFamilyMap) {
37314
37405
  for (let i = 0; i < origins.length; i++) {
37315
37406
  const code = run.glyphIndices ? font.charForGlyph(glyphs[i]) : run.codes[i];
37316
37407
  if (!run.glyphIndices && !(run.options & ETO_IGNORELANGUAGE) && isC1Control(code)) continue;
37317
- text += String.fromCharCode(code);
37408
+ text += String.fromCodePoint(code);
37318
37409
  visibleOrigins.push(origins[i]);
37319
37410
  }
37320
37411
  if (text.length === 0) return;
@@ -39330,7 +39421,11 @@ function setViewportExt(m, cx, cy) {
39330
39421
  fixIsotropic(m);
39331
39422
  }
39332
39423
  function scaled(v, num, den) {
39333
- return Math.trunc(v * num / den);
39424
+ const product = Math.imul(v, num);
39425
+ if (product === -2147483648 && den === -1) {
39426
+ return 0;
39427
+ }
39428
+ return Math.trunc(product / den);
39334
39429
  }
39335
39430
  function scaleWindowExt(m, xNum, xDen, yNum, yDen) {
39336
39431
  if (!extentsSettable(m) || !xDen || !yDen) {
@@ -39483,10 +39578,16 @@ function setExtent(r, viewport, cx, cy) {
39483
39578
  activateGdiMappingMode(r);
39484
39579
  }
39485
39580
  function scaleExtent(r, viewport, xNum, xDenom, yNum, yDenom) {
39486
- const ext = viewport ? r.viewportExt : r.windowExt;
39487
- const cx = xDenom !== 0 ? Math.round(ext.cx * xNum / xDenom) : ext.cx;
39488
- const cy = yDenom !== 0 ? Math.round(ext.cy * yNum / yDenom) : ext.cy;
39489
- setExtent(r, viewport, cx, cy);
39581
+ const m = emfMapping(r);
39582
+ if (!extentsSettable(m) || !xDenom || !yDenom) {
39583
+ return;
39584
+ }
39585
+ (viewport ? scaleViewportExt : scaleWindowExt)(m, xNum, xDenom, yNum, yDenom);
39586
+ if (r.windowExt.cx === m.winExt.cx && r.windowExt.cy === m.winExt.cy && r.viewportExt.cx === m.vpExt.cx && r.viewportExt.cy === m.vpExt.cy) {
39587
+ return;
39588
+ }
39589
+ storeMapping(r, m);
39590
+ activateGdiMappingMode(r);
39490
39591
  }
39491
39592
  function handleCoordinateRecord(rCtx, recType, dataOff, recSize) {
39492
39593
  const { view } = rCtx;
@@ -40350,6 +40451,35 @@ function surfaceSize5(ctx) {
40350
40451
  return typeof w === "number" && typeof h === "number" && w > 0 && h > 0 ? { w, h } : null;
40351
40452
  }
40352
40453
 
40454
+ // src/emf-plus-text-alignment.ts
40455
+ var SIMPLE_SCRIPT = /^(?:\p{Script=Latin}|\p{Script=Common}|\p{Co})$/u;
40456
+ var SHAPING_CHARACTER = /[\p{M}\p{Cf}\p{Emoji_Presentation}]/u;
40457
+ function layoutPlusAlignedString(font, codes, format, x, y, width, height) {
40458
+ const horizontal = format.alignment, vertical = format.lineAlignment;
40459
+ if (!horizontal && !vertical || horizontal < 0 || horizontal > 2 || vertical < 0 || vertical > 2 || font.gridFit || font.mode !== "gray" || !font.designAdvance || font.ttf.winDescent === void 0 || font.syntheticBold || font.syntheticItalic || format.flags & 3 || ![x, y, width, height].every(Number.isFinite) || width <= 0 || height <= 0 || codes.some((c) => !Number.isInteger(c) || c < 32 || c > 1114111 || c >= 127 && c <= 159 || c === 8232 || c === 8233)) return null;
40460
+ if (codes.some((c) => {
40461
+ const character = String.fromCodePoint(c);
40462
+ return !SIMPLE_SCRIPT.test(character) || SHAPING_CHARACTER.test(character) || font.glyphIndex(c) === 0;
40463
+ })) return null;
40464
+ const em = font.ppem;
40465
+ const tracking = format.tracking ?? 1.03;
40466
+ const leading = em * (format.leadingMargin ?? 1 / 6);
40467
+ const trailing = em * (format.trailingMargin ?? 1 / 6);
40468
+ if (![tracking, leading, trailing].every((v) => Number.isFinite(v) && v >= 0)) return null;
40469
+ if (!(format.flags & 4) && (font.ttf.fsSelection & 1 || font.ttf.macStyle & 2)) return null;
40470
+ const dx = codes.map((c) => font.designAdvance(font.glyphIndex(c), tracking));
40471
+ let measuredCount = codes.length;
40472
+ if (!(format.flags & 2048)) while (measuredCount > 0 && codes[measuredCount - 1] === 32) measuredCount--;
40473
+ const measuredWidth = dx.slice(0, measuredCount).reduce((a, b) => a + b, 0) + leading + trailing;
40474
+ const measuredHeight = em * (font.ttf.winAscent + font.ttf.winDescent) / font.ttf.unitsPerEm + (format.flags & 4 ? 0 : em / 8);
40475
+ if (measuredWidth > width || measuredHeight > height) return null;
40476
+ return {
40477
+ x: Math.fround(x + horizontal / 2 * (width - measuredWidth) + leading),
40478
+ y: Math.fround(y + vertical / 2 * (height - measuredHeight)) + Math.round(em * font.ttf.winAscent / font.ttf.unitsPerEm),
40479
+ dx
40480
+ };
40481
+ }
40482
+
40353
40483
  // src/emf-plus-text-image-handlers.ts
40354
40484
  function imageAttributesWrap(rCtx, attributesId) {
40355
40485
  const obj = attributesId <= 255 ? rCtx.objectTable.get(attributesId) : void 0;
@@ -40618,25 +40748,32 @@ function paintPlusEngineRun(rCtx, ef, run) {
40618
40748
  );
40619
40749
  return true;
40620
40750
  }
40621
- function drawPlusStringWithEngine(rCtx, font, text, layoutX, layoutY, paint, alignment, format, brushSampler2 = null) {
40622
- if (alignment !== 0) {
40623
- return false;
40624
- }
40751
+ function drawPlusStringWithEngine(rCtx, font, text, layoutX, layoutY, layoutW, layoutH, paint, alignment, format, brushSampler2 = null) {
40625
40752
  const ef = realizePlusEngineFont(rCtx, font, paint, brushSampler2);
40626
40753
  if (!ef) {
40627
40754
  return false;
40628
40755
  }
40629
40756
  const { realized, emPx, m, unhinted } = ef;
40757
+ const codes = Array.from(text, (character) => character.codePointAt(0));
40758
+ if (format && (alignment !== 0 || format.lineAlignment !== 0) && unhinted && m[0] === 1 && m[3] === 1 && emPx === realized.ppem) {
40759
+ const aligned = layoutPlusAlignedString(
40760
+ realized,
40761
+ codes,
40762
+ format,
40763
+ layoutX + m[4],
40764
+ layoutY + m[5],
40765
+ layoutW,
40766
+ layoutH
40767
+ );
40768
+ if (aligned) return paintPlusEngineRun(rCtx, ef, { codes, glyphIndices: false, ...aligned, dy: null });
40769
+ }
40770
+ if (alignment !== 0) return false;
40630
40771
  const ttf = realized.ttf;
40631
40772
  const exactAscent = emPx * ttf.winAscent / ttf.unitsPerEm;
40632
40773
  const ascent = unhinted ? Math.round(exactAscent) : Math.ceil(exactAscent);
40633
40774
  let x = m[0] * layoutX + m[4] + emPx * (format?.leadingMargin ?? PLUS_LEADING_EM);
40634
40775
  if (!unhinted) x = Math.round(x);
40635
40776
  const y = m[3] * layoutY + m[5] + ascent;
40636
- const codes = [];
40637
- for (let i = 0; i < text.length; i++) {
40638
- codes.push(text.charCodeAt(i));
40639
- }
40640
40777
  const tracking = format?.tracking ?? PLUS_DEFAULT_TRACKING;
40641
40778
  const dx = unhinted ? codes.map((c) => realized.advance(realized.glyphIndex(c)) * tracking) : null;
40642
40779
  return paintPlusEngineRun(rCtx, ef, { codes, glyphIndices: false, x, y, dx, dy: null });
@@ -40718,8 +40855,8 @@ function handleEmfPlusTextImageRecord(rCtx, recType, recFlags, dataOff, recDataS
40718
40855
  const strLen = view.getUint32(dataOff + 8, true);
40719
40856
  const layoutX = view.getFloat32(dataOff + 12, true);
40720
40857
  const layoutY = view.getFloat32(dataOff + 16, true);
40721
- view.getFloat32(dataOff + 20, true);
40722
- view.getFloat32(dataOff + 24, true);
40858
+ const layoutW = view.getFloat32(dataOff + 20, true);
40859
+ const layoutH = view.getFloat32(dataOff + 24, true);
40723
40860
  const fontId = recFlags & 255;
40724
40861
  const font = objectTable.get(fontId);
40725
40862
  if (strLen > 0 && dataOff + 28 + strLen * 2 <= dataOff + recDataSize) {
@@ -40735,6 +40872,8 @@ function handleEmfPlusTextImageRecord(rCtx, recType, recFlags, dataOff, recDataS
40735
40872
  text,
40736
40873
  layoutX,
40737
40874
  layoutY,
40875
+ layoutW,
40876
+ layoutH,
40738
40877
  paint,
40739
40878
  alignment,
40740
40879
  format,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "emf-converter",
3
- "version": "4.20.0",
3
+ "version": "4.20.2",
4
4
  "description": "Convert EMF/WMF metafile binaries to PNG or JPEG data URLs, or SVG (markup, data URL, React/JSX)",
5
5
  "keywords": [
6
6
  "canvas",