emf-converter 4.13.0 → 4.14.0

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
@@ -29941,23 +29941,35 @@ function perpendicularVectors(width, dx0, dy0, height = width, matrixPen) {
29941
29941
  const vx = rounded[0], vy = rounded[1];
29942
29942
  return { v: flip ? [-vx, -vy] : [vx, vy], ray: flip ? [-x, -y] : [x, y] };
29943
29943
  }
29944
- function squareExtension(width, dx, dy, scale = 1, height = width, matrix, wholePixelNorm = false, symmetric = wholePixelNorm) {
29944
+ function norm(x, y) {
29945
+ return Math.sqrt(x * x + y * y);
29946
+ }
29947
+ function squareExtension(width, dx, dy, scale = 1, height = width, matrix, wholePixelNorm = false, symmetric = wholePixelNorm, logicalScale = 1) {
29948
+ const slack = 8;
29945
29949
  if (matrix) {
29946
29950
  const det = matrix[0] * matrix[3] - matrix[1] * matrix[2];
29947
29951
  const lx = (matrix[3] * dx - matrix[2] * dy) / det;
29948
29952
  const ly = (-matrix[1] * dx + matrix[0] * dy) / det;
29949
- const l = Math.hypot(lx, ly);
29953
+ let l = Math.hypot(lx, ly);
29954
+ if (wholePixelNorm) {
29955
+ l = norm(Math.floor((lx + slack) / 16) * 16, Math.floor((ly + slack) / 16) * 16);
29956
+ if (l === 0) return [0, 0];
29957
+ }
29950
29958
  const q = width / 2 * scale / l;
29951
29959
  return [Math.floor((matrix[0] * lx + matrix[2] * ly) * q + 0.5), Math.floor((matrix[1] * lx + matrix[3] * ly) * q + 0.5)];
29952
29960
  }
29953
- let len = width === height ? Math.hypot(dx, dy) : Math.hypot(dx, dy * width / height);
29954
- if (wholePixelNorm && width === height) {
29955
- const whole = Math.hypot(Math.floor(dx / 16), Math.floor(dy / 16)) * 16;
29956
- if (whole === 0) return [0, 0];
29957
- len = whole;
29958
- }
29961
+ const [ax, ay] = typeof logicalScale === "number" ? [logicalScale, logicalScale] : logicalScale;
29959
29962
  const r = width / 2 * scale;
29960
29963
  const round = symmetric ? (v) => Math.sign(v) * Math.floor(Math.abs(v) + 0.5) : (v) => Math.floor(v + 0.5);
29964
+ if (wholePixelNorm && (width === height || ax !== ay)) {
29965
+ const lx = dx / ax;
29966
+ const ly = dy / ay;
29967
+ const cut = ax === 1 && ay === 1 ? 0 : slack;
29968
+ const whole = norm(Math.floor((lx + cut) / 16) * 16, Math.floor((ly + cut) / 16) * 16);
29969
+ if (whole === 0) return [0, 0];
29970
+ return [round(lx / whole * r), round(ly * ay / ax / whole * r)];
29971
+ }
29972
+ const len = width === height ? norm(dx, dy) : norm(dx, dy * width / height);
29961
29973
  return [round(dx / len * r), round(dy / len * r)];
29962
29974
  }
29963
29975
  function turnSign(ax, ay, bx, by) {
@@ -29997,7 +30009,7 @@ var Outliner = class {
29997
30009
  const [L, R] = drawVertices(this.pen, d[0], d[1], this.opts.width, this.opts.height, nibMatrix);
29998
30010
  const perpendicular = curve && drawDir ? drawDir : [dx, dy];
29999
30011
  const vectors = perpendicularVectors(this.opts.width, perpendicular[0], perpendicular[1], this.opts.height, nibMatrix ? this.pen : void 0);
30000
- return { dx, dy, L, R, v: vectors.v, vRaw: vectors.ray, e: squareExtension(this.opts.width, perpendicular[0], perpendicular[1], 1, this.opts.height, nibMatrix, !!this.opts.wholePixelDashVectors), pe: [perpendicular[0], perpendicular[1]], curveEnd: !!drawDir, role: drawDir?.[2] ?? 3, tangentLength: drawDir ? drawDir[0] * drawDir[0] + drawDir[1] * drawDir[1] : 0, curve };
30012
+ return { dx, dy, L, R, v: vectors.v, vRaw: vectors.ray, e: squareExtension(this.opts.capWidth ?? this.opts.width, perpendicular[0], perpendicular[1], 1, this.opts.height ?? this.opts.capWidth, nibMatrix, !!(this.opts.wholePixelDashVectors || this.opts.cutToLogicalUnits), void 0, this.opts.logicalScale), pe: [perpendicular[0], perpendicular[1]], curveEnd: !!drawDir, role: drawDir?.[2] ?? 3, tangentLength: drawDir ? drawDir[0] * drawDir[0] + drawDir[1] * drawDir[1] : 0, curve };
30001
30013
  }
30002
30014
  push(p, v) {
30003
30015
  this.pts.push([p[0] + v[0], p[1] + v[1]]);
@@ -30315,8 +30327,8 @@ function dashPieces(P, tangents, curveFlags, pattern, shorten, wholePixelVectors
30315
30327
  const [x1, y1] = P[i + 1];
30316
30328
  const dir = [x1 - x0, y1 - y0];
30317
30329
  const det = matrix ? matrix[0] * matrix[3] - matrix[1] * matrix[2] : 1;
30318
- const real = matrix ? Math.hypot((matrix[3] * dir[0] - matrix[2] * dir[1]) / det, (-matrix[1] * dir[0] + matrix[0] * dir[1]) / det) : metric ? Math.hypot(dir[0] / metric[0], dir[1] / metric[1]) : Math.hypot(dir[0], dir[1]);
30319
- const len = metric || matrix ? real : wholePixelVectors ? Math.hypot(Math.floor(dir[0] / 16), Math.floor(dir[1] / 16)) * 16 : Math.hypot(dir[0], dir[1]);
30330
+ const real = matrix ? Math.hypot((matrix[3] * dir[0] - matrix[2] * dir[1]) / det, (-matrix[1] * dir[0] + matrix[0] * dir[1]) / det) : metric ? Math.hypot(dir[0] / metric[0], dir[1] / metric[1]) : norm(dir[0], dir[1]);
30331
+ const len = metric || matrix ? real : wholePixelVectors ? norm(Math.floor(dir[0] / 16), Math.floor(dir[1] / 16)) * 16 : norm(dir[0], dir[1]);
30320
30332
  if (len === 0) {
30321
30333
  if (on && cur) {
30322
30334
  cur.pts.push(P[i + 1]);
@@ -30424,26 +30436,16 @@ function widenPath(path, opts) {
30424
30436
  const runTangents = closed ? [...dirs, void 0] : dirs;
30425
30437
  const runCurves = closed ? [...curves, !!fig.roundWiden] : curves;
30426
30438
  for (const piece of dashPieces(run, runTangents, runCurves, opts.dashes, shorten, !!opts.wholePixelDashVectors, opts.dashMetric, opts.deviceNib ? opts.matrix : void 0)) {
30427
- const pts = [piece.pts[0]];
30428
- const pdirs = [];
30429
- const pdraws = [];
30430
- const pcurves = [];
30431
- for (let i = 1; i < piece.pts.length; i++) {
30432
- const q = piece.pts[i];
30433
- const last = pts[pts.length - 1];
30434
- if (q[0] !== last[0] || q[1] !== last[1]) {
30435
- pts.push(q);
30436
- pdirs.push(piece.dirs[i - 1]);
30437
- pdraws.push(piece.draws[i - 1]);
30438
- pcurves.push(piece.curves[i - 1]);
30439
- }
30440
- }
30439
+ const pts = piece.pts;
30440
+ const pdirs = piece.dirs;
30441
+ const pdraws = piece.draws;
30442
+ const pcurves = piece.curves;
30441
30443
  outliner.open(pts, pdirs.length > 0 ? pdirs : [piece.dirs[0]], pdirs.length > 0 ? pdraws : [piece.draws[0]], pdirs.length > 0 ? pcurves : [piece.curves[0]], piece.startScale, piece.endScale);
30442
30444
  }
30443
30445
  } else if (closed && P.length >= 3) {
30444
30446
  outliner.closed(P, dirs, curves);
30445
30447
  } else if (closed && P.length === 2) {
30446
- outliner.open([P[0], P[1], P[0]]);
30448
+ outliner.closed(P, dirs, curves);
30447
30449
  } else {
30448
30450
  outliner.open(P, void 0, dirs, curves);
30449
30451
  }
@@ -30706,11 +30708,17 @@ function penWidenOptions(rCtx, opts = {}) {
30706
30708
  join: opts.roundPen ? "round" : opts.rectangle && !state.penExtended ? "miter" : !state.penExtended || joinBits === 0 ? "round" : joinBits === 4096 ? "bevel" : "miter",
30707
30709
  miterLimit: state.miterLimit ?? 10,
30708
30710
  dashes: dashes ? dashes.map((v) => v * 16) : null,
30709
- shortenDashes: (flags & 15) !== 7
30711
+ shortenDashes: (flags & 15) !== 7,
30712
+ // Under a rotation the device vector is converted back to logical units for a square cap's extension.
30713
+ cutToLogicalUnits: !dashes
30710
30714
  };
30711
30715
  }
30716
+ const unrotated = Math.abs(matrix[1]) < 1e-6 && Math.abs(matrix[2]) < 1e-6;
30717
+ const scaled2 = !rCtx.wholeDevicePixels && unrotated && (Math.abs(Math.abs(matrix[0]) - 1) > 1e-6 || Math.abs(Math.abs(matrix[3]) - 1) > 1e-6);
30718
+ const uniform = scaled2 && !ellipse && state.penExtended && (flags & 15) !== 6;
30719
+ const nibWidth = uniform ? Math.max(1, Math.floor(widthPx + 0.5)) : widthPx;
30712
30720
  return {
30713
- width: Math.round((ellipse ? state.penWidth * Math.abs(matrix[0]) : widthPx) * 16),
30721
+ width: Math.round((ellipse ? state.penWidth * Math.abs(matrix[0]) : nibWidth) * 16),
30714
30722
  ...ellipse ? { height: Math.round(state.penWidth * Math.abs(matrix[3]) * 16) } : {},
30715
30723
  cap: opts.roundPen || !state.penExtended || capBits === 0 ? "round" : capBits === 256 ? "square" : "flat",
30716
30724
  join: opts.roundPen ? "round" : opts.rectangle && !state.penExtended ? "miter" : !state.penExtended || joinBits === 0 ? "round" : joinBits === 4096 ? "bevel" : "miter",
@@ -30720,7 +30728,13 @@ function penWidenOptions(rCtx, opts = {}) {
30720
30728
  shortenDashes: (flags & 15) !== 7,
30721
30729
  // One logical unit per device pixel, unrotated: GDI measures dash
30722
30730
  // segments in whole pixels.
30723
- wholePixelDashVectors: Math.abs(matrix[0] - 1) < 1e-6 && Math.abs(matrix[3] - 1) < 1e-6 && Math.abs(matrix[1]) < 1e-6 && Math.abs(matrix[2]) < 1e-6
30731
+ wholePixelDashVectors: Math.abs(matrix[0] - 1) < 1e-6 && Math.abs(matrix[3] - 1) < 1e-6 && Math.abs(matrix[1]) < 1e-6 && Math.abs(matrix[2]) < 1e-6,
30732
+ ...scaled2 && !dashes ? {
30733
+ cutToLogicalUnits: true,
30734
+ logicalScale: ellipse ? [Math.abs(matrix[0]), Math.abs(matrix[3])] : Math.abs(matrix[0]),
30735
+ // The pen rounds to whole pixels, but a square cap extends by half the unrounded width.
30736
+ ...uniform ? { capWidth: state.penWidth * Math.abs(matrix[0]) * 16 } : {}
30737
+ } : {}
30724
30738
  };
30725
30739
  }
30726
30740
  function hatchBackgroundMask(hatch) {
@@ -32012,8 +32026,14 @@ function handleRoundRect(rCtx, dataOff, recSize) {
32012
32026
  const cornerFixW = inset ? Math.abs(cornerW * devM[0]) * 16 : fixExtent(rCtx, cornerW, 0);
32013
32027
  const cornerFixH = inset ? Math.abs(cornerH * devM[3]) * 16 : fixExtent(rCtx, cornerH, 1);
32014
32028
  const cornerOnFrame = (fix, size, framedSize) => inset && size !== 0 ? Math.floor(Math.min(fix, size) * framedSize / size) : fix;
32015
- const cw = cornerOnFrame(cornerFixW, Math.abs((r - l) * devM[0]) * 16, Math.abs(framed.exx));
32016
- const ch = cornerOnFrame(cornerFixH, Math.abs((b - t) * devM[3]) * 16, Math.abs(framed.eyy));
32029
+ let cw = cornerOnFrame(cornerFixW, Math.abs((r - l) * devM[0]) * 16, Math.abs(framed.exx));
32030
+ let ch = cornerOnFrame(cornerFixH, Math.abs((b - t) * devM[3]) * 16, Math.abs(framed.eyy));
32031
+ if (!inset && rCtx.state.penStyle !== 6 && cornerW !== 0 && cornerH !== 0 && Math.abs(devM[1]) < 1e-9 && Math.abs(devM[2]) < 1e-9 && rCtx.state.penStyle !== 5 && !penIsCosmetic(rCtx) && rCtx.state.worldTransform.every((v, i) => v === [1, 0, 0, 1, 0, 0][i])) {
32032
+ const fullW = (Math.abs(r - l) + 1) * Math.abs(devM[0]) * 16;
32033
+ const fullH = (Math.abs(b - t) + 1) * Math.abs(devM[3]) * 16;
32034
+ cw = Math.floor(Math.min(cornerFixW, fullW) * Math.abs(framed.exx) / fullW);
32035
+ ch = Math.floor(Math.min(cornerFixH, fullH) * Math.abs(framed.eyy) / fullH);
32036
+ }
32017
32037
  const drawRoundRect = inset ? (c) => appendRoundRectPath(
32018
32038
  c,
32019
32039
  Math.min(framed.ax, framed.ax + framed.exx) / 16,
package/dist/browser.mjs CHANGED
@@ -29939,23 +29939,35 @@ function perpendicularVectors(width, dx0, dy0, height = width, matrixPen) {
29939
29939
  const vx = rounded[0], vy = rounded[1];
29940
29940
  return { v: flip ? [-vx, -vy] : [vx, vy], ray: flip ? [-x, -y] : [x, y] };
29941
29941
  }
29942
- function squareExtension(width, dx, dy, scale = 1, height = width, matrix, wholePixelNorm = false, symmetric = wholePixelNorm) {
29942
+ function norm(x, y) {
29943
+ return Math.sqrt(x * x + y * y);
29944
+ }
29945
+ function squareExtension(width, dx, dy, scale = 1, height = width, matrix, wholePixelNorm = false, symmetric = wholePixelNorm, logicalScale = 1) {
29946
+ const slack = 8;
29943
29947
  if (matrix) {
29944
29948
  const det = matrix[0] * matrix[3] - matrix[1] * matrix[2];
29945
29949
  const lx = (matrix[3] * dx - matrix[2] * dy) / det;
29946
29950
  const ly = (-matrix[1] * dx + matrix[0] * dy) / det;
29947
- const l = Math.hypot(lx, ly);
29951
+ let l = Math.hypot(lx, ly);
29952
+ if (wholePixelNorm) {
29953
+ l = norm(Math.floor((lx + slack) / 16) * 16, Math.floor((ly + slack) / 16) * 16);
29954
+ if (l === 0) return [0, 0];
29955
+ }
29948
29956
  const q = width / 2 * scale / l;
29949
29957
  return [Math.floor((matrix[0] * lx + matrix[2] * ly) * q + 0.5), Math.floor((matrix[1] * lx + matrix[3] * ly) * q + 0.5)];
29950
29958
  }
29951
- let len = width === height ? Math.hypot(dx, dy) : Math.hypot(dx, dy * width / height);
29952
- if (wholePixelNorm && width === height) {
29953
- const whole = Math.hypot(Math.floor(dx / 16), Math.floor(dy / 16)) * 16;
29954
- if (whole === 0) return [0, 0];
29955
- len = whole;
29956
- }
29959
+ const [ax, ay] = typeof logicalScale === "number" ? [logicalScale, logicalScale] : logicalScale;
29957
29960
  const r = width / 2 * scale;
29958
29961
  const round = symmetric ? (v) => Math.sign(v) * Math.floor(Math.abs(v) + 0.5) : (v) => Math.floor(v + 0.5);
29962
+ if (wholePixelNorm && (width === height || ax !== ay)) {
29963
+ const lx = dx / ax;
29964
+ const ly = dy / ay;
29965
+ const cut = ax === 1 && ay === 1 ? 0 : slack;
29966
+ const whole = norm(Math.floor((lx + cut) / 16) * 16, Math.floor((ly + cut) / 16) * 16);
29967
+ if (whole === 0) return [0, 0];
29968
+ return [round(lx / whole * r), round(ly * ay / ax / whole * r)];
29969
+ }
29970
+ const len = width === height ? norm(dx, dy) : norm(dx, dy * width / height);
29959
29971
  return [round(dx / len * r), round(dy / len * r)];
29960
29972
  }
29961
29973
  function turnSign(ax, ay, bx, by) {
@@ -29995,7 +30007,7 @@ var Outliner = class {
29995
30007
  const [L, R] = drawVertices(this.pen, d[0], d[1], this.opts.width, this.opts.height, nibMatrix);
29996
30008
  const perpendicular = curve && drawDir ? drawDir : [dx, dy];
29997
30009
  const vectors = perpendicularVectors(this.opts.width, perpendicular[0], perpendicular[1], this.opts.height, nibMatrix ? this.pen : void 0);
29998
- return { dx, dy, L, R, v: vectors.v, vRaw: vectors.ray, e: squareExtension(this.opts.width, perpendicular[0], perpendicular[1], 1, this.opts.height, nibMatrix, !!this.opts.wholePixelDashVectors), pe: [perpendicular[0], perpendicular[1]], curveEnd: !!drawDir, role: drawDir?.[2] ?? 3, tangentLength: drawDir ? drawDir[0] * drawDir[0] + drawDir[1] * drawDir[1] : 0, curve };
30010
+ return { dx, dy, L, R, v: vectors.v, vRaw: vectors.ray, e: squareExtension(this.opts.capWidth ?? this.opts.width, perpendicular[0], perpendicular[1], 1, this.opts.height ?? this.opts.capWidth, nibMatrix, !!(this.opts.wholePixelDashVectors || this.opts.cutToLogicalUnits), void 0, this.opts.logicalScale), pe: [perpendicular[0], perpendicular[1]], curveEnd: !!drawDir, role: drawDir?.[2] ?? 3, tangentLength: drawDir ? drawDir[0] * drawDir[0] + drawDir[1] * drawDir[1] : 0, curve };
29999
30011
  }
30000
30012
  push(p, v) {
30001
30013
  this.pts.push([p[0] + v[0], p[1] + v[1]]);
@@ -30313,8 +30325,8 @@ function dashPieces(P, tangents, curveFlags, pattern, shorten, wholePixelVectors
30313
30325
  const [x1, y1] = P[i + 1];
30314
30326
  const dir = [x1 - x0, y1 - y0];
30315
30327
  const det = matrix ? matrix[0] * matrix[3] - matrix[1] * matrix[2] : 1;
30316
- const real = matrix ? Math.hypot((matrix[3] * dir[0] - matrix[2] * dir[1]) / det, (-matrix[1] * dir[0] + matrix[0] * dir[1]) / det) : metric ? Math.hypot(dir[0] / metric[0], dir[1] / metric[1]) : Math.hypot(dir[0], dir[1]);
30317
- const len = metric || matrix ? real : wholePixelVectors ? Math.hypot(Math.floor(dir[0] / 16), Math.floor(dir[1] / 16)) * 16 : Math.hypot(dir[0], dir[1]);
30328
+ const real = matrix ? Math.hypot((matrix[3] * dir[0] - matrix[2] * dir[1]) / det, (-matrix[1] * dir[0] + matrix[0] * dir[1]) / det) : metric ? Math.hypot(dir[0] / metric[0], dir[1] / metric[1]) : norm(dir[0], dir[1]);
30329
+ const len = metric || matrix ? real : wholePixelVectors ? norm(Math.floor(dir[0] / 16), Math.floor(dir[1] / 16)) * 16 : norm(dir[0], dir[1]);
30318
30330
  if (len === 0) {
30319
30331
  if (on && cur) {
30320
30332
  cur.pts.push(P[i + 1]);
@@ -30422,26 +30434,16 @@ function widenPath(path, opts) {
30422
30434
  const runTangents = closed ? [...dirs, void 0] : dirs;
30423
30435
  const runCurves = closed ? [...curves, !!fig.roundWiden] : curves;
30424
30436
  for (const piece of dashPieces(run, runTangents, runCurves, opts.dashes, shorten, !!opts.wholePixelDashVectors, opts.dashMetric, opts.deviceNib ? opts.matrix : void 0)) {
30425
- const pts = [piece.pts[0]];
30426
- const pdirs = [];
30427
- const pdraws = [];
30428
- const pcurves = [];
30429
- for (let i = 1; i < piece.pts.length; i++) {
30430
- const q = piece.pts[i];
30431
- const last = pts[pts.length - 1];
30432
- if (q[0] !== last[0] || q[1] !== last[1]) {
30433
- pts.push(q);
30434
- pdirs.push(piece.dirs[i - 1]);
30435
- pdraws.push(piece.draws[i - 1]);
30436
- pcurves.push(piece.curves[i - 1]);
30437
- }
30438
- }
30437
+ const pts = piece.pts;
30438
+ const pdirs = piece.dirs;
30439
+ const pdraws = piece.draws;
30440
+ const pcurves = piece.curves;
30439
30441
  outliner.open(pts, pdirs.length > 0 ? pdirs : [piece.dirs[0]], pdirs.length > 0 ? pdraws : [piece.draws[0]], pdirs.length > 0 ? pcurves : [piece.curves[0]], piece.startScale, piece.endScale);
30440
30442
  }
30441
30443
  } else if (closed && P.length >= 3) {
30442
30444
  outliner.closed(P, dirs, curves);
30443
30445
  } else if (closed && P.length === 2) {
30444
- outliner.open([P[0], P[1], P[0]]);
30446
+ outliner.closed(P, dirs, curves);
30445
30447
  } else {
30446
30448
  outliner.open(P, void 0, dirs, curves);
30447
30449
  }
@@ -30704,11 +30706,17 @@ function penWidenOptions(rCtx, opts = {}) {
30704
30706
  join: opts.roundPen ? "round" : opts.rectangle && !state.penExtended ? "miter" : !state.penExtended || joinBits === 0 ? "round" : joinBits === 4096 ? "bevel" : "miter",
30705
30707
  miterLimit: state.miterLimit ?? 10,
30706
30708
  dashes: dashes ? dashes.map((v) => v * 16) : null,
30707
- shortenDashes: (flags & 15) !== 7
30709
+ shortenDashes: (flags & 15) !== 7,
30710
+ // Under a rotation the device vector is converted back to logical units for a square cap's extension.
30711
+ cutToLogicalUnits: !dashes
30708
30712
  };
30709
30713
  }
30714
+ const unrotated = Math.abs(matrix[1]) < 1e-6 && Math.abs(matrix[2]) < 1e-6;
30715
+ const scaled2 = !rCtx.wholeDevicePixels && unrotated && (Math.abs(Math.abs(matrix[0]) - 1) > 1e-6 || Math.abs(Math.abs(matrix[3]) - 1) > 1e-6);
30716
+ const uniform = scaled2 && !ellipse && state.penExtended && (flags & 15) !== 6;
30717
+ const nibWidth = uniform ? Math.max(1, Math.floor(widthPx + 0.5)) : widthPx;
30710
30718
  return {
30711
- width: Math.round((ellipse ? state.penWidth * Math.abs(matrix[0]) : widthPx) * 16),
30719
+ width: Math.round((ellipse ? state.penWidth * Math.abs(matrix[0]) : nibWidth) * 16),
30712
30720
  ...ellipse ? { height: Math.round(state.penWidth * Math.abs(matrix[3]) * 16) } : {},
30713
30721
  cap: opts.roundPen || !state.penExtended || capBits === 0 ? "round" : capBits === 256 ? "square" : "flat",
30714
30722
  join: opts.roundPen ? "round" : opts.rectangle && !state.penExtended ? "miter" : !state.penExtended || joinBits === 0 ? "round" : joinBits === 4096 ? "bevel" : "miter",
@@ -30718,7 +30726,13 @@ function penWidenOptions(rCtx, opts = {}) {
30718
30726
  shortenDashes: (flags & 15) !== 7,
30719
30727
  // One logical unit per device pixel, unrotated: GDI measures dash
30720
30728
  // segments in whole pixels.
30721
- wholePixelDashVectors: Math.abs(matrix[0] - 1) < 1e-6 && Math.abs(matrix[3] - 1) < 1e-6 && Math.abs(matrix[1]) < 1e-6 && Math.abs(matrix[2]) < 1e-6
30729
+ wholePixelDashVectors: Math.abs(matrix[0] - 1) < 1e-6 && Math.abs(matrix[3] - 1) < 1e-6 && Math.abs(matrix[1]) < 1e-6 && Math.abs(matrix[2]) < 1e-6,
30730
+ ...scaled2 && !dashes ? {
30731
+ cutToLogicalUnits: true,
30732
+ logicalScale: ellipse ? [Math.abs(matrix[0]), Math.abs(matrix[3])] : Math.abs(matrix[0]),
30733
+ // The pen rounds to whole pixels, but a square cap extends by half the unrounded width.
30734
+ ...uniform ? { capWidth: state.penWidth * Math.abs(matrix[0]) * 16 } : {}
30735
+ } : {}
30722
30736
  };
30723
30737
  }
30724
30738
  function hatchBackgroundMask(hatch) {
@@ -32010,8 +32024,14 @@ function handleRoundRect(rCtx, dataOff, recSize) {
32010
32024
  const cornerFixW = inset ? Math.abs(cornerW * devM[0]) * 16 : fixExtent(rCtx, cornerW, 0);
32011
32025
  const cornerFixH = inset ? Math.abs(cornerH * devM[3]) * 16 : fixExtent(rCtx, cornerH, 1);
32012
32026
  const cornerOnFrame = (fix, size, framedSize) => inset && size !== 0 ? Math.floor(Math.min(fix, size) * framedSize / size) : fix;
32013
- const cw = cornerOnFrame(cornerFixW, Math.abs((r - l) * devM[0]) * 16, Math.abs(framed.exx));
32014
- const ch = cornerOnFrame(cornerFixH, Math.abs((b - t) * devM[3]) * 16, Math.abs(framed.eyy));
32027
+ let cw = cornerOnFrame(cornerFixW, Math.abs((r - l) * devM[0]) * 16, Math.abs(framed.exx));
32028
+ let ch = cornerOnFrame(cornerFixH, Math.abs((b - t) * devM[3]) * 16, Math.abs(framed.eyy));
32029
+ if (!inset && rCtx.state.penStyle !== 6 && cornerW !== 0 && cornerH !== 0 && Math.abs(devM[1]) < 1e-9 && Math.abs(devM[2]) < 1e-9 && rCtx.state.penStyle !== 5 && !penIsCosmetic(rCtx) && rCtx.state.worldTransform.every((v, i) => v === [1, 0, 0, 1, 0, 0][i])) {
32030
+ const fullW = (Math.abs(r - l) + 1) * Math.abs(devM[0]) * 16;
32031
+ const fullH = (Math.abs(b - t) + 1) * Math.abs(devM[3]) * 16;
32032
+ cw = Math.floor(Math.min(cornerFixW, fullW) * Math.abs(framed.exx) / fullW);
32033
+ ch = Math.floor(Math.min(cornerFixH, fullH) * Math.abs(framed.eyy) / fullH);
32034
+ }
32015
32035
  const drawRoundRect = inset ? (c) => appendRoundRectPath(
32016
32036
  c,
32017
32037
  Math.min(framed.ax, framed.ax + framed.exx) / 16,
package/dist/index.js CHANGED
@@ -29942,23 +29942,35 @@ function perpendicularVectors(width, dx0, dy0, height = width, matrixPen) {
29942
29942
  const vx = rounded[0], vy = rounded[1];
29943
29943
  return { v: flip ? [-vx, -vy] : [vx, vy], ray: flip ? [-x, -y] : [x, y] };
29944
29944
  }
29945
- function squareExtension(width, dx, dy, scale = 1, height = width, matrix, wholePixelNorm = false, symmetric = wholePixelNorm) {
29945
+ function norm(x, y) {
29946
+ return Math.sqrt(x * x + y * y);
29947
+ }
29948
+ function squareExtension(width, dx, dy, scale = 1, height = width, matrix, wholePixelNorm = false, symmetric = wholePixelNorm, logicalScale = 1) {
29949
+ const slack = 8;
29946
29950
  if (matrix) {
29947
29951
  const det = matrix[0] * matrix[3] - matrix[1] * matrix[2];
29948
29952
  const lx = (matrix[3] * dx - matrix[2] * dy) / det;
29949
29953
  const ly = (-matrix[1] * dx + matrix[0] * dy) / det;
29950
- const l = Math.hypot(lx, ly);
29954
+ let l = Math.hypot(lx, ly);
29955
+ if (wholePixelNorm) {
29956
+ l = norm(Math.floor((lx + slack) / 16) * 16, Math.floor((ly + slack) / 16) * 16);
29957
+ if (l === 0) return [0, 0];
29958
+ }
29951
29959
  const q = width / 2 * scale / l;
29952
29960
  return [Math.floor((matrix[0] * lx + matrix[2] * ly) * q + 0.5), Math.floor((matrix[1] * lx + matrix[3] * ly) * q + 0.5)];
29953
29961
  }
29954
- let len = width === height ? Math.hypot(dx, dy) : Math.hypot(dx, dy * width / height);
29955
- if (wholePixelNorm && width === height) {
29956
- const whole = Math.hypot(Math.floor(dx / 16), Math.floor(dy / 16)) * 16;
29957
- if (whole === 0) return [0, 0];
29958
- len = whole;
29959
- }
29962
+ const [ax, ay] = typeof logicalScale === "number" ? [logicalScale, logicalScale] : logicalScale;
29960
29963
  const r = width / 2 * scale;
29961
29964
  const round = symmetric ? (v) => Math.sign(v) * Math.floor(Math.abs(v) + 0.5) : (v) => Math.floor(v + 0.5);
29965
+ if (wholePixelNorm && (width === height || ax !== ay)) {
29966
+ const lx = dx / ax;
29967
+ const ly = dy / ay;
29968
+ const cut = ax === 1 && ay === 1 ? 0 : slack;
29969
+ const whole = norm(Math.floor((lx + cut) / 16) * 16, Math.floor((ly + cut) / 16) * 16);
29970
+ if (whole === 0) return [0, 0];
29971
+ return [round(lx / whole * r), round(ly * ay / ax / whole * r)];
29972
+ }
29973
+ const len = width === height ? norm(dx, dy) : norm(dx, dy * width / height);
29962
29974
  return [round(dx / len * r), round(dy / len * r)];
29963
29975
  }
29964
29976
  function turnSign(ax, ay, bx, by) {
@@ -29998,7 +30010,7 @@ var Outliner = class {
29998
30010
  const [L, R] = drawVertices(this.pen, d[0], d[1], this.opts.width, this.opts.height, nibMatrix);
29999
30011
  const perpendicular = curve && drawDir ? drawDir : [dx, dy];
30000
30012
  const vectors = perpendicularVectors(this.opts.width, perpendicular[0], perpendicular[1], this.opts.height, nibMatrix ? this.pen : void 0);
30001
- return { dx, dy, L, R, v: vectors.v, vRaw: vectors.ray, e: squareExtension(this.opts.width, perpendicular[0], perpendicular[1], 1, this.opts.height, nibMatrix, !!this.opts.wholePixelDashVectors), pe: [perpendicular[0], perpendicular[1]], curveEnd: !!drawDir, role: drawDir?.[2] ?? 3, tangentLength: drawDir ? drawDir[0] * drawDir[0] + drawDir[1] * drawDir[1] : 0, curve };
30013
+ return { dx, dy, L, R, v: vectors.v, vRaw: vectors.ray, e: squareExtension(this.opts.capWidth ?? this.opts.width, perpendicular[0], perpendicular[1], 1, this.opts.height ?? this.opts.capWidth, nibMatrix, !!(this.opts.wholePixelDashVectors || this.opts.cutToLogicalUnits), void 0, this.opts.logicalScale), pe: [perpendicular[0], perpendicular[1]], curveEnd: !!drawDir, role: drawDir?.[2] ?? 3, tangentLength: drawDir ? drawDir[0] * drawDir[0] + drawDir[1] * drawDir[1] : 0, curve };
30002
30014
  }
30003
30015
  push(p, v) {
30004
30016
  this.pts.push([p[0] + v[0], p[1] + v[1]]);
@@ -30316,8 +30328,8 @@ function dashPieces(P, tangents, curveFlags, pattern, shorten, wholePixelVectors
30316
30328
  const [x1, y1] = P[i + 1];
30317
30329
  const dir = [x1 - x0, y1 - y0];
30318
30330
  const det = matrix ? matrix[0] * matrix[3] - matrix[1] * matrix[2] : 1;
30319
- const real = matrix ? Math.hypot((matrix[3] * dir[0] - matrix[2] * dir[1]) / det, (-matrix[1] * dir[0] + matrix[0] * dir[1]) / det) : metric ? Math.hypot(dir[0] / metric[0], dir[1] / metric[1]) : Math.hypot(dir[0], dir[1]);
30320
- const len = metric || matrix ? real : wholePixelVectors ? Math.hypot(Math.floor(dir[0] / 16), Math.floor(dir[1] / 16)) * 16 : Math.hypot(dir[0], dir[1]);
30331
+ const real = matrix ? Math.hypot((matrix[3] * dir[0] - matrix[2] * dir[1]) / det, (-matrix[1] * dir[0] + matrix[0] * dir[1]) / det) : metric ? Math.hypot(dir[0] / metric[0], dir[1] / metric[1]) : norm(dir[0], dir[1]);
30332
+ const len = metric || matrix ? real : wholePixelVectors ? norm(Math.floor(dir[0] / 16), Math.floor(dir[1] / 16)) * 16 : norm(dir[0], dir[1]);
30321
30333
  if (len === 0) {
30322
30334
  if (on && cur) {
30323
30335
  cur.pts.push(P[i + 1]);
@@ -30425,26 +30437,16 @@ function widenPath(path, opts) {
30425
30437
  const runTangents = closed ? [...dirs, void 0] : dirs;
30426
30438
  const runCurves = closed ? [...curves, !!fig.roundWiden] : curves;
30427
30439
  for (const piece of dashPieces(run, runTangents, runCurves, opts.dashes, shorten, !!opts.wholePixelDashVectors, opts.dashMetric, opts.deviceNib ? opts.matrix : void 0)) {
30428
- const pts = [piece.pts[0]];
30429
- const pdirs = [];
30430
- const pdraws = [];
30431
- const pcurves = [];
30432
- for (let i = 1; i < piece.pts.length; i++) {
30433
- const q = piece.pts[i];
30434
- const last = pts[pts.length - 1];
30435
- if (q[0] !== last[0] || q[1] !== last[1]) {
30436
- pts.push(q);
30437
- pdirs.push(piece.dirs[i - 1]);
30438
- pdraws.push(piece.draws[i - 1]);
30439
- pcurves.push(piece.curves[i - 1]);
30440
- }
30441
- }
30440
+ const pts = piece.pts;
30441
+ const pdirs = piece.dirs;
30442
+ const pdraws = piece.draws;
30443
+ const pcurves = piece.curves;
30442
30444
  outliner.open(pts, pdirs.length > 0 ? pdirs : [piece.dirs[0]], pdirs.length > 0 ? pdraws : [piece.draws[0]], pdirs.length > 0 ? pcurves : [piece.curves[0]], piece.startScale, piece.endScale);
30443
30445
  }
30444
30446
  } else if (closed && P.length >= 3) {
30445
30447
  outliner.closed(P, dirs, curves);
30446
30448
  } else if (closed && P.length === 2) {
30447
- outliner.open([P[0], P[1], P[0]]);
30449
+ outliner.closed(P, dirs, curves);
30448
30450
  } else {
30449
30451
  outliner.open(P, void 0, dirs, curves);
30450
30452
  }
@@ -30707,11 +30709,17 @@ function penWidenOptions(rCtx, opts = {}) {
30707
30709
  join: opts.roundPen ? "round" : opts.rectangle && !state.penExtended ? "miter" : !state.penExtended || joinBits === 0 ? "round" : joinBits === 4096 ? "bevel" : "miter",
30708
30710
  miterLimit: state.miterLimit ?? 10,
30709
30711
  dashes: dashes ? dashes.map((v) => v * 16) : null,
30710
- shortenDashes: (flags & 15) !== 7
30712
+ shortenDashes: (flags & 15) !== 7,
30713
+ // Under a rotation the device vector is converted back to logical units for a square cap's extension.
30714
+ cutToLogicalUnits: !dashes
30711
30715
  };
30712
30716
  }
30717
+ const unrotated = Math.abs(matrix[1]) < 1e-6 && Math.abs(matrix[2]) < 1e-6;
30718
+ const scaled2 = !rCtx.wholeDevicePixels && unrotated && (Math.abs(Math.abs(matrix[0]) - 1) > 1e-6 || Math.abs(Math.abs(matrix[3]) - 1) > 1e-6);
30719
+ const uniform = scaled2 && !ellipse && state.penExtended && (flags & 15) !== 6;
30720
+ const nibWidth = uniform ? Math.max(1, Math.floor(widthPx + 0.5)) : widthPx;
30713
30721
  return {
30714
- width: Math.round((ellipse ? state.penWidth * Math.abs(matrix[0]) : widthPx) * 16),
30722
+ width: Math.round((ellipse ? state.penWidth * Math.abs(matrix[0]) : nibWidth) * 16),
30715
30723
  ...ellipse ? { height: Math.round(state.penWidth * Math.abs(matrix[3]) * 16) } : {},
30716
30724
  cap: opts.roundPen || !state.penExtended || capBits === 0 ? "round" : capBits === 256 ? "square" : "flat",
30717
30725
  join: opts.roundPen ? "round" : opts.rectangle && !state.penExtended ? "miter" : !state.penExtended || joinBits === 0 ? "round" : joinBits === 4096 ? "bevel" : "miter",
@@ -30721,7 +30729,13 @@ function penWidenOptions(rCtx, opts = {}) {
30721
30729
  shortenDashes: (flags & 15) !== 7,
30722
30730
  // One logical unit per device pixel, unrotated: GDI measures dash
30723
30731
  // segments in whole pixels.
30724
- wholePixelDashVectors: Math.abs(matrix[0] - 1) < 1e-6 && Math.abs(matrix[3] - 1) < 1e-6 && Math.abs(matrix[1]) < 1e-6 && Math.abs(matrix[2]) < 1e-6
30732
+ wholePixelDashVectors: Math.abs(matrix[0] - 1) < 1e-6 && Math.abs(matrix[3] - 1) < 1e-6 && Math.abs(matrix[1]) < 1e-6 && Math.abs(matrix[2]) < 1e-6,
30733
+ ...scaled2 && !dashes ? {
30734
+ cutToLogicalUnits: true,
30735
+ logicalScale: ellipse ? [Math.abs(matrix[0]), Math.abs(matrix[3])] : Math.abs(matrix[0]),
30736
+ // The pen rounds to whole pixels, but a square cap extends by half the unrounded width.
30737
+ ...uniform ? { capWidth: state.penWidth * Math.abs(matrix[0]) * 16 } : {}
30738
+ } : {}
30725
30739
  };
30726
30740
  }
30727
30741
  function hatchBackgroundMask(hatch) {
@@ -32013,8 +32027,14 @@ function handleRoundRect(rCtx, dataOff, recSize) {
32013
32027
  const cornerFixW = inset ? Math.abs(cornerW * devM[0]) * 16 : fixExtent(rCtx, cornerW, 0);
32014
32028
  const cornerFixH = inset ? Math.abs(cornerH * devM[3]) * 16 : fixExtent(rCtx, cornerH, 1);
32015
32029
  const cornerOnFrame = (fix, size, framedSize) => inset && size !== 0 ? Math.floor(Math.min(fix, size) * framedSize / size) : fix;
32016
- const cw = cornerOnFrame(cornerFixW, Math.abs((r - l) * devM[0]) * 16, Math.abs(framed.exx));
32017
- const ch = cornerOnFrame(cornerFixH, Math.abs((b - t) * devM[3]) * 16, Math.abs(framed.eyy));
32030
+ let cw = cornerOnFrame(cornerFixW, Math.abs((r - l) * devM[0]) * 16, Math.abs(framed.exx));
32031
+ let ch = cornerOnFrame(cornerFixH, Math.abs((b - t) * devM[3]) * 16, Math.abs(framed.eyy));
32032
+ if (!inset && rCtx.state.penStyle !== 6 && cornerW !== 0 && cornerH !== 0 && Math.abs(devM[1]) < 1e-9 && Math.abs(devM[2]) < 1e-9 && rCtx.state.penStyle !== 5 && !penIsCosmetic(rCtx) && rCtx.state.worldTransform.every((v, i) => v === [1, 0, 0, 1, 0, 0][i])) {
32033
+ const fullW = (Math.abs(r - l) + 1) * Math.abs(devM[0]) * 16;
32034
+ const fullH = (Math.abs(b - t) + 1) * Math.abs(devM[3]) * 16;
32035
+ cw = Math.floor(Math.min(cornerFixW, fullW) * Math.abs(framed.exx) / fullW);
32036
+ ch = Math.floor(Math.min(cornerFixH, fullH) * Math.abs(framed.eyy) / fullH);
32037
+ }
32018
32038
  const drawRoundRect = inset ? (c) => appendRoundRectPath(
32019
32039
  c,
32020
32040
  Math.min(framed.ax, framed.ax + framed.exx) / 16,
package/dist/index.mjs CHANGED
@@ -29940,23 +29940,35 @@ function perpendicularVectors(width, dx0, dy0, height = width, matrixPen) {
29940
29940
  const vx = rounded[0], vy = rounded[1];
29941
29941
  return { v: flip ? [-vx, -vy] : [vx, vy], ray: flip ? [-x, -y] : [x, y] };
29942
29942
  }
29943
- function squareExtension(width, dx, dy, scale = 1, height = width, matrix, wholePixelNorm = false, symmetric = wholePixelNorm) {
29943
+ function norm(x, y) {
29944
+ return Math.sqrt(x * x + y * y);
29945
+ }
29946
+ function squareExtension(width, dx, dy, scale = 1, height = width, matrix, wholePixelNorm = false, symmetric = wholePixelNorm, logicalScale = 1) {
29947
+ const slack = 8;
29944
29948
  if (matrix) {
29945
29949
  const det = matrix[0] * matrix[3] - matrix[1] * matrix[2];
29946
29950
  const lx = (matrix[3] * dx - matrix[2] * dy) / det;
29947
29951
  const ly = (-matrix[1] * dx + matrix[0] * dy) / det;
29948
- const l = Math.hypot(lx, ly);
29952
+ let l = Math.hypot(lx, ly);
29953
+ if (wholePixelNorm) {
29954
+ l = norm(Math.floor((lx + slack) / 16) * 16, Math.floor((ly + slack) / 16) * 16);
29955
+ if (l === 0) return [0, 0];
29956
+ }
29949
29957
  const q = width / 2 * scale / l;
29950
29958
  return [Math.floor((matrix[0] * lx + matrix[2] * ly) * q + 0.5), Math.floor((matrix[1] * lx + matrix[3] * ly) * q + 0.5)];
29951
29959
  }
29952
- let len = width === height ? Math.hypot(dx, dy) : Math.hypot(dx, dy * width / height);
29953
- if (wholePixelNorm && width === height) {
29954
- const whole = Math.hypot(Math.floor(dx / 16), Math.floor(dy / 16)) * 16;
29955
- if (whole === 0) return [0, 0];
29956
- len = whole;
29957
- }
29960
+ const [ax, ay] = typeof logicalScale === "number" ? [logicalScale, logicalScale] : logicalScale;
29958
29961
  const r = width / 2 * scale;
29959
29962
  const round = symmetric ? (v) => Math.sign(v) * Math.floor(Math.abs(v) + 0.5) : (v) => Math.floor(v + 0.5);
29963
+ if (wholePixelNorm && (width === height || ax !== ay)) {
29964
+ const lx = dx / ax;
29965
+ const ly = dy / ay;
29966
+ const cut = ax === 1 && ay === 1 ? 0 : slack;
29967
+ const whole = norm(Math.floor((lx + cut) / 16) * 16, Math.floor((ly + cut) / 16) * 16);
29968
+ if (whole === 0) return [0, 0];
29969
+ return [round(lx / whole * r), round(ly * ay / ax / whole * r)];
29970
+ }
29971
+ const len = width === height ? norm(dx, dy) : norm(dx, dy * width / height);
29960
29972
  return [round(dx / len * r), round(dy / len * r)];
29961
29973
  }
29962
29974
  function turnSign(ax, ay, bx, by) {
@@ -29996,7 +30008,7 @@ var Outliner = class {
29996
30008
  const [L, R] = drawVertices(this.pen, d[0], d[1], this.opts.width, this.opts.height, nibMatrix);
29997
30009
  const perpendicular = curve && drawDir ? drawDir : [dx, dy];
29998
30010
  const vectors = perpendicularVectors(this.opts.width, perpendicular[0], perpendicular[1], this.opts.height, nibMatrix ? this.pen : void 0);
29999
- return { dx, dy, L, R, v: vectors.v, vRaw: vectors.ray, e: squareExtension(this.opts.width, perpendicular[0], perpendicular[1], 1, this.opts.height, nibMatrix, !!this.opts.wholePixelDashVectors), pe: [perpendicular[0], perpendicular[1]], curveEnd: !!drawDir, role: drawDir?.[2] ?? 3, tangentLength: drawDir ? drawDir[0] * drawDir[0] + drawDir[1] * drawDir[1] : 0, curve };
30011
+ return { dx, dy, L, R, v: vectors.v, vRaw: vectors.ray, e: squareExtension(this.opts.capWidth ?? this.opts.width, perpendicular[0], perpendicular[1], 1, this.opts.height ?? this.opts.capWidth, nibMatrix, !!(this.opts.wholePixelDashVectors || this.opts.cutToLogicalUnits), void 0, this.opts.logicalScale), pe: [perpendicular[0], perpendicular[1]], curveEnd: !!drawDir, role: drawDir?.[2] ?? 3, tangentLength: drawDir ? drawDir[0] * drawDir[0] + drawDir[1] * drawDir[1] : 0, curve };
30000
30012
  }
30001
30013
  push(p, v) {
30002
30014
  this.pts.push([p[0] + v[0], p[1] + v[1]]);
@@ -30314,8 +30326,8 @@ function dashPieces(P, tangents, curveFlags, pattern, shorten, wholePixelVectors
30314
30326
  const [x1, y1] = P[i + 1];
30315
30327
  const dir = [x1 - x0, y1 - y0];
30316
30328
  const det = matrix ? matrix[0] * matrix[3] - matrix[1] * matrix[2] : 1;
30317
- const real = matrix ? Math.hypot((matrix[3] * dir[0] - matrix[2] * dir[1]) / det, (-matrix[1] * dir[0] + matrix[0] * dir[1]) / det) : metric ? Math.hypot(dir[0] / metric[0], dir[1] / metric[1]) : Math.hypot(dir[0], dir[1]);
30318
- const len = metric || matrix ? real : wholePixelVectors ? Math.hypot(Math.floor(dir[0] / 16), Math.floor(dir[1] / 16)) * 16 : Math.hypot(dir[0], dir[1]);
30329
+ const real = matrix ? Math.hypot((matrix[3] * dir[0] - matrix[2] * dir[1]) / det, (-matrix[1] * dir[0] + matrix[0] * dir[1]) / det) : metric ? Math.hypot(dir[0] / metric[0], dir[1] / metric[1]) : norm(dir[0], dir[1]);
30330
+ const len = metric || matrix ? real : wholePixelVectors ? norm(Math.floor(dir[0] / 16), Math.floor(dir[1] / 16)) * 16 : norm(dir[0], dir[1]);
30319
30331
  if (len === 0) {
30320
30332
  if (on && cur) {
30321
30333
  cur.pts.push(P[i + 1]);
@@ -30423,26 +30435,16 @@ function widenPath(path, opts) {
30423
30435
  const runTangents = closed ? [...dirs, void 0] : dirs;
30424
30436
  const runCurves = closed ? [...curves, !!fig.roundWiden] : curves;
30425
30437
  for (const piece of dashPieces(run, runTangents, runCurves, opts.dashes, shorten, !!opts.wholePixelDashVectors, opts.dashMetric, opts.deviceNib ? opts.matrix : void 0)) {
30426
- const pts = [piece.pts[0]];
30427
- const pdirs = [];
30428
- const pdraws = [];
30429
- const pcurves = [];
30430
- for (let i = 1; i < piece.pts.length; i++) {
30431
- const q = piece.pts[i];
30432
- const last = pts[pts.length - 1];
30433
- if (q[0] !== last[0] || q[1] !== last[1]) {
30434
- pts.push(q);
30435
- pdirs.push(piece.dirs[i - 1]);
30436
- pdraws.push(piece.draws[i - 1]);
30437
- pcurves.push(piece.curves[i - 1]);
30438
- }
30439
- }
30438
+ const pts = piece.pts;
30439
+ const pdirs = piece.dirs;
30440
+ const pdraws = piece.draws;
30441
+ const pcurves = piece.curves;
30440
30442
  outliner.open(pts, pdirs.length > 0 ? pdirs : [piece.dirs[0]], pdirs.length > 0 ? pdraws : [piece.draws[0]], pdirs.length > 0 ? pcurves : [piece.curves[0]], piece.startScale, piece.endScale);
30441
30443
  }
30442
30444
  } else if (closed && P.length >= 3) {
30443
30445
  outliner.closed(P, dirs, curves);
30444
30446
  } else if (closed && P.length === 2) {
30445
- outliner.open([P[0], P[1], P[0]]);
30447
+ outliner.closed(P, dirs, curves);
30446
30448
  } else {
30447
30449
  outliner.open(P, void 0, dirs, curves);
30448
30450
  }
@@ -30705,11 +30707,17 @@ function penWidenOptions(rCtx, opts = {}) {
30705
30707
  join: opts.roundPen ? "round" : opts.rectangle && !state.penExtended ? "miter" : !state.penExtended || joinBits === 0 ? "round" : joinBits === 4096 ? "bevel" : "miter",
30706
30708
  miterLimit: state.miterLimit ?? 10,
30707
30709
  dashes: dashes ? dashes.map((v) => v * 16) : null,
30708
- shortenDashes: (flags & 15) !== 7
30710
+ shortenDashes: (flags & 15) !== 7,
30711
+ // Under a rotation the device vector is converted back to logical units for a square cap's extension.
30712
+ cutToLogicalUnits: !dashes
30709
30713
  };
30710
30714
  }
30715
+ const unrotated = Math.abs(matrix[1]) < 1e-6 && Math.abs(matrix[2]) < 1e-6;
30716
+ const scaled2 = !rCtx.wholeDevicePixels && unrotated && (Math.abs(Math.abs(matrix[0]) - 1) > 1e-6 || Math.abs(Math.abs(matrix[3]) - 1) > 1e-6);
30717
+ const uniform = scaled2 && !ellipse && state.penExtended && (flags & 15) !== 6;
30718
+ const nibWidth = uniform ? Math.max(1, Math.floor(widthPx + 0.5)) : widthPx;
30711
30719
  return {
30712
- width: Math.round((ellipse ? state.penWidth * Math.abs(matrix[0]) : widthPx) * 16),
30720
+ width: Math.round((ellipse ? state.penWidth * Math.abs(matrix[0]) : nibWidth) * 16),
30713
30721
  ...ellipse ? { height: Math.round(state.penWidth * Math.abs(matrix[3]) * 16) } : {},
30714
30722
  cap: opts.roundPen || !state.penExtended || capBits === 0 ? "round" : capBits === 256 ? "square" : "flat",
30715
30723
  join: opts.roundPen ? "round" : opts.rectangle && !state.penExtended ? "miter" : !state.penExtended || joinBits === 0 ? "round" : joinBits === 4096 ? "bevel" : "miter",
@@ -30719,7 +30727,13 @@ function penWidenOptions(rCtx, opts = {}) {
30719
30727
  shortenDashes: (flags & 15) !== 7,
30720
30728
  // One logical unit per device pixel, unrotated: GDI measures dash
30721
30729
  // segments in whole pixels.
30722
- wholePixelDashVectors: Math.abs(matrix[0] - 1) < 1e-6 && Math.abs(matrix[3] - 1) < 1e-6 && Math.abs(matrix[1]) < 1e-6 && Math.abs(matrix[2]) < 1e-6
30730
+ wholePixelDashVectors: Math.abs(matrix[0] - 1) < 1e-6 && Math.abs(matrix[3] - 1) < 1e-6 && Math.abs(matrix[1]) < 1e-6 && Math.abs(matrix[2]) < 1e-6,
30731
+ ...scaled2 && !dashes ? {
30732
+ cutToLogicalUnits: true,
30733
+ logicalScale: ellipse ? [Math.abs(matrix[0]), Math.abs(matrix[3])] : Math.abs(matrix[0]),
30734
+ // The pen rounds to whole pixels, but a square cap extends by half the unrounded width.
30735
+ ...uniform ? { capWidth: state.penWidth * Math.abs(matrix[0]) * 16 } : {}
30736
+ } : {}
30723
30737
  };
30724
30738
  }
30725
30739
  function hatchBackgroundMask(hatch) {
@@ -32011,8 +32025,14 @@ function handleRoundRect(rCtx, dataOff, recSize) {
32011
32025
  const cornerFixW = inset ? Math.abs(cornerW * devM[0]) * 16 : fixExtent(rCtx, cornerW, 0);
32012
32026
  const cornerFixH = inset ? Math.abs(cornerH * devM[3]) * 16 : fixExtent(rCtx, cornerH, 1);
32013
32027
  const cornerOnFrame = (fix, size, framedSize) => inset && size !== 0 ? Math.floor(Math.min(fix, size) * framedSize / size) : fix;
32014
- const cw = cornerOnFrame(cornerFixW, Math.abs((r - l) * devM[0]) * 16, Math.abs(framed.exx));
32015
- const ch = cornerOnFrame(cornerFixH, Math.abs((b - t) * devM[3]) * 16, Math.abs(framed.eyy));
32028
+ let cw = cornerOnFrame(cornerFixW, Math.abs((r - l) * devM[0]) * 16, Math.abs(framed.exx));
32029
+ let ch = cornerOnFrame(cornerFixH, Math.abs((b - t) * devM[3]) * 16, Math.abs(framed.eyy));
32030
+ if (!inset && rCtx.state.penStyle !== 6 && cornerW !== 0 && cornerH !== 0 && Math.abs(devM[1]) < 1e-9 && Math.abs(devM[2]) < 1e-9 && rCtx.state.penStyle !== 5 && !penIsCosmetic(rCtx) && rCtx.state.worldTransform.every((v, i) => v === [1, 0, 0, 1, 0, 0][i])) {
32031
+ const fullW = (Math.abs(r - l) + 1) * Math.abs(devM[0]) * 16;
32032
+ const fullH = (Math.abs(b - t) + 1) * Math.abs(devM[3]) * 16;
32033
+ cw = Math.floor(Math.min(cornerFixW, fullW) * Math.abs(framed.exx) / fullW);
32034
+ ch = Math.floor(Math.min(cornerFixH, fullH) * Math.abs(framed.eyy) / fullH);
32035
+ }
32016
32036
  const drawRoundRect = inset ? (c) => appendRoundRectPath(
32017
32037
  c,
32018
32038
  Math.min(framed.ax, framed.ax + framed.exx) / 16,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "emf-converter",
3
- "version": "4.13.0",
3
+ "version": "4.14.0",
4
4
  "description": "Convert EMF/WMF metafile binaries to PNG data URLs or SVG (markup, data URL, React/JSX)",
5
5
  "keywords": [
6
6
  "canvas",