html2canvas-pro 2.3.1 → 2.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * html2canvas-pro 2.3.1 <https://yorickshan.github.io/html2canvas-pro/>
2
+ * html2canvas-pro 2.3.2 <https://yorickshan.github.io/html2canvas-pro/>
3
3
  * Copyright (c) 2024-present yorickshan and html2canvas-pro contributors
4
4
  * Released under MIT License
5
5
  */
@@ -8950,60 +8950,6 @@
8950
8950
  }
8951
8951
  };
8952
8952
  //#endregion
8953
- //#region src/core/util.ts
8954
- const SMALL_IMAGE = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
8955
- //#endregion
8956
- //#region src/render/font-metrics.ts
8957
- const SAMPLE_TEXT = "Hidden Text";
8958
- var FontMetrics = class {
8959
- constructor(document) {
8960
- this._data = {};
8961
- this._document = document;
8962
- }
8963
- parseMetrics(fontFamily, fontSize) {
8964
- const container = this._document.createElement("div");
8965
- const img = this._document.createElement("img");
8966
- const span = this._document.createElement("span");
8967
- const body = this._document.body;
8968
- container.style.visibility = "hidden";
8969
- container.style.fontFamily = fontFamily;
8970
- container.style.fontSize = fontSize;
8971
- container.style.margin = "0";
8972
- container.style.padding = "0";
8973
- container.style.whiteSpace = "nowrap";
8974
- body.appendChild(container);
8975
- img.src = SMALL_IMAGE;
8976
- img.width = 1;
8977
- img.height = 1;
8978
- img.style.margin = "0";
8979
- img.style.padding = "0";
8980
- img.style.verticalAlign = "baseline";
8981
- span.style.fontFamily = fontFamily;
8982
- span.style.fontSize = fontSize;
8983
- span.style.margin = "0";
8984
- span.style.padding = "0";
8985
- span.appendChild(this._document.createTextNode(SAMPLE_TEXT));
8986
- container.appendChild(span);
8987
- container.appendChild(img);
8988
- const baseline = img.offsetTop - span.offsetTop + 2;
8989
- container.removeChild(span);
8990
- container.appendChild(this._document.createTextNode(SAMPLE_TEXT));
8991
- container.style.lineHeight = "normal";
8992
- img.style.verticalAlign = "super";
8993
- const middle = img.offsetTop - container.offsetTop + 2;
8994
- body.removeChild(container);
8995
- return {
8996
- baseline,
8997
- middle
8998
- };
8999
- }
9000
- getMetrics(fontFamily, fontSize) {
9001
- const key = `${fontFamily} ${fontSize}`;
9002
- if (typeof this._data[key] === "undefined") this._data[key] = this.parseMetrics(fontFamily, fontSize);
9003
- return this._data[key];
9004
- }
9005
- };
9006
- //#endregion
9007
8953
  //#region src/render/canvas/text/text-decoration-renderer.ts
9008
8954
  var TextDecorationRenderer = class {
9009
8955
  constructor(ctx) {
@@ -9092,6 +9038,33 @@
9092
9038
  }
9093
9039
  };
9094
9040
  //#endregion
9041
+ //#region src/render/canvas/font-utils.ts
9042
+ /**
9043
+ * Font measurement utility
9044
+ *
9045
+ * Provides Canvas API-based baseline measurement that works correctly with
9046
+ * webfonts (which may not be loaded in the original document) and system fonts.
9047
+ *
9048
+ * Fallback chain:
9049
+ * 1. fontBoundingBoxAscent — font-level metric (Chrome 99+, FF 116+, Safari 17.4+)
9050
+ * 2. actualBoundingBoxAscent — glyph-level metric (widely supported)
9051
+ * 3. fallback value — coarse CSS fallback
9052
+ */
9053
+ const SAMPLE_TEXT = "Mg";
9054
+ /**
9055
+ * Measure the baseline ascent for the currently set font.
9056
+ *
9057
+ * @param ctx - Canvas 2D rendering context with ctx.font already set
9058
+ * @param fallback - Fallback value when no metrics are available (e.g. fontSize.number)
9059
+ * @returns The distance from the text baseline to the top of the bounding box
9060
+ */
9061
+ const measureBaseline = (ctx, fallback) => {
9062
+ const tm = ctx.measureText(SAMPLE_TEXT);
9063
+ const fmAscent = tm.fontBoundingBoxAscent;
9064
+ const ascent = fmAscent !== void 0 && !Number.isNaN(fmAscent) ? fmAscent : tm.actualBoundingBoxAscent;
9065
+ return ascent !== void 0 && !Number.isNaN(ascent) ? ascent : fallback;
9066
+ };
9067
+ //#endregion
9095
9068
  //#region src/render/canvas/text-renderer.ts
9096
9069
  const iOSBrokenFonts = ["-apple-system", "system-ui"];
