html2canvas-pro 2.3.0 → 2.3.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.
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * html2canvas-pro 2.3.0 <https://yorickshan.github.io/html2canvas-pro/>
2
+ * html2canvas-pro 2.3.1 <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
  */
@@ -9144,6 +9144,7 @@
9144
9144
  constructor(deps) {
9145
9145
  this.glyphWidthCache = null;
9146
9146
  this.ctx = deps.ctx;
9147
+ this.fontMetrics = deps.fontMetrics;
9147
9148
  this.options = deps.options;
9148
9149
  this.decorationRenderer = new TextDecorationRenderer(deps.ctx);
9149
9150
  }
@@ -9258,19 +9259,19 @@
9258
9259
  this.ctx.strokeText(letter, x, y);
9259
9260
  });
9260
9261
  }
9261
- renderTextStrokeWithStyle(text, styles) {
9262
+ renderTextStrokeWithStyle(text, styles, baseline) {
9262
9263
  if (!styles.webkitTextStrokeWidth || !text.text.trim().length) return;
9263
9264
  this.ctx.strokeStyle = asString(styles.webkitTextStrokeColor);
9264
9265
  this.ctx.lineWidth = styles.webkitTextStrokeWidth;
9265
9266
  this.ctx.lineJoin = getTextStrokeLineJoin();
9266
- this.renderStrokeText(text, styles.letterSpacing, styles.fontSize.number, styles.writingMode);
9267
+ this.renderStrokeText(text, styles.letterSpacing, baseline, styles.writingMode);
9267
9268
  this.ctx.strokeStyle = "";
9268
9269
  this.ctx.lineWidth = 0;
9269
9270
  this.ctx.lineJoin = "miter";
9270
9271
  }
9271
- renderTextFillWithShadows(text, styles) {
9272
+ renderTextFillWithShadows(text, styles, baseline) {
9272
9273
  this.ctx.fillStyle = asString(styles.color);
9273
- this.renderTextWithLetterSpacing(text, styles.letterSpacing, styles.fontSize.number, styles.writingMode);
9274
+ this.renderTextWithLetterSpacing(text, styles.letterSpacing, baseline, styles.writingMode);
9274
9275
  const textShadows = styles.textShadow;
9275
9276
  if (textShadows.length && text.text.trim().length) {
9276
9277
  textShadows.slice(0).reverse().forEach((textShadow) => {
@@ -9278,7 +9279,7 @@
9278
9279
  this.ctx.shadowOffsetX = textShadow.offsetX.number * this.options.scale;
9279
9280
  this.ctx.shadowOffsetY = textShadow.offsetY.number * this.options.scale;
9280
9281
  this.ctx.shadowBlur = textShadow.blur.number;
9281
- this.renderTextWithLetterSpacing(text, styles.letterSpacing, styles.fontSize.number, styles.writingMode);
9282
+ this.renderTextWithLetterSpacing(text, styles.letterSpacing, baseline, styles.writingMode);
9282
9283
  });
9283
9284
  this.ctx.shadowColor = "";
9284
9285
  this.ctx.shadowOffsetX = 0;
@@ -9290,15 +9291,15 @@
9290
9291
  * Helper method to render text with paint order support
9291
9292
  * Reduces code duplication in line-clamp and normal rendering
9292
9293
  */
9293
- renderTextBoundWithPaintOrder(textBound, styles, paintOrderLayers) {
9294
+ renderTextBoundWithPaintOrder(textBound, styles, paintOrderLayers, baseline) {
9294
9295
  paintOrderLayers.forEach((paintOrderLayer) => {
9295
9296
  switch (paintOrderLayer) {
9296
9297
  case 0:
9297
9298
  this.ctx.fillStyle = asString(styles.color);
9298
- this.renderTextWithLetterSpacing(textBound, styles.letterSpacing, styles.fontSize.number, styles.writingMode);
9299
+ this.renderTextWithLetterSpacing(textBound, styles.letterSpacing, baseline, styles.writingMode);
9299
9300
  break;
9300
9301
  case 1:
9301
- this.renderTextStrokeWithStyle(textBound, styles);
9302
+ this.renderTextStrokeWithStyle(textBound, styles, baseline);
9302
9303
  break;
9303
9304
  }
9304
9305
  });
@@ -9358,7 +9359,7 @@
9358
9359
  * Groups text bounds by their Y position into visual lines, then renders
9359
9360
  * only the first N-1 complete lines followed by an ellipsis on the Nth line.
9360
9361
  */
9361
- renderLineClampedText(text, styles, paintOrder, containerBounds) {
9362
+ renderLineClampedText(text, styles, paintOrder, baseline, containerBounds) {
9362
9363
  const lineHeight = styles.fontSize.number * 1.5;
9363
9364
  const lines = [];
9364
9365
  let currentLine = [];
@@ -9373,7 +9374,7 @@
9373
9374
  if (currentLine.length > 0) lines.push(currentLine);
9374
9375
  const maxLines = styles.webkitLineClamp;
9375
9376
  if (lines.length <= maxLines) return false;
9376
- for (let i = 0; i < maxLines - 1; i++) lines[i].forEach((tb) => this.renderTextBoundWithPaintOrder(tb, styles, paintOrder));
9377
+ for (let i = 0; i < maxLines - 1; i++) lines[i].forEach((tb) => this.renderTextBoundWithPaintOrder(tb, styles, paintOrder, baseline));
9377
9378
  const lastLine = lines[maxLines - 1];
9378
9379
  if (lastLine?.length && containerBounds) {
9379
9380
  const textStr = lastLine.map((tb) => tb.text).join("");
@@ -9382,8 +9383,8 @@
9382
9383
  const bounds = new TextBounds(this.truncateTextWithEllipsis(textStr, avail, styles.letterSpacing), first.bounds);
9383
9384
  for (const layer of paintOrder) if (layer === 0) {
9384
9385
  this.ctx.fillStyle = asString(styles.color);
9385
- this.renderTextWithLetterSpacing(bounds, styles.letterSpacing, styles.fontSize.number, styles.writingMode);
9386
- } else if (layer === 1) this.renderTextStrokeWithStyle(bounds, styles);
9386
+ this.renderTextWithLetterSpacing(bounds, styles.letterSpacing, baseline, styles.writingMode);
9387
+ } else if (layer === 1) this.renderTextStrokeWithStyle(bounds, styles, baseline);
9387
9388
  }
9388
9389
  return true;
9389
9390
  }
@@ -9391,34 +9392,36 @@
9391
9392
  * Render single-line text with text-overflow: ellipsis.
9392
9393
  * Returns true if ellipsis was applied (caller should skip normal rendering).
9393
9394
  */
9394
- renderEllipsisText(text, styles, paintOrder, containerBounds) {
9395
+ renderEllipsisText(text, styles, paintOrder, baseline, containerBounds) {
9395
9396
  const lineHeight = styles.fontSize.number * 1.5;
9396
9397
  const firstTop = text.textBounds[0].bounds.top;
9397
9398
  if (!text.textBounds.every((tb) => Math.abs(tb.bounds.top - firstTop) < lineHeight * .5)) return false;
9398
9399
  let fullText = text.textBounds.map((tb) => tb.text).join("").replace(/\s+/g, " ").trim();
9399
9400
  if (this.ctx.measureText(fullText).width <= containerBounds.width) return false;
9400
9401
  const bounds = new TextBounds(this.truncateTextWithEllipsis(fullText, containerBounds.width, styles.letterSpacing), text.textBounds[0].bounds);
9401
- for (const layer of paintOrder) if (layer === 0) this.renderTextFillWithShadows(bounds, styles);
9402
- else if (layer === 1) this.renderTextStrokeWithStyle(bounds, styles);
9402
+ for (const layer of paintOrder) if (layer === 0) this.renderTextFillWithShadows(bounds, styles, baseline);
9403
+ else if (layer === 1) this.renderTextStrokeWithStyle(bounds, styles, baseline);
9403
9404
  return true;
9404
9405
  }
9405
9406
  async renderTextNode(text, styles, containerBounds) {
9406
9407
  this.glyphWidthCache = null;
9407
- this.ctx.font = this.createFontStyle(styles)[0];
9408
+ const [fontString, fontFamily, fontSize] = this.createFontStyle(styles);
9409
+ this.ctx.font = fontString;
9408
9410
  this.ctx.direction = styles.direction === 1 ? "rtl" : "ltr";
9409
9411
  this.ctx.textAlign = "left";
9410
9412
  this.ctx.textBaseline = "alphabetic";
9411
9413
  const paintOrder = styles.paintOrder;
9414
+ const { baseline } = this.fontMetrics.getMetrics(fontFamily, fontSize);
9412
9415
  if (styles.webkitLineClamp > 0 && (styles.display & 2) !== 0 && styles.overflowY === 1 && text.textBounds.length > 0) {
9413
- if (this.renderLineClampedText(text, styles, paintOrder, containerBounds)) return;
9416
+ if (this.renderLineClampedText(text, styles, paintOrder, baseline, containerBounds)) return;
9414
9417
  }
9415
- if (styles.textOverflow === 1 && containerBounds && styles.overflowX === 1 && text.textBounds.length > 0 && this.renderEllipsisText(text, styles, paintOrder, containerBounds)) return;
9418
+ if (styles.textOverflow === 1 && containerBounds && styles.overflowX === 1 && text.textBounds.length > 0 && this.renderEllipsisText(text, styles, paintOrder, baseline, containerBounds)) return;
9416
9419
  text.textBounds.forEach((tb) => {
9417
9420
  paintOrder.forEach((layer) => {
9418
9421
  if (layer === 0) {
9419
- this.renderTextFillWithShadows(tb, styles);
9422
+ this.renderTextFillWithShadows(tb, styles, baseline);
9420
9423
  if (styles.textDecorationLine.length) this.renderTextDecoration(tb.bounds, styles);
9421
- } else if (layer === 1) this.renderTextStrokeWithStyle(tb, styles);
9424
+ } else if (layer === 1) this.renderTextStrokeWithStyle(tb, styles, baseline);
9422
9425
  });
9423
9426
  });
9424
9427
  }
@@ -10455,7 +10458,7 @@
10455
10458
  this.effectsRenderer = new EffectsRenderer({ ctx: this.ctx }, { path: (paths) => this.path(paths) });
10456
10459
  this.textRenderer = new TextRenderer({
10457
10460
  ctx: this.ctx,
10458
- context: this.context,
10461
+ fontMetrics: this.fontMetrics,
10459
10462
  options: { scale: options.scale }
10460
10463
  });
10461
10464
  this.context.logger.debug(`Canvas renderer initialized (${options.width}x${options.height}) with scale ${options.scale}`);