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