emf-converter 4.20.0 → 4.20.1

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) {
@@ -29029,7 +29087,7 @@ function monoBox(o) {
29029
29087
  }
29030
29088
  return { xMin, xMax, yMin, yMax };
29031
29089
  }
29032
- function rasterizeMono(o, dropout, fixedBox, stubOvershoot = true) {
29090
+ function rasterizeMono(o, dropout, fixedBox, stubOvershoot = true, smartCrossings26Dot6 = false) {
29033
29091
  const box = fixedBox ?? monoBox(o);
29034
29092
  if (!box) {
29035
29093
  return null;
@@ -29051,6 +29109,15 @@ function rasterizeMono(o, dropout, fixedBox, stubOvershoot = true) {
29051
29109
  const at = (col, row2) => (height - 1 - row2) * width + col;
29052
29110
  const r = new Raster(dropout);
29053
29111
  const P = r.precision;
29112
+ const monoContour = stubOvershoot && (dropout === 1 || dropout === 4 || dropout === 5) && local.endPts.length === 1 && Array.from(local.onCurve).every(Boolean) ? new MonoStubContour(local) : null;
29113
+ const smartPixel = (raster, x1, x2) => {
29114
+ if (monoContour && smartCrossings26Dot6) {
29115
+ const step = P / 64;
29116
+ x1 = Math.round(x1 / step) * step;
29117
+ x2 = Math.round(x2 / step) * step;
29118
+ }
29119
+ return raster.floorPub(Math.floor((x1 + x2 + Math.floor(P * 63 / 64)) / 2));
29120
+ };
29054
29121
  if (r.convert(local, false, 0, (height - 1) * P)) {
29055
29122
  r.sweep(
29056
29123
  (y, x1, x2) => {
@@ -29081,17 +29148,19 @@ function rasterizeMono(o, dropout, fixedBox, stubOvershoot = true) {
29081
29148
  pxl = e2;
29082
29149
  break;
29083
29150
  case 4:
29084
- pxl = r.floorPub(Math.floor((x1 + x2 + Math.floor(P * 63 / 64)) / 2));
29151
+ pxl = smartPixel(r, x1, x2);
29085
29152
  break;
29086
29153
  case 1:
29087
29154
  case 5:
29088
- if (left.next === right && left.height <= 0 && !(stubOvershoot && left.flags & OVERSHOOT_TOP && x2 - x1 >= r.half)) {
29155
+ const stub = monoContour?.isStub(false, y, x1 / P, e2 / P, e1 / P, P);
29156
+ if (stub === true) return;
29157
+ if (stub == null && left.next === right && left.height <= 0 && !(stubOvershoot && left.flags & OVERSHOOT_TOP && x2 - x1 >= r.half)) {
29089
29158
  return;
29090
29159
  }
29091
- if (right.next === left && left.start === y && !(stubOvershoot && left.flags & OVERSHOOT_BOTTOM && x2 - x1 >= r.half)) {
29160
+ if (stub == null && right.next === left && left.start === y && !(stubOvershoot && left.flags & OVERSHOOT_BOTTOM && x2 - x1 >= r.half)) {
29092
29161
  return;
29093
29162
  }
29094
- pxl = mode === 1 ? e2 : r.floorPub(Math.floor((x1 + x2 + Math.floor(P * 63 / 64)) / 2));
29163
+ pxl = mode === 1 ? e2 : smartPixel(r, x1, x2);
29095
29164
  break;
29096
29165
  default:
29097
29166
  return;
@@ -29140,17 +29209,19 @@ function rasterizeMono(o, dropout, fixedBox, stubOvershoot = true) {
29140
29209
  pxl = e2;
29141
29210
  break;
29142
29211
  case 4:
29143
- pxl = h.floorPub(Math.floor((x1 + x2 + Math.floor(P * 63 / 64)) / 2));
29212
+ pxl = smartPixel(h, x1, x2);
29144
29213
  break;
29145
29214
  case 1:
29146
29215
  case 5:
29147
- if (left.next === right && left.height <= 0 && !(stubOvershoot && left.flags & OVERSHOOT_TOP && x2 - x1 >= h.half)) {
29216
+ const stub = monoContour?.isStub(true, col, x1 / P, e2 / P, e1 / P, P);
29217
+ if (stub === true) return;
29218
+ if (stub == null && left.next === right && left.height <= 0 && !(stubOvershoot && left.flags & OVERSHOOT_TOP && x2 - x1 >= h.half)) {
29148
29219
  return;
29149
29220
  }
29150
- if (right.next === left && left.start === col && !(stubOvershoot && left.flags & OVERSHOOT_BOTTOM && x2 - x1 >= h.half)) {
29221
+ if (stub == null && right.next === left && left.start === col && !(stubOvershoot && left.flags & OVERSHOOT_BOTTOM && x2 - x1 >= h.half)) {
29151
29222
  return;
29152
29223
  }
29153
- pxl = mode === 1 ? e2 : h.floorPub(Math.floor((x1 + x2 + Math.floor(P * 63 / 64)) / 2));
29224
+ pxl = mode === 1 ? e2 : smartPixel(h, x1, x2);
29154
29225
  break;
29155
29226
  default:
29156
29227
  return;
@@ -29743,12 +29814,14 @@ var RealizedFont = class {
29743
29814
  }
29744
29815
  let bitmap;
29745
29816
  if (this.mode === "mono") {
29746
- 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);
29747
29819
  } else if (this.mode === "cleartype") {
29748
29820
  const dropout = src.scanSet ? dropoutMode(src.scanControl, src.scanType) : 2;
29749
29821
  bitmap = rasterizeClearType(o, this.gdiPlus, this.gdiPlus ? clearTypeSpread(this.ttf) : void 0, dropout);
29750
29822
  } else {
29751
- 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);
29752
29825
  }
29753
29826
  if (bitmap && this.syntheticBold) {
29754
29827
  bitmap = embolden(bitmap, this.mode === "mono" ? 1 : 16);
@@ -30060,6 +30133,23 @@ function resolvePpem(font, height) {
30060
30133
  return ppem;
30061
30134
  }
30062
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
+ }
30063
30153
  function clearTypeSpread(ttf) {
30064
30154
  if (ttf.family.toLowerCase() !== "courier new") {
30065
30155
  return void 0;
@@ -37315,7 +37405,7 @@ function emitSvgRun(ctx, font, run, glyphs, origins, fontFamilyMap) {
37315
37405
  for (let i = 0; i < origins.length; i++) {
37316
37406
  const code = run.glyphIndices ? font.charForGlyph(glyphs[i]) : run.codes[i];
37317
37407
  if (!run.glyphIndices && !(run.options & ETO_IGNORELANGUAGE) && isC1Control(code)) continue;
37318
- text += String.fromCharCode(code);
37408
+ text += String.fromCodePoint(code);
37319
37409
  visibleOrigins.push(origins[i]);
37320
37410
  }
37321
37411
  if (text.length === 0) return;
@@ -39331,7 +39421,11 @@ function setViewportExt(m, cx, cy) {
39331
39421
  fixIsotropic(m);
39332
39422
  }
39333
39423
  function scaled(v, num, den) {
39334
- 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);
39335
39429
  }
39336
39430
  function scaleWindowExt(m, xNum, xDen, yNum, yDen) {
39337
39431
  if (!extentsSettable(m) || !xDen || !yDen) {
@@ -39484,10 +39578,16 @@ function setExtent(r, viewport, cx, cy) {
39484
39578
  activateGdiMappingMode(r);
39485
39579
  }
39486
39580
  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);
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);
39491
39591
  }
39492
39592
  function handleCoordinateRecord(rCtx, recType, dataOff, recSize) {
39493
39593
  const { view } = rCtx;
@@ -40635,8 +40735,8 @@ function drawPlusStringWithEngine(rCtx, font, text, layoutX, layoutY, paint, ali
40635
40735
  if (!unhinted) x = Math.round(x);
40636
40736
  const y = m[3] * layoutY + m[5] + ascent;
40637
40737
  const codes = [];
40638
- for (let i = 0; i < text.length; i++) {
40639
- codes.push(text.charCodeAt(i));
40738
+ for (const character of text) {
40739
+ codes.push(character.codePointAt(0));
40640
40740
  }
40641
40741
  const tracking = format?.tracking ?? PLUS_DEFAULT_TRACKING;
40642
40742
  const dx = unhinted ? codes.map((c) => realized.advance(realized.glyphIndex(c)) * tracking) : null;
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) {
@@ -29027,7 +29085,7 @@ function monoBox(o) {
29027
29085
  }
29028
29086
  return { xMin, xMax, yMin, yMax };
29029
29087
  }
29030
- function rasterizeMono(o, dropout, fixedBox, stubOvershoot = true) {
29088
+ function rasterizeMono(o, dropout, fixedBox, stubOvershoot = true, smartCrossings26Dot6 = false) {
29031
29089
  const box = fixedBox ?? monoBox(o);
29032
29090
  if (!box) {
29033
29091
  return null;
@@ -29049,6 +29107,15 @@ function rasterizeMono(o, dropout, fixedBox, stubOvershoot = true) {
29049
29107
  const at = (col, row2) => (height - 1 - row2) * width + col;
29050
29108
  const r = new Raster(dropout);
29051
29109
  const P = r.precision;
29110
+ const monoContour = stubOvershoot && (dropout === 1 || dropout === 4 || dropout === 5) && local.endPts.length === 1 && Array.from(local.onCurve).every(Boolean) ? new MonoStubContour(local) : null;
29111
+ const smartPixel = (raster, x1, x2) => {
29112
+ if (monoContour && smartCrossings26Dot6) {
29113
+ const step = P / 64;
29114
+ x1 = Math.round(x1 / step) * step;
29115
+ x2 = Math.round(x2 / step) * step;
29116
+ }
29117
+ return raster.floorPub(Math.floor((x1 + x2 + Math.floor(P * 63 / 64)) / 2));
29118
+ };
29052
29119
  if (r.convert(local, false, 0, (height - 1) * P)) {
29053
29120
  r.sweep(
29054
29121
  (y, x1, x2) => {
@@ -29079,17 +29146,19 @@ function rasterizeMono(o, dropout, fixedBox, stubOvershoot = true) {
29079
29146
  pxl = e2;
29080
29147
  break;
29081
29148
  case 4:
29082
- pxl = r.floorPub(Math.floor((x1 + x2 + Math.floor(P * 63 / 64)) / 2));
29149
+ pxl = smartPixel(r, x1, x2);
29083
29150
  break;
29084
29151
  case 1:
29085
29152
  case 5:
29086
- if (left.next === right && left.height <= 0 && !(stubOvershoot && left.flags & OVERSHOOT_TOP && x2 - x1 >= r.half)) {
29153
+ const stub = monoContour?.isStub(false, y, x1 / P, e2 / P, e1 / P, P);
29154
+ if (stub === true) return;
29155
+ if (stub == null && left.next === right && left.height <= 0 && !(stubOvershoot && left.flags & OVERSHOOT_TOP && x2 - x1 >= r.half)) {
29087
29156
  return;
29088
29157
  }
29089
- if (right.next === left && left.start === y && !(stubOvershoot && left.flags & OVERSHOOT_BOTTOM && x2 - x1 >= r.half)) {
29158
+ if (stub == null && right.next === left && left.start === y && !(stubOvershoot && left.flags & OVERSHOOT_BOTTOM && x2 - x1 >= r.half)) {
29090
29159
  return;
29091
29160
  }
29092
- pxl = mode === 1 ? e2 : r.floorPub(Math.floor((x1 + x2 + Math.floor(P * 63 / 64)) / 2));
29161
+ pxl = mode === 1 ? e2 : smartPixel(r, x1, x2);
29093
29162
  break;
29094
29163
  default:
29095
29164
  return;
@@ -29138,17 +29207,19 @@ function rasterizeMono(o, dropout, fixedBox, stubOvershoot = true) {
29138
29207
  pxl = e2;
29139
29208
  break;
29140
29209
  case 4:
29141
- pxl = h.floorPub(Math.floor((x1 + x2 + Math.floor(P * 63 / 64)) / 2));
29210
+ pxl = smartPixel(h, x1, x2);
29142
29211
  break;
29143
29212
  case 1:
29144
29213
  case 5:
29145
- if (left.next === right && left.height <= 0 && !(stubOvershoot && left.flags & OVERSHOOT_TOP && x2 - x1 >= h.half)) {
29214
+ const stub = monoContour?.isStub(true, col, x1 / P, e2 / P, e1 / P, P);
29215
+ if (stub === true) return;
29216
+ if (stub == null && left.next === right && left.height <= 0 && !(stubOvershoot && left.flags & OVERSHOOT_TOP && x2 - x1 >= h.half)) {
29146
29217
  return;
29147
29218
  }
29148
- if (right.next === left && left.start === col && !(stubOvershoot && left.flags & OVERSHOOT_BOTTOM && x2 - x1 >= h.half)) {
29219
+ if (stub == null && right.next === left && left.start === col && !(stubOvershoot && left.flags & OVERSHOOT_BOTTOM && x2 - x1 >= h.half)) {
29149
29220
  return;
29150
29221
  }
29151
- pxl = mode === 1 ? e2 : h.floorPub(Math.floor((x1 + x2 + Math.floor(P * 63 / 64)) / 2));
29222
+ pxl = mode === 1 ? e2 : smartPixel(h, x1, x2);
29152
29223
  break;
29153
29224
  default:
29154
29225
  return;
@@ -29741,12 +29812,14 @@ var RealizedFont = class {
29741
29812
  }
29742
29813
  let bitmap;
29743
29814
  if (this.mode === "mono") {
29744
- bitmap = rasterizeMono(o, dropoutMode(src.scanSet ? src.scanControl : this.ppem <= DEFAULT_DROPOUT_PPEM, src.scanType));
29815
+ const smartGrid = this.ppemX === this.ppem && !m && !this.syntheticItalic && !subX && !subY;
29816
+ bitmap = rasterizeMono(o, dropoutMode(src.scanSet ? src.scanControl : this.ppem <= DEFAULT_DROPOUT_PPEM, src.scanType), void 0, true, smartGrid);
29745
29817
  } else if (this.mode === "cleartype") {
29746
29818
  const dropout = src.scanSet ? dropoutMode(src.scanControl, src.scanType) : 2;
29747
29819
  bitmap = rasterizeClearType(o, this.gdiPlus, this.gdiPlus ? clearTypeSpread(this.ttf) : void 0, dropout);
29748
29820
  } else {
29749
- bitmap = rasterizeGray(o, src.scanSet ? dropoutMode(src.scanControl, src.scanType) : 2);
29821
+ const dropout = src.scanSet ? dropoutMode(src.scanControl, src.scanType) : 2;
29822
+ 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
29823
  }
29751
29824
  if (bitmap && this.syntheticBold) {
29752
29825
  bitmap = embolden(bitmap, this.mode === "mono" ? 1 : 16);
@@ -30058,6 +30131,23 @@ function resolvePpem(font, height) {
30058
30131
  return ppem;
30059
30132
  }
30060
30133
  var CLEARTYPE_OVERSAMPLE = 6;
30134
+ function rasterizeCourierGray(o, dropout) {
30135
+ const samples = rasterizeSamples(o, 4, 4, 1, dropout, false);
30136
+ if (!samples) return null;
30137
+ const { box, hi } = samples;
30138
+ const width = box.xMax - box.xMin;
30139
+ const height = box.yMax - box.yMin;
30140
+ const data = new Uint8Array(width * height);
30141
+ for (let y = 0; y < hi.height; y++) {
30142
+ const row2 = y * hi.width;
30143
+ for (let x = 0; x < hi.width; x++) {
30144
+ if (hi.data[row2 + x] || x > 0 && hi.data[row2 + x - 1]) {
30145
+ data[Math.floor(y / 4) * width + Math.floor(x / 4)]++;
30146
+ }
30147
+ }
30148
+ }
30149
+ return { width, height, left: box.xMin, top: box.yMax, data };
30150
+ }
30061
30151
  function clearTypeSpread(ttf) {
30062
30152
  if (ttf.family.toLowerCase() !== "courier new") {
30063
30153
  return void 0;
@@ -37313,7 +37403,7 @@ function emitSvgRun(ctx, font, run, glyphs, origins, fontFamilyMap) {
37313
37403
  for (let i = 0; i < origins.length; i++) {
37314
37404
  const code = run.glyphIndices ? font.charForGlyph(glyphs[i]) : run.codes[i];
37315
37405
  if (!run.glyphIndices && !(run.options & ETO_IGNORELANGUAGE) && isC1Control(code)) continue;
37316
- text += String.fromCharCode(code);
37406
+ text += String.fromCodePoint(code);
37317
37407
  visibleOrigins.push(origins[i]);
37318
37408
  }
37319
37409
  if (text.length === 0) return;
@@ -39329,7 +39419,11 @@ function setViewportExt(m, cx, cy) {
39329
39419
  fixIsotropic(m);
39330
39420
  }
39331
39421
  function scaled(v, num, den) {
39332
- return Math.trunc(v * num / den);
39422
+ const product = Math.imul(v, num);
39423
+ if (product === -2147483648 && den === -1) {
39424
+ return 0;
39425
+ }
39426
+ return Math.trunc(product / den);
39333
39427
  }
39334
39428
  function scaleWindowExt(m, xNum, xDen, yNum, yDen) {
39335
39429
  if (!extentsSettable(m) || !xDen || !yDen) {
@@ -39482,10 +39576,16 @@ function setExtent(r, viewport, cx, cy) {
39482
39576
  activateGdiMappingMode(r);
39483
39577
  }
39484
39578
  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);
39579
+ const m = emfMapping(r);
39580
+ if (!extentsSettable(m) || !xDenom || !yDenom) {
39581
+ return;
39582
+ }
39583
+ (viewport ? scaleViewportExt : scaleWindowExt)(m, xNum, xDenom, yNum, yDenom);
39584
+ 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) {
39585
+ return;
39586
+ }
39587
+ storeMapping(r, m);
39588
+ activateGdiMappingMode(r);
39489
39589
  }
39490
39590
  function handleCoordinateRecord(rCtx, recType, dataOff, recSize) {
39491
39591
  const { view } = rCtx;
@@ -40633,8 +40733,8 @@ function drawPlusStringWithEngine(rCtx, font, text, layoutX, layoutY, paint, ali
40633
40733
  if (!unhinted) x = Math.round(x);
40634
40734
  const y = m[3] * layoutY + m[5] + ascent;
40635
40735
  const codes = [];
40636
- for (let i = 0; i < text.length; i++) {
40637
- codes.push(text.charCodeAt(i));
40736
+ for (const character of text) {
40737
+ codes.push(character.codePointAt(0));
40638
40738
  }
40639
40739
  const tracking = format?.tracking ?? PLUS_DEFAULT_TRACKING;
40640
40740
  const dx = unhinted ? codes.map((c) => realized.advance(realized.glyphIndex(c)) * tracking) : null;
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) {
@@ -29030,7 +29088,7 @@ function monoBox(o) {
29030
29088
  }
29031
29089
  return { xMin, xMax, yMin, yMax };
29032
29090
  }
29033
- function rasterizeMono(o, dropout, fixedBox, stubOvershoot = true) {
29091
+ function rasterizeMono(o, dropout, fixedBox, stubOvershoot = true, smartCrossings26Dot6 = false) {
29034
29092
  const box = fixedBox ?? monoBox(o);
29035
29093
  if (!box) {
29036
29094
  return null;
@@ -29052,6 +29110,15 @@ function rasterizeMono(o, dropout, fixedBox, stubOvershoot = true) {
29052
29110
  const at = (col, row2) => (height - 1 - row2) * width + col;
29053
29111
  const r = new Raster(dropout);
29054
29112
  const P = r.precision;
29113
+ const monoContour = stubOvershoot && (dropout === 1 || dropout === 4 || dropout === 5) && local.endPts.length === 1 && Array.from(local.onCurve).every(Boolean) ? new MonoStubContour(local) : null;
29114
+ const smartPixel = (raster, x1, x2) => {
29115
+ if (monoContour && smartCrossings26Dot6) {
29116
+ const step = P / 64;
29117
+ x1 = Math.round(x1 / step) * step;
29118
+ x2 = Math.round(x2 / step) * step;
29119
+ }
29120
+ return raster.floorPub(Math.floor((x1 + x2 + Math.floor(P * 63 / 64)) / 2));
29121
+ };
29055
29122
  if (r.convert(local, false, 0, (height - 1) * P)) {
29056
29123
  r.sweep(
29057
29124
  (y, x1, x2) => {
@@ -29082,17 +29149,19 @@ function rasterizeMono(o, dropout, fixedBox, stubOvershoot = true) {
29082
29149
  pxl = e2;
29083
29150
  break;
29084
29151
  case 4:
29085
- pxl = r.floorPub(Math.floor((x1 + x2 + Math.floor(P * 63 / 64)) / 2));
29152
+ pxl = smartPixel(r, x1, x2);
29086
29153
  break;
29087
29154
  case 1:
29088
29155
  case 5:
29089
- if (left.next === right && left.height <= 0 && !(stubOvershoot && left.flags & OVERSHOOT_TOP && x2 - x1 >= r.half)) {
29156
+ const stub = monoContour?.isStub(false, y, x1 / P, e2 / P, e1 / P, P);
29157
+ if (stub === true) return;
29158
+ if (stub == null && left.next === right && left.height <= 0 && !(stubOvershoot && left.flags & OVERSHOOT_TOP && x2 - x1 >= r.half)) {
29090
29159
  return;
29091
29160
  }
29092
- if (right.next === left && left.start === y && !(stubOvershoot && left.flags & OVERSHOOT_BOTTOM && x2 - x1 >= r.half)) {
29161
+ if (stub == null && right.next === left && left.start === y && !(stubOvershoot && left.flags & OVERSHOOT_BOTTOM && x2 - x1 >= r.half)) {
29093
29162
  return;
29094
29163
  }
29095
- pxl = mode === 1 ? e2 : r.floorPub(Math.floor((x1 + x2 + Math.floor(P * 63 / 64)) / 2));
29164
+ pxl = mode === 1 ? e2 : smartPixel(r, x1, x2);
29096
29165
  break;
29097
29166
  default:
29098
29167
  return;
@@ -29141,17 +29210,19 @@ function rasterizeMono(o, dropout, fixedBox, stubOvershoot = true) {
29141
29210
  pxl = e2;
29142
29211
  break;
29143
29212
  case 4:
29144
- pxl = h.floorPub(Math.floor((x1 + x2 + Math.floor(P * 63 / 64)) / 2));
29213
+ pxl = smartPixel(h, x1, x2);
29145
29214
  break;
29146
29215
  case 1:
29147
29216
  case 5:
29148
- if (left.next === right && left.height <= 0 && !(stubOvershoot && left.flags & OVERSHOOT_TOP && x2 - x1 >= h.half)) {
29217
+ const stub = monoContour?.isStub(true, col, x1 / P, e2 / P, e1 / P, P);
29218
+ if (stub === true) return;
29219
+ if (stub == null && left.next === right && left.height <= 0 && !(stubOvershoot && left.flags & OVERSHOOT_TOP && x2 - x1 >= h.half)) {
29149
29220
  return;
29150
29221
  }
29151
- if (right.next === left && left.start === col && !(stubOvershoot && left.flags & OVERSHOOT_BOTTOM && x2 - x1 >= h.half)) {
29222
+ if (stub == null && right.next === left && left.start === col && !(stubOvershoot && left.flags & OVERSHOOT_BOTTOM && x2 - x1 >= h.half)) {
29152
29223
  return;
29153
29224
  }
29154
- pxl = mode === 1 ? e2 : h.floorPub(Math.floor((x1 + x2 + Math.floor(P * 63 / 64)) / 2));
29225
+ pxl = mode === 1 ? e2 : smartPixel(h, x1, x2);
29155
29226
  break;
29156
29227
  default:
29157
29228
  return;
@@ -29744,12 +29815,14 @@ var RealizedFont = class {
29744
29815
  }
29745
29816
  let bitmap;
29746
29817
  if (this.mode === "mono") {
29747
- 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);
29748
29820
  } else if (this.mode === "cleartype") {
29749
29821
  const dropout = src.scanSet ? dropoutMode(src.scanControl, src.scanType) : 2;
29750
29822
  bitmap = rasterizeClearType(o, this.gdiPlus, this.gdiPlus ? clearTypeSpread(this.ttf) : void 0, dropout);
29751
29823
  } else {
29752
- 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);
29753
29826
  }
29754
29827
  if (bitmap && this.syntheticBold) {
29755
29828
  bitmap = embolden(bitmap, this.mode === "mono" ? 1 : 16);
@@ -30061,6 +30134,23 @@ function resolvePpem(font, height) {
30061
30134
  return ppem;
30062
30135
  }
30063
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
+ }
30064
30154
  function clearTypeSpread(ttf) {
30065
30155
  if (ttf.family.toLowerCase() !== "courier new") {
30066
30156
  return void 0;
@@ -37316,7 +37406,7 @@ function emitSvgRun(ctx, font, run, glyphs, origins, fontFamilyMap) {
37316
37406
  for (let i = 0; i < origins.length; i++) {
37317
37407
  const code = run.glyphIndices ? font.charForGlyph(glyphs[i]) : run.codes[i];
37318
37408
  if (!run.glyphIndices && !(run.options & ETO_IGNORELANGUAGE) && isC1Control(code)) continue;
37319
- text += String.fromCharCode(code);
37409
+ text += String.fromCodePoint(code);
37320
37410
  visibleOrigins.push(origins[i]);
37321
37411
  }
37322
37412
  if (text.length === 0) return;
@@ -39332,7 +39422,11 @@ function setViewportExt(m, cx, cy) {
39332
39422
  fixIsotropic(m);
39333
39423
  }
39334
39424
  function scaled(v, num, den) {
39335
- 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);
39336
39430
  }
39337
39431
  function scaleWindowExt(m, xNum, xDen, yNum, yDen) {
39338
39432
  if (!extentsSettable(m) || !xDen || !yDen) {
@@ -39485,10 +39579,16 @@ function setExtent(r, viewport, cx, cy) {
39485
39579
  activateGdiMappingMode(r);
39486
39580
  }
39487
39581
  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);
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);
39492
39592
  }
39493
39593
  function handleCoordinateRecord(rCtx, recType, dataOff, recSize) {
39494
39594
  const { view } = rCtx;
@@ -40636,8 +40736,8 @@ function drawPlusStringWithEngine(rCtx, font, text, layoutX, layoutY, paint, ali
40636
40736
  if (!unhinted) x = Math.round(x);
40637
40737
  const y = m[3] * layoutY + m[5] + ascent;
40638
40738
  const codes = [];
40639
- for (let i = 0; i < text.length; i++) {
40640
- codes.push(text.charCodeAt(i));
40739
+ for (const character of text) {
40740
+ codes.push(character.codePointAt(0));
40641
40741
  }
40642
40742
  const tracking = format?.tracking ?? PLUS_DEFAULT_TRACKING;
40643
40743
  const dx = unhinted ? codes.map((c) => realized.advance(realized.glyphIndex(c)) * tracking) : null;
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) {
@@ -29028,7 +29086,7 @@ function monoBox(o) {
29028
29086
  }
29029
29087
  return { xMin, xMax, yMin, yMax };
29030
29088
  }
29031
- function rasterizeMono(o, dropout, fixedBox, stubOvershoot = true) {
29089
+ function rasterizeMono(o, dropout, fixedBox, stubOvershoot = true, smartCrossings26Dot6 = false) {
29032
29090
  const box = fixedBox ?? monoBox(o);
29033
29091
  if (!box) {
29034
29092
  return null;
@@ -29050,6 +29108,15 @@ function rasterizeMono(o, dropout, fixedBox, stubOvershoot = true) {
29050
29108
  const at = (col, row2) => (height - 1 - row2) * width + col;
29051
29109
  const r = new Raster(dropout);
29052
29110
  const P = r.precision;
29111
+ const monoContour = stubOvershoot && (dropout === 1 || dropout === 4 || dropout === 5) && local.endPts.length === 1 && Array.from(local.onCurve).every(Boolean) ? new MonoStubContour(local) : null;
29112
+ const smartPixel = (raster, x1, x2) => {
29113
+ if (monoContour && smartCrossings26Dot6) {
29114
+ const step = P / 64;
29115
+ x1 = Math.round(x1 / step) * step;
29116
+ x2 = Math.round(x2 / step) * step;
29117
+ }
29118
+ return raster.floorPub(Math.floor((x1 + x2 + Math.floor(P * 63 / 64)) / 2));
29119
+ };
29053
29120
  if (r.convert(local, false, 0, (height - 1) * P)) {
29054
29121
  r.sweep(
29055
29122
  (y, x1, x2) => {
@@ -29080,17 +29147,19 @@ function rasterizeMono(o, dropout, fixedBox, stubOvershoot = true) {
29080
29147
  pxl = e2;
29081
29148
  break;
29082
29149
  case 4:
29083
- pxl = r.floorPub(Math.floor((x1 + x2 + Math.floor(P * 63 / 64)) / 2));
29150
+ pxl = smartPixel(r, x1, x2);
29084
29151
  break;
29085
29152
  case 1:
29086
29153
  case 5:
29087
- if (left.next === right && left.height <= 0 && !(stubOvershoot && left.flags & OVERSHOOT_TOP && x2 - x1 >= r.half)) {
29154
+ const stub = monoContour?.isStub(false, y, x1 / P, e2 / P, e1 / P, P);
29155
+ if (stub === true) return;
29156
+ if (stub == null && left.next === right && left.height <= 0 && !(stubOvershoot && left.flags & OVERSHOOT_TOP && x2 - x1 >= r.half)) {
29088
29157
  return;
29089
29158
  }
29090
- if (right.next === left && left.start === y && !(stubOvershoot && left.flags & OVERSHOOT_BOTTOM && x2 - x1 >= r.half)) {
29159
+ if (stub == null && right.next === left && left.start === y && !(stubOvershoot && left.flags & OVERSHOOT_BOTTOM && x2 - x1 >= r.half)) {
29091
29160
  return;
29092
29161
  }
29093
- pxl = mode === 1 ? e2 : r.floorPub(Math.floor((x1 + x2 + Math.floor(P * 63 / 64)) / 2));
29162
+ pxl = mode === 1 ? e2 : smartPixel(r, x1, x2);
29094
29163
  break;
29095
29164
  default:
29096
29165
  return;
@@ -29139,17 +29208,19 @@ function rasterizeMono(o, dropout, fixedBox, stubOvershoot = true) {
29139
29208
  pxl = e2;
29140
29209
  break;
29141
29210
  case 4:
29142
- pxl = h.floorPub(Math.floor((x1 + x2 + Math.floor(P * 63 / 64)) / 2));
29211
+ pxl = smartPixel(h, x1, x2);
29143
29212
  break;
29144
29213
  case 1:
29145
29214
  case 5:
29146
- if (left.next === right && left.height <= 0 && !(stubOvershoot && left.flags & OVERSHOOT_TOP && x2 - x1 >= h.half)) {
29215
+ const stub = monoContour?.isStub(true, col, x1 / P, e2 / P, e1 / P, P);
29216
+ if (stub === true) return;
29217
+ if (stub == null && left.next === right && left.height <= 0 && !(stubOvershoot && left.flags & OVERSHOOT_TOP && x2 - x1 >= h.half)) {
29147
29218
  return;
29148
29219
  }
29149
- if (right.next === left && left.start === col && !(stubOvershoot && left.flags & OVERSHOOT_BOTTOM && x2 - x1 >= h.half)) {
29220
+ if (stub == null && right.next === left && left.start === col && !(stubOvershoot && left.flags & OVERSHOOT_BOTTOM && x2 - x1 >= h.half)) {
29150
29221
  return;
29151
29222
  }
29152
- pxl = mode === 1 ? e2 : h.floorPub(Math.floor((x1 + x2 + Math.floor(P * 63 / 64)) / 2));
29223
+ pxl = mode === 1 ? e2 : smartPixel(h, x1, x2);
29153
29224
  break;
29154
29225
  default:
29155
29226
  return;
@@ -29742,12 +29813,14 @@ var RealizedFont = class {
29742
29813
  }
29743
29814
  let bitmap;
29744
29815
  if (this.mode === "mono") {
29745
- 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);
29746
29818
  } else if (this.mode === "cleartype") {
29747
29819
  const dropout = src.scanSet ? dropoutMode(src.scanControl, src.scanType) : 2;
29748
29820
  bitmap = rasterizeClearType(o, this.gdiPlus, this.gdiPlus ? clearTypeSpread(this.ttf) : void 0, dropout);
29749
29821
  } else {
29750
- 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);
29751
29824
  }
29752
29825
  if (bitmap && this.syntheticBold) {
29753
29826
  bitmap = embolden(bitmap, this.mode === "mono" ? 1 : 16);
@@ -30059,6 +30132,23 @@ function resolvePpem(font, height) {
30059
30132
  return ppem;
30060
30133
  }
30061
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
+ }
30062
30152
  function clearTypeSpread(ttf) {
30063
30153
  if (ttf.family.toLowerCase() !== "courier new") {
30064
30154
  return void 0;
@@ -37314,7 +37404,7 @@ function emitSvgRun(ctx, font, run, glyphs, origins, fontFamilyMap) {
37314
37404
  for (let i = 0; i < origins.length; i++) {
37315
37405
  const code = run.glyphIndices ? font.charForGlyph(glyphs[i]) : run.codes[i];
37316
37406
  if (!run.glyphIndices && !(run.options & ETO_IGNORELANGUAGE) && isC1Control(code)) continue;
37317
- text += String.fromCharCode(code);
37407
+ text += String.fromCodePoint(code);
37318
37408
  visibleOrigins.push(origins[i]);
37319
37409
  }
37320
37410
  if (text.length === 0) return;
@@ -39330,7 +39420,11 @@ function setViewportExt(m, cx, cy) {
39330
39420
  fixIsotropic(m);
39331
39421
  }
39332
39422
  function scaled(v, num, den) {
39333
- 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);
39334
39428
  }
39335
39429
  function scaleWindowExt(m, xNum, xDen, yNum, yDen) {
39336
39430
  if (!extentsSettable(m) || !xDen || !yDen) {
@@ -39483,10 +39577,16 @@ function setExtent(r, viewport, cx, cy) {
39483
39577
  activateGdiMappingMode(r);
39484
39578
  }
39485
39579
  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);
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);
39490
39590
  }
39491
39591
  function handleCoordinateRecord(rCtx, recType, dataOff, recSize) {
39492
39592
  const { view } = rCtx;
@@ -40634,8 +40734,8 @@ function drawPlusStringWithEngine(rCtx, font, text, layoutX, layoutY, paint, ali
40634
40734
  if (!unhinted) x = Math.round(x);
40635
40735
  const y = m[3] * layoutY + m[5] + ascent;
40636
40736
  const codes = [];
40637
- for (let i = 0; i < text.length; i++) {
40638
- codes.push(text.charCodeAt(i));
40737
+ for (const character of text) {
40738
+ codes.push(character.codePointAt(0));
40639
40739
  }
40640
40740
  const tracking = format?.tracking ?? PLUS_DEFAULT_TRACKING;
40641
40741
  const dx = unhinted ? codes.map((c) => realized.advance(realized.glyphIndex(c)) * tracking) : null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "emf-converter",
3
- "version": "4.20.0",
3
+ "version": "4.20.1",
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",