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