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