9097
9070
  /**
@@ -9144,7 +9117,6 @@
9144
9117
  constructor(deps) {
9145
9118
  this.glyphWidthCache = null;
9146
9119
  this.ctx = deps.ctx;
9147
- this.fontMetrics = deps.fontMetrics;
9148
9120
  this.options = deps.options;
9149
9121
  this.decorationRenderer = new TextDecorationRenderer(deps.ctx);
9150
9122
  }
@@ -9405,13 +9377,13 @@
9405
9377
  }
9406
9378
  async renderTextNode(text, styles, containerBounds) {
9407
9379
  this.glyphWidthCache = null;
9408
- const [fontString, fontFamily, fontSize] = this.createFontStyle(styles);
9380
+ const [fontString] = this.createFontStyle(styles);
9409
9381
  this.ctx.font = fontString;
9410
9382
  this.ctx.direction = styles.direction === 1 ? "rtl" : "ltr";
9411
9383
  this.ctx.textAlign = "left";
9412
9384
  this.ctx.textBaseline = "alphabetic";
9413
9385
  const paintOrder = styles.paintOrder;
9414
- const { baseline } = this.fontMetrics.getMetrics(fontFamily, fontSize);
9386
+ const baseline = measureBaseline(this.ctx, styles.fontSize.number);
9415
9387
  if (styles.webkitLineClamp > 0 && (styles.display & 2) !== 0 && styles.overflowY === 1 && text.textBounds.length > 0) {
9416
9388
  if (this.renderLineClampedText(text, styles, paintOrder, baseline, containerBounds)) return;
9417
9389
  }
@@ -10307,7 +10279,7 @@
10307
10279
  /**
10308
10280
  * Render form element content: checkbox, radio, text input.
10309
10281
  */
10310
- function renderFormElements(ctx, fontMetrics, textRenderer, pathFn, container, styles) {
10282
+ function renderFormElements(ctx, textRenderer, pathFn, container, styles) {
10311
10283
  if (container instanceof InputElementContainer) {
10312
10284
  const size = Math.min(container.bounds.width, container.bounds.height);
10313
10285
  if (container.type === "checkbox" && container.checked) {
@@ -10334,9 +10306,9 @@
10334
10306
  }
10335
10307
  }
10336
10308
  if (isTextInputElement(container) && container.value.length) {
10337
- const [font, fontFamily, fontSize] = textRenderer.createFontStyle(styles);
10338
- const { baseline } = fontMetrics.getMetrics(fontFamily, fontSize);
10309
+ const [font] = textRenderer.createFontStyle(styles);
10339
10310
  ctx.font = font;
10311
+ const baseline = measureBaseline(ctx, getAbsoluteValue(styles.fontSize, 0));
10340
10312
  ctx.fillStyle = container instanceof InputElementContainer && container.isPlaceholder ? asString(PLACEHOLDER_COLOR) : asString(styles.color);
10341
10313
  ctx.textBaseline = "alphabetic";
10342
10314
  ctx.textAlign = canvasTextAlign(container.styles.textAlign);
@@ -10434,7 +10406,6 @@
10434
10406
  this.canvas.style.width = `${options.width}px`;
10435
10407
  this.canvas.style.height = `${options.height}px`;
10436
10408
  }
10437
- this.fontMetrics = new FontMetrics(document);
10438
10409
  this.ctx.scale(this.options.scale, this.options.scale);
10439
10410
  this.ctx.translate(-options.x, -options.y);
10440
10411
  this.ctx.textBaseline = "bottom";
@@ -10458,7 +10429,6 @@
10458
10429
  this.effectsRenderer = new EffectsRenderer({ ctx: this.ctx }, { path: (paths) => this.path(paths) });
10459
10430
  this.textRenderer = new TextRenderer({
10460
10431
  ctx: this.ctx,
10461
- fontMetrics: this.fontMetrics,
10462
10432
  options: { scale: options.scale }
10463
10433
  });
10464
10434
  this.context.logger.debug(`Canvas renderer initialized (${options.width}x${options.height}) with scale ${options.scale}`);
@@ -10506,7 +10476,7 @@
10506
10476
  width: this.options.width,
10507
10477
  height: this.options.height
10508
10478
  }, (ctx, opts) => new CanvasRenderer(ctx, opts), container, curves, styles, (c, cv, img) => this.renderReplacedElement(c, cv, img));
10509
- renderFormElements(this.ctx, this.fontMetrics, this.textRenderer, this.path.bind(this), container, styles);
10479
+ renderFormElements(this.ctx, this.textRenderer, this.path.bind(this), container, styles);
10510
10480
  await renderListMarker(this.ctx, this.context, this.textRenderer, paint, container, styles);
10511
10481
  }
10512
10482
  async renderStackContent(stack) {