goatdee-canvas 0.0.13 → 0.0.15

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/index.cjs CHANGED
@@ -3178,6 +3178,8 @@ async function waitForAllImages(container, timeout = 10000) {
3178
3178
  });
3179
3179
  await Promise.all(promises);
3180
3180
  }
3181
+ /** 渲染图片的最大边长(物理像素)。超过此值会导致 GPU 纹理上传失败或内存溢出。 */
3182
+ const MAX_IMAGE_DIMENSION = 4096;
3181
3183
 
3182
3184
  /**
3183
3185
  * HTML to EditorJSON Converter - Parsing Functions
@@ -11514,6 +11516,9 @@ async function renderBackgroundToBase64(element, width, height) {
11514
11516
  }
11515
11517
  try {
11516
11518
  const dpr = win.devicePixelRatio || 1;
11519
+ // 限制输出物理像素长边不超过 MAX_IMAGE_DIMENSION
11520
+ const maxDim = Math.max(width, height);
11521
+ const clampedDpr = maxDim > 0 ? Math.min(dpr, MAX_IMAGE_DIMENSION / maxDim) : dpr;
11517
11522
  const tempContainer = doc.createElement("div");
11518
11523
  tempContainer.style.position = "fixed";
11519
11524
  tempContainer.style.left = "-9999px";
@@ -11532,7 +11537,7 @@ async function renderBackgroundToBase64(element, width, height) {
11532
11537
  doc.body.appendChild(tempContainer);
11533
11538
  const canvas = await html2canvasExports(tempContainer, {
11534
11539
  backgroundColor: null,
11535
- scale: dpr,
11540
+ scale: clampedDpr,
11536
11541
  logging: false,
11537
11542
  useCORS: true,
11538
11543
  allowTaint: true,
@@ -11542,8 +11547,8 @@ async function renderBackgroundToBase64(element, width, height) {
11542
11547
  doc.body.removeChild(tempContainer);
11543
11548
  return {
11544
11549
  base64: canvas.toDataURL("image/png"),
11545
- actualWidth: width * dpr,
11546
- actualHeight: height * dpr,
11550
+ actualWidth: width * clampedDpr,
11551
+ actualHeight: height * clampedDpr,
11547
11552
  logicalWidth: width,
11548
11553
  logicalHeight: height,
11549
11554
  };
@@ -11584,6 +11589,9 @@ async function renderElementWithHtml2Canvas(element, styles, rect) {
11584
11589
  tempContainer.style.zIndex = "-9999";
11585
11590
  const totalWidth = width + padding * 2;
11586
11591
  const totalHeight = height + padding * 2;
11592
+ // 限制输出物理像素长边不超过 MAX_IMAGE_DIMENSION
11593
+ const maxDim = Math.max(totalWidth, totalHeight);
11594
+ const clampedDpr = maxDim > 0 ? Math.min(dpr, MAX_IMAGE_DIMENSION / maxDim) : dpr;
11587
11595
  tempContainer.style.width = `${totalWidth}px`;
11588
11596
  tempContainer.style.height = `${totalHeight}px`;
11589
11597
  tempContainer.style.overflow = "visible";
@@ -11597,7 +11605,7 @@ async function renderElementWithHtml2Canvas(element, styles, rect) {
11597
11605
  doc.body.appendChild(tempContainer);
11598
11606
  const canvas = await html2canvasExports(tempContainer, {
11599
11607
  backgroundColor: null,
11600
- scale: dpr,
11608
+ scale: clampedDpr,
11601
11609
  logging: false,
11602
11610
  useCORS: true,
11603
11611
  allowTaint: true,
@@ -11608,8 +11616,8 @@ async function renderElementWithHtml2Canvas(element, styles, rect) {
11608
11616
  return {
11609
11617
  base64: canvas.toDataURL("image/png"),
11610
11618
  padding: padding,
11611
- actualWidth: totalWidth * dpr,
11612
- actualHeight: totalHeight * dpr,
11619
+ actualWidth: totalWidth * clampedDpr,
11620
+ actualHeight: totalHeight * clampedDpr,
11613
11621
  logicalWidth: totalWidth,
11614
11622
  logicalHeight: totalHeight,
11615
11623
  };
@@ -11697,6 +11705,11 @@ async function renderElementWithFilterToBase64(element, width, height, styles) {
11697
11705
  const w = Math.ceil(width);
11698
11706
  const h = Math.ceil(height);
11699
11707
  const dpr = win.devicePixelRatio || 1;
11708
+ // 限制输出物理像素长边不超过 MAX_IMAGE_DIMENSION(含 padding 后的总尺寸)
11709
+ const totalW = w + padding * 2;
11710
+ const totalH = h + padding * 2;
11711
+ const maxDim = Math.max(totalW, totalH);
11712
+ const clampedDpr = maxDim > 0 ? Math.min(dpr, MAX_IMAGE_DIMENSION / maxDim) : dpr;
11700
11713
  try {
11701
11714
  const tempContainer = doc.createElement('div');
11702
11715
  tempContainer.style.cssText =
@@ -11710,7 +11723,7 @@ async function renderElementWithFilterToBase64(element, width, height, styles) {
11710
11723
  doc.body.appendChild(tempContainer);
11711
11724
  const noFilterCanvas = await html2canvasExports(tempContainer, {
11712
11725
  backgroundColor: null,
11713
- scale: dpr,
11726
+ scale: clampedDpr,
11714
11727
  logging: false,
11715
11728
  useCORS: true,
11716
11729
  allowTaint: true,
@@ -11718,22 +11731,20 @@ async function renderElementWithFilterToBase64(element, width, height, styles) {
11718
11731
  height: h,
11719
11732
  });
11720
11733
  doc.body.removeChild(tempContainer);
11721
- const totalW = w + padding * 2;
11722
- const totalH = h + padding * 2;
11723
11734
  const finalCanvas = doc.createElement('canvas');
11724
- finalCanvas.width = totalW * dpr;
11725
- finalCanvas.height = totalH * dpr;
11735
+ finalCanvas.width = totalW * clampedDpr;
11736
+ finalCanvas.height = totalH * clampedDpr;
11726
11737
  const ctx = finalCanvas.getContext('2d');
11727
11738
  if (!ctx)
11728
11739
  return null;
11729
- ctx.scale(dpr, dpr);
11730
- ctx.filter = scaleFilterPxByDpr(filterStr, dpr);
11740
+ ctx.scale(clampedDpr, clampedDpr);
11741
+ ctx.filter = scaleFilterPxByDpr(filterStr, clampedDpr);
11731
11742
  ctx.drawImage(noFilterCanvas, padding, padding, w, h);
11732
11743
  return {
11733
11744
  base64: finalCanvas.toDataURL('image/png'),
11734
11745
  padding,
11735
- actualWidth: totalW * dpr,
11736
- actualHeight: totalH * dpr,
11746
+ actualWidth: totalW * clampedDpr,
11747
+ actualHeight: totalH * clampedDpr,
11737
11748
  logicalWidth: totalW,
11738
11749
  logicalHeight: totalH,
11739
11750
  };
@@ -12626,9 +12637,12 @@ async function renderPseudoElementWithHtml2Canvas(element, pseudoElement, rect,
12626
12637
  }
12627
12638
  }
12628
12639
  const dpr = win.devicePixelRatio || 1;
12640
+ // 限制输出物理像素长边不超过 MAX_IMAGE_DIMENSION
12641
+ const maxDim = Math.max(totalWidth, totalHeight);
12642
+ const clampedDpr = maxDim > 0 ? Math.min(dpr, MAX_IMAGE_DIMENSION / maxDim) : dpr;
12629
12643
  const canvas = await html2canvasExports(tempContainer, {
12630
12644
  backgroundColor: null,
12631
- scale: dpr,
12645
+ scale: clampedDpr,
12632
12646
  logging: false,
12633
12647
  useCORS: true,
12634
12648
  allowTaint: true,
@@ -12640,8 +12654,8 @@ async function renderPseudoElementWithHtml2Canvas(element, pseudoElement, rect,
12640
12654
  return {
12641
12655
  base64: canvas.toDataURL("image/png"),
12642
12656
  padding: padding,
12643
- actualWidth: totalWidth * dpr,
12644
- actualHeight: totalHeight * dpr,
12657
+ actualWidth: totalWidth * clampedDpr,
12658
+ actualHeight: totalHeight * clampedDpr,
12645
12659
  logicalWidth: totalWidth,
12646
12660
  logicalHeight: totalHeight,
12647
12661
  };
@@ -12699,6 +12713,9 @@ async function renderMarkerWithHtml2Canvas(element, rect, markerStyles, elementS
12699
12713
  const totalWidth = Math.ceil(rect.width);
12700
12714
  const totalHeight = Math.ceil(rect.height);
12701
12715
  const dpr = win.devicePixelRatio || 1;
12716
+ // 限制输出物理像素长边不超过 MAX_IMAGE_DIMENSION
12717
+ const maxDim = Math.max(totalWidth, totalHeight);
12718
+ const clampedDpr = maxDim > 0 ? Math.min(dpr, MAX_IMAGE_DIMENSION / maxDim) : dpr;
12702
12719
  const tempContainer = doc.createElement('div');
12703
12720
  tempContainer.style.position = 'fixed';
12704
12721
  tempContainer.style.left = '-9999px';
@@ -12730,7 +12747,7 @@ async function renderMarkerWithHtml2Canvas(element, rect, markerStyles, elementS
12730
12747
  }
12731
12748
  const canvas = await html2canvasExports(tempContainer, {
12732
12749
  backgroundColor: null,
12733
- scale: dpr,
12750
+ scale: clampedDpr,
12734
12751
  logging: false,
12735
12752
  useCORS: true,
12736
12753
  allowTaint: true,
@@ -12741,8 +12758,8 @@ async function renderMarkerWithHtml2Canvas(element, rect, markerStyles, elementS
12741
12758
  return {
12742
12759
  base64: canvas.toDataURL('image/png'),
12743
12760
  padding: 0,
12744
- actualWidth: totalWidth * dpr,
12745
- actualHeight: totalHeight * dpr,
12761
+ actualWidth: totalWidth * clampedDpr,
12762
+ actualHeight: totalHeight * clampedDpr,
12746
12763
  logicalWidth: totalWidth,
12747
12764
  logicalHeight: totalHeight,
12748
12765
  };
@@ -12854,8 +12871,13 @@ function convertCanvasElement(element, context) {
12854
12871
  const displayWidth = rect.width;
12855
12872
  const displayHeight = rect.height;
12856
12873
  // 导出 2 倍图:绘制到离屏 canvas (2*displaySize),再 toDataURL
12857
- const exportWidth = Math.round(displayWidth * CANVAS_EXPORT_PIXEL_RATIO);
12858
- const exportHeight = Math.round(displayHeight * CANVAS_EXPORT_PIXEL_RATIO);
12874
+ // 同时确保物理像素长边不超过 MAX_IMAGE_DIMENSION
12875
+ const rawExportWidth = Math.round(displayWidth * CANVAS_EXPORT_PIXEL_RATIO);
12876
+ const rawExportHeight = Math.round(displayHeight * CANVAS_EXPORT_PIXEL_RATIO);
12877
+ const maxDim = Math.max(rawExportWidth, rawExportHeight);
12878
+ const clampRatio = maxDim > MAX_IMAGE_DIMENSION ? MAX_IMAGE_DIMENSION / maxDim : 1;
12879
+ const exportWidth = Math.round(rawExportWidth * clampRatio);
12880
+ const exportHeight = Math.round(rawExportHeight * clampRatio);
12859
12881
  let src;
12860
12882
  try {
12861
12883
  const doc = element.ownerDocument;
@@ -12878,8 +12900,9 @@ function convertCanvasElement(element, context) {
12878
12900
  ? resolveVariable(styles.boxShadow, cssVariables)
12879
12901
  : null;
12880
12902
  const shadow = parseShadow(shadowStr);
12881
- // 图层尺寸用 2 倍宽高,scale 0.5,显示时等于 displayWidth x displayHeight,实现高清
12882
- const scale = 1 / CANVAS_EXPORT_PIXEL_RATIO;
12903
+ // 图层尺寸用导出宽高,scaleX/scaleY 保证显示尺寸等于 displayWidth x displayHeight,实现高清
12904
+ const scaleX = displayWidth / exportWidth;
12905
+ const scaleY = displayHeight / exportHeight;
12883
12906
  const layer = {
12884
12907
  type: "image",
12885
12908
  id: uuid(),
@@ -12887,8 +12910,8 @@ function convertCanvasElement(element, context) {
12887
12910
  top,
12888
12911
  width: exportWidth,
12889
12912
  height: exportHeight,
12890
- scaleX: scale,
12891
- scaleY: scale,
12913
+ scaleX,
12914
+ scaleY,
12892
12915
  src,
12893
12916
  };
12894
12917
  if (borderRadius > 0) {
@@ -13018,8 +13041,12 @@ async function convertSVGToPNG(svgDataUrl, width, height, win, scale = 5) {
13018
13041
  img.onload = () => {
13019
13042
  try {
13020
13043
  const canvas = document.createElement("canvas");
13021
- const actualWidth = width * scale * dpr;
13022
- const actualHeight = height * scale * dpr;
13044
+ // 动态限制 scale:确保物理像素长边不超过 MAX_IMAGE_DIMENSION
13045
+ const maxDim = Math.max(width, height);
13046
+ const maxScale = maxDim > 0 ? MAX_IMAGE_DIMENSION / (maxDim * dpr) : scale;
13047
+ const clampedScale = Math.min(scale, maxScale);
13048
+ const actualWidth = Math.round(width * clampedScale * dpr);
13049
+ const actualHeight = Math.round(height * clampedScale * dpr);
13023
13050
  canvas.width = actualWidth;
13024
13051
  canvas.height = actualHeight;
13025
13052
  const ctx = canvas.getContext("2d");
@@ -13027,7 +13054,7 @@ async function convertSVGToPNG(svgDataUrl, width, height, win, scale = 5) {
13027
13054
  resolve(null);
13028
13055
  return;
13029
13056
  }
13030
- ctx.scale(scale * dpr, scale * dpr);
13057
+ ctx.scale(clampedScale * dpr, clampedScale * dpr);
13031
13058
  ctx.drawImage(img, 0, 0, width, height);
13032
13059
  resolve({
13033
13060
  base64: canvas.toDataURL("image/png"),