@pixldocs/canvas-renderer 0.5.99 → 0.5.102

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.js CHANGED
@@ -3663,28 +3663,33 @@ function updateCoverLayout(g) {
3663
3663
  const hasCustomSvgMask = Boolean(
3664
3664
  currentClipPath && (isSvgMaskClipPath(currentClipPath) || isLuminanceMaskClipPath(currentClipPath) || currentClipPath.__svgMask)
3665
3665
  );
3666
- if (hasCustomSvgMask) {
3666
+ const hasEdgeFadeMask = Boolean(currentClipPath && currentClipPath.__edgeFadeMask);
3667
+ if (hasCustomSvgMask || hasEdgeFadeMask) {
3667
3668
  if (isSvgMaskClipPath(currentClipPath)) {
3668
3669
  syncSvgMaskClipPath(g);
3669
3670
  } else if (currentClipPath && typeof currentClipPath.set === "function") {
3670
- const baseW = Math.max(1, Number(currentClipPath.width) || frameW);
3671
- const baseH = Math.max(1, Number(currentClipPath.height) || frameH);
3672
- currentClipPath.set({
3673
- left: 0,
3674
- top: 0,
3675
- originX: "center",
3676
- originY: "center",
3677
- scaleX: frameW / baseW,
3678
- scaleY: frameH / baseH,
3679
- selectable: false,
3680
- evented: false,
3681
- hasControls: false,
3682
- hasBorders: false
3683
- });
3684
- currentClipPath.absolutePositioned = false;
3685
- currentClipPath.excludeFromExport = true;
3686
- currentClipPath.dirty = true;
3687
- currentClipPath.setCoords();
3671
+ if (hasEdgeFadeMask) {
3672
+ currentClipPath.dirty = true;
3673
+ } else {
3674
+ const baseW = Math.max(1, Number(currentClipPath.width) || frameW);
3675
+ const baseH = Math.max(1, Number(currentClipPath.height) || frameH);
3676
+ currentClipPath.set({
3677
+ left: 0,
3678
+ top: 0,
3679
+ originX: "center",
3680
+ originY: "center",
3681
+ scaleX: frameW / baseW,
3682
+ scaleY: frameH / baseH,
3683
+ selectable: false,
3684
+ evented: false,
3685
+ hasControls: false,
3686
+ hasBorders: false
3687
+ });
3688
+ currentClipPath.absolutePositioned = false;
3689
+ currentClipPath.excludeFromExport = true;
3690
+ currentClipPath.dirty = true;
3691
+ currentClipPath.setCoords();
3692
+ }
3688
3693
  }
3689
3694
  } else {
3690
3695
  const needsNewClipPath = !g.clipPath || shape === "circle" && !(g.clipPath instanceof fabric.Ellipse) || shape !== "circle" && !(g.clipPath instanceof fabric.Rect);
@@ -4641,10 +4646,12 @@ function exitCropMode(g, commit = true) {
4641
4646
  g.hasControls = true;
4642
4647
  g.borderDashArray = void 0;
4643
4648
  installCanvaMaskControls(g);
4644
- g.borderColor = void 0;
4645
- g.cornerColor = void 0;
4646
- g.cornerStrokeColor = void 0;
4649
+ g.borderColor = "#4f46e5";
4650
+ g.borderDashArray = void 0;
4651
+ g.cornerColor = "#ffffff";
4652
+ g.cornerStrokeColor = "#4f46e5";
4647
4653
  g.cornerStyle = "rect";
4654
+ g.transparentCorners = false;
4648
4655
  g[CROP_MODE_FLAG] = false;
4649
4656
  g.__cropZoomLastPointer = void 0;
4650
4657
  g.__lastPointerForCrop = void 0;
@@ -5066,10 +5073,14 @@ function applyTextBackground(obj, cfg) {
5066
5073
  const blur = Math.max(0, Number(shadow.blur ?? 0));
5067
5074
  const shadowColor = String(shadow.color);
5068
5075
  const pad = Math.max(16, Math.ceil(blur * 4) + Math.ceil(Math.max(Math.abs(ox), Math.abs(oy))) + 8);
5069
- const bx = -w / 2 - pL - pad;
5070
- const by = -h / 2 - pT - pad;
5071
- const bw = w + pL + pR + pad * 2;
5072
- const bh = h + pT + pB + pad * 2;
5076
+ const shadowBounds = unionBounds([
5077
+ ...rects,
5078
+ computeTextVisualBounds(this, w, h)
5079
+ ]);
5080
+ const bx = shadowBounds.x - pad;
5081
+ const by = shadowBounds.y - pad;
5082
+ const bw = shadowBounds.w + pad * 2;
5083
+ const bh = shadowBounds.h + pad * 2;
5073
5084
  const dataAttrs = `data-blur="${blur.toFixed(3)}" data-ox="${ox.toFixed(3)}" data-oy="${oy.toFixed(3)}" data-bx="${bx.toFixed(3)}" data-by="${by.toFixed(3)}" data-bw="${bw.toFixed(3)}" data-bh="${bh.toFixed(3)}" data-color="${escapeXmlAttr(shadowColor)}"`;
5074
5085
  const wrapShadow = (markup) => blur <= 0 ? `<g transform="translate(${ox.toFixed(3)} ${oy.toFixed(3)})">${markup}</g>` : `<g class="__pdShadowRaster" ${dataAttrs}>${markup}</g>`;
5075
5086
  if (hasBg && (bg == null ? void 0 : bg.shadowAffectsBg) !== false) {
@@ -5131,6 +5142,51 @@ function buildRoundedRectPathD(x, y, w, h, rTL, rTR, rBR, rBL) {
5131
5142
  parts.push("Z");
5132
5143
  return parts.join(" ");
5133
5144
  }
5145
+ function unionBounds(bounds) {
5146
+ const valid = bounds.filter((b) => Number.isFinite(b.x) && Number.isFinite(b.y) && b.w > 0 && b.h > 0);
5147
+ if (valid.length === 0) return { x: 0, y: 0, w: 1, h: 1 };
5148
+ const minX = Math.min(...valid.map((b) => b.x));
5149
+ const minY = Math.min(...valid.map((b) => b.y));
5150
+ const maxX = Math.max(...valid.map((b) => b.x + b.w));
5151
+ const maxY = Math.max(...valid.map((b) => b.y + b.h));
5152
+ return { x: minX, y: minY, w: Math.max(1, maxX - minX), h: Math.max(1, maxY - minY) };
5153
+ }
5154
+ function computeTextVisualBounds(obj, w, h) {
5155
+ var _a;
5156
+ const lines = (obj == null ? void 0 : obj._textLines) ?? [];
5157
+ if (!lines || lines.length === 0) return { x: -w / 2, y: -h / 2, w, h };
5158
+ const rects = [];
5159
+ const halfW = w / 2;
5160
+ const halfH = h / 2;
5161
+ const lineHeightRatio = Math.max(0.01, Number((obj == null ? void 0 : obj.lineHeight) ?? 1) || 1);
5162
+ let cursorY = -halfH;
5163
+ for (let i = 0; i < lines.length; i++) {
5164
+ let lineW = 0;
5165
+ let lineLeft = 0;
5166
+ let lineH = 0;
5167
+ try {
5168
+ lineW = obj.getLineWidth(i) || 0;
5169
+ } catch {
5170
+ lineW = 0;
5171
+ }
5172
+ try {
5173
+ lineLeft = ((_a = obj._getLineLeftOffset) == null ? void 0 : _a.call(obj, i)) ?? 0;
5174
+ } catch {
5175
+ lineLeft = 0;
5176
+ }
5177
+ try {
5178
+ lineH = obj.getHeightOfLine(i) || 0;
5179
+ } catch {
5180
+ lineH = 0;
5181
+ }
5182
+ const rawSlotH = i === lines.length - 1 ? lineH / lineHeightRatio : lineH;
5183
+ const usedH = cursorY + halfH;
5184
+ const slotH = Math.max(0, Math.min(rawSlotH, h - usedH));
5185
+ if (lineW > 0 && slotH > 0) rects.push({ x: -halfW + lineLeft, y: cursorY, w: lineW, h: slotH });
5186
+ cursorY += slotH;
5187
+ }
5188
+ return unionBounds(rects.length > 0 ? rects : [{ x: -w / 2, y: -h / 2, w, h }]);
5189
+ }
5134
5190
  function computeBgRects(obj, w, h, pT, pR, pB, pL, fit) {
5135
5191
  var _a;
5136
5192
  if (!fit) {
@@ -6125,6 +6181,127 @@ function renderSmartElementToDataUri(type, props, width, height) {
6125
6181
  if (!svg) return null;
6126
6182
  return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;
6127
6183
  }
6184
+ function hasEdgeFade(p) {
6185
+ if (!p) return false;
6186
+ const sides = [
6187
+ [p.fadeTopSize, p.fadeTopAmount],
6188
+ [p.fadeRightSize, p.fadeRightAmount],
6189
+ [p.fadeBottomSize, p.fadeBottomAmount],
6190
+ [p.fadeLeftSize, p.fadeLeftAmount]
6191
+ ];
6192
+ for (const [size, amount] of sides) {
6193
+ const s = Number(size) || 0;
6194
+ const a = amount == null ? 1 : Number(amount);
6195
+ if (s > 0 && a < 1) return true;
6196
+ }
6197
+ return false;
6198
+ }
6199
+ function edgeFadeKey(p) {
6200
+ if (!hasEdgeFade(p)) return "";
6201
+ return [
6202
+ (p == null ? void 0 : p.fadeTopSize) ?? 0,
6203
+ (p == null ? void 0 : p.fadeTopAmount) ?? 1,
6204
+ (p == null ? void 0 : p.fadeRightSize) ?? 0,
6205
+ (p == null ? void 0 : p.fadeRightAmount) ?? 1,
6206
+ (p == null ? void 0 : p.fadeBottomSize) ?? 0,
6207
+ (p == null ? void 0 : p.fadeBottomAmount) ?? 1,
6208
+ (p == null ? void 0 : p.fadeLeftSize) ?? 0,
6209
+ (p == null ? void 0 : p.fadeLeftAmount) ?? 1,
6210
+ (p == null ? void 0 : p.fadeTopHardness) ?? 1,
6211
+ (p == null ? void 0 : p.fadeRightHardness) ?? 1,
6212
+ (p == null ? void 0 : p.fadeBottomHardness) ?? 1,
6213
+ (p == null ? void 0 : p.fadeLeftHardness) ?? 1
6214
+ ].join("|");
6215
+ }
6216
+ function clamp01(n) {
6217
+ if (!Number.isFinite(n)) return 0;
6218
+ return Math.max(0, Math.min(1, n));
6219
+ }
6220
+ function bakeEdgeFade(source, fade) {
6221
+ const w = source.naturalWidth || source.width || 0;
6222
+ const h = source.naturalHeight || source.height || 0;
6223
+ const canvas = document.createElement("canvas");
6224
+ canvas.width = Math.max(1, w);
6225
+ canvas.height = Math.max(1, h);
6226
+ const ctx = canvas.getContext("2d");
6227
+ if (!ctx) return canvas;
6228
+ ctx.drawImage(source, 0, 0, canvas.width, canvas.height);
6229
+ const clampHardness = (n) => {
6230
+ const v = Number(n);
6231
+ if (!Number.isFinite(v) || v <= 0) return 1;
6232
+ return Math.max(0.1, Math.min(5, v));
6233
+ };
6234
+ const sides = [
6235
+ { side: "top", size: clamp01(fade.fadeTopSize ?? 0), amount: clamp01(fade.fadeTopAmount ?? 1), hardness: clampHardness(fade.fadeTopHardness) },
6236
+ { side: "right", size: clamp01(fade.fadeRightSize ?? 0), amount: clamp01(fade.fadeRightAmount ?? 1), hardness: clampHardness(fade.fadeRightHardness) },
6237
+ { side: "bottom", size: clamp01(fade.fadeBottomSize ?? 0), amount: clamp01(fade.fadeBottomAmount ?? 1), hardness: clampHardness(fade.fadeBottomHardness) },
6238
+ { side: "left", size: clamp01(fade.fadeLeftSize ?? 0), amount: clamp01(fade.fadeLeftAmount ?? 1), hardness: clampHardness(fade.fadeLeftHardness) }
6239
+ ];
6240
+ for (const { side, size, amount, hardness } of sides) {
6241
+ if (size <= 0 || amount >= 1) continue;
6242
+ const mask = document.createElement("canvas");
6243
+ mask.width = canvas.width;
6244
+ mask.height = canvas.height;
6245
+ const mctx = mask.getContext("2d");
6246
+ if (!mctx) continue;
6247
+ mctx.fillStyle = "#000";
6248
+ mctx.fillRect(0, 0, mask.width, mask.height);
6249
+ let g;
6250
+ let x = 0, y = 0, rectW = mask.width, rectH = mask.height;
6251
+ const STOPS = 24;
6252
+ const innerAlpha = (t) => {
6253
+ const keepProgress = Math.pow(t, hardness);
6254
+ const erase = (1 - amount) * (1 - keepProgress);
6255
+ return 1 - erase;
6256
+ };
6257
+ const fillStops = (grad, reverse) => {
6258
+ for (let i = 0; i <= STOPS; i++) {
6259
+ const t = i / STOPS;
6260
+ const a = innerAlpha(t);
6261
+ grad.addColorStop(t, `rgba(0,0,0,${a})`);
6262
+ }
6263
+ };
6264
+ if (side === "top") {
6265
+ const band = Math.max(1, Math.round(mask.height * size));
6266
+ g = mctx.createLinearGradient(0, 0, 0, band);
6267
+ fillStops(g);
6268
+ rectH = band;
6269
+ } else if (side === "bottom") {
6270
+ const band = Math.max(1, Math.round(mask.height * size));
6271
+ y = mask.height - band;
6272
+ g = mctx.createLinearGradient(0, mask.height, 0, y);
6273
+ fillStops(g);
6274
+ rectH = band;
6275
+ } else if (side === "left") {
6276
+ const band = Math.max(1, Math.round(mask.width * size));
6277
+ g = mctx.createLinearGradient(0, 0, band, 0);
6278
+ fillStops(g);
6279
+ rectW = band;
6280
+ } else {
6281
+ const band = Math.max(1, Math.round(mask.width * size));
6282
+ x = mask.width - band;
6283
+ g = mctx.createLinearGradient(mask.width, 0, x, 0);
6284
+ fillStops(g);
6285
+ rectW = band;
6286
+ }
6287
+ mctx.fillStyle = g;
6288
+ mctx.fillRect(x, y, rectW, rectH);
6289
+ if (amount <= 1e-3) {
6290
+ const edgePx = Math.min(2, side === "top" || side === "bottom" ? rectH : rectW);
6291
+ mctx.fillStyle = "rgba(0,0,0,0)";
6292
+ mctx.globalCompositeOperation = "copy";
6293
+ if (side === "top") mctx.fillRect(0, 0, mask.width, edgePx);
6294
+ if (side === "bottom") mctx.fillRect(0, mask.height - edgePx, mask.width, edgePx);
6295
+ if (side === "left") mctx.fillRect(0, 0, edgePx, mask.height);
6296
+ if (side === "right") mctx.fillRect(mask.width - edgePx, 0, edgePx, mask.height);
6297
+ mctx.globalCompositeOperation = "source-over";
6298
+ }
6299
+ ctx.globalCompositeOperation = "destination-in";
6300
+ ctx.drawImage(mask, 0, 0);
6301
+ ctx.globalCompositeOperation = "source-over";
6302
+ }
6303
+ return canvas;
6304
+ }
6128
6305
  function angleToCoords(angleDeg) {
6129
6306
  const rad = angleDeg * Math.PI / 180;
6130
6307
  const x1 = 0.5 - Math.sin(rad) * 0.5;
@@ -7634,6 +7811,10 @@ const PageCanvas = forwardRef(
7634
7811
  fabricCanvas.on("mouse:dblclick", (e) => {
7635
7812
  if (!isActiveRef.current || !allowEditing) return;
7636
7813
  let target = e.target;
7814
+ if (!target) {
7815
+ const active = fabricCanvas.getActiveObject();
7816
+ if (active) target = active;
7817
+ }
7637
7818
  if (target && target instanceof fabric.Group && target.__cropGroup) {
7638
7819
  const ct = target.__cropData;
7639
7820
  if ((ct == null ? void 0 : ct._img) && !isCropGroupInCropMode(target)) {
@@ -7789,7 +7970,7 @@ const PageCanvas = forwardRef(
7789
7970
  visibilityUpdateInProgressRef.current = false;
7790
7971
  }
7791
7972
  doSyncRef.current = () => {
7792
- var _a, _b, _c, _d, _e, _f, _g, _h, _i;
7973
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
7793
7974
  const shouldSkipUpdates2 = syncLockedRef.current || editLockRef.current;
7794
7975
  const state = useEditorStore.getState();
7795
7976
  const elementsToSync = elements;
@@ -7950,15 +8131,22 @@ const PageCanvas = forwardRef(
7950
8131
  const storedImageUrl = existingObj.__imageSrc;
7951
8132
  const currentUrlNormalized = currentImageUrl.trim();
7952
8133
  const storedUrlNormalized = storedImageUrl ? String(storedImageUrl).trim() : "";
7953
- const svgColorMapStr = element.svgColorMap ? JSON.stringify(element.svgColorMap) : "";
7954
- const storedColorMapStr = existingObj.__svgColorMap || "";
7955
- const colorMapChanged = svgColorMapStr !== storedColorMapStr;
7956
- const sourceUrlChanged = currentUrlNormalized !== storedUrlNormalized;
7957
- const needsReload = sourceUrlChanged || colorMapChanged;
7958
8134
  const hasUrl = currentUrlNormalized !== "";
7959
8135
  const isCropGroup2 = existingObj instanceof fabric.Group && existingObj.__cropGroup;
7960
8136
  const isPlaceholderGroup = isEmptyImagePlaceholderGroup(existingObj);
7961
8137
  const isPlaceholder = isPlaceholderGroup || !(existingObj instanceof fabric.FabricImage) || existingObj instanceof fabric.Group && !isCropGroup2;
8138
+ const svgColorMapStr = element.svgColorMap ? JSON.stringify(element.svgColorMap) : "";
8139
+ const storedColorMapStr = existingObj.__svgColorMap || "";
8140
+ const colorMapChanged = svgColorMapStr !== storedColorMapStr;
8141
+ const sourceUrlChanged = currentUrlNormalized !== storedUrlNormalized;
8142
+ const newFadeKey = edgeFadeKey(element);
8143
+ const oldFadeKey = isCropGroup2 ? existingObj.__edgeFadeInputKey || "" : existingObj.__edgeFadeKey || "";
8144
+ const innerImg = (_e = existingObj == null ? void 0 : existingObj.__cropData) == null ? void 0 : _e._img;
8145
+ const innerOldKey = isCropGroup2 ? oldFadeKey : innerImg ? innerImg.__edgeFadeKey || "" : oldFadeKey;
8146
+ const cropFadeRendererMissing = isCropGroup2 && Boolean(newFadeKey) && !existingObj.__edgeFadeRenderConfig;
8147
+ const fadeKeyChanged = newFadeKey !== oldFadeKey || newFadeKey !== innerOldKey || cropFadeRendererMissing;
8148
+ const needsReload = sourceUrlChanged || colorMapChanged || fadeKeyChanged && !isCropGroup2;
8149
+ const needsCropGroupFadeUpdate = isCropGroup2 && fadeKeyChanged;
7962
8150
  const hadUrlBefore = storedImageUrl && String(storedImageUrl).trim() !== "";
7963
8151
  if (!hasUrl && hadUrlBefore) {
7964
8152
  const placeholder = isCropGroup2 ? createImagePlaceholderForGroup(element) : createImagePlaceholder(element);
@@ -7974,16 +8162,16 @@ const PageCanvas = forwardRef(
7974
8162
  fc.requestRenderAll();
7975
8163
  continue;
7976
8164
  }
7977
- const imageFitForReplace = element.imageFit || ((_e = element.style) == null ? void 0 : _e.imageFit) || "cover";
7978
- const clipShapeForReplace = element.clipShape ?? ((_f = element.style) == null ? void 0 : _f.imageFrameShape) ?? (isPreviewMode ? "rectangle" : "none");
8165
+ const imageFitForReplace = element.imageFit || ((_f = element.style) == null ? void 0 : _f.imageFit) || "cover";
8166
+ const clipShapeForReplace = element.clipShape ?? ((_g = element.style) == null ? void 0 : _g.imageFrameShape) ?? (isPreviewMode ? "rectangle" : "none");
7979
8167
  const needCropGroupForElement = imageFitForReplace !== "fill" || clipShapeForReplace && clipShapeForReplace !== "none";
7980
8168
  const plainImageNeedsCropGroup = hasUrl && !isCropGroup2 && existingObj instanceof fabric.FabricImage && needCropGroupForElement;
7981
- if (hasUrl && (needsReload || isPlaceholder || plainImageNeedsCropGroup)) {
8169
+ if (hasUrl && (needsReload || isPlaceholder || plainImageNeedsCropGroup || needsCropGroupFadeUpdate)) {
7982
8170
  if (needsReload && !isBeingTransformed && (!wasJustModified || sourceUrlChanged)) {
7983
8171
  loadImageAsync(element, existingObj, fc);
7984
8172
  } else if (plainImageNeedsCropGroup) {
7985
8173
  loadImageAsync(element, existingObj, fc);
7986
- } else if (!needsReload && isCropGroup2) {
8174
+ } else if ((!needsReload || needsCropGroupFadeUpdate) && isCropGroup2) {
7987
8175
  const ct = existingObj.__cropData;
7988
8176
  if (ct) {
7989
8177
  const resolvedCrop = pageTree.length > 0 ? getNodeBounds(element, pageTree) : { width: typeof element.width === "number" ? element.width : 200, height: typeof element.height === "number" ? element.height : 50 };
@@ -8080,6 +8268,7 @@ const PageCanvas = forwardRef(
8080
8268
  }
8081
8269
  }
8082
8270
  updateCoverLayout(existingObj);
8271
+ applyEdgeFadeFrameClipPath(existingObj, element, ct.frameW, ct.frameH, ct.shape || "rect", ct.rx || 0);
8083
8272
  if (allowEditing) {
8084
8273
  installCanvaMaskControls(existingObj);
8085
8274
  } else {
@@ -8389,7 +8578,7 @@ const PageCanvas = forwardRef(
8389
8578
  fc.add(placeholder);
8390
8579
  fc.bringObjectToFront(placeholder);
8391
8580
  const activeObj = fc.getActiveObject();
8392
- if (activeObj && (((_g = activeObj._ct) == null ? void 0 : _g.isCropGroup) || activeObj.__cropGroup)) {
8581
+ if (activeObj && (((_h = activeObj._ct) == null ? void 0 : _h.isCropGroup) || activeObj.__cropGroup)) {
8393
8582
  fc.setActiveObject(activeObj);
8394
8583
  }
8395
8584
  placeholder.dirty = true;
@@ -8429,7 +8618,7 @@ const PageCanvas = forwardRef(
8429
8618
  fc.add(obj);
8430
8619
  fc.bringObjectToFront(obj);
8431
8620
  const activeObj = fc.getActiveObject();
8432
- if (activeObj && (((_h = activeObj._ct) == null ? void 0 : _h.isCropGroup) || activeObj.__cropGroup)) {
8621
+ if (activeObj && (((_i = activeObj._ct) == null ? void 0 : _i.isCropGroup) || activeObj.__cropGroup)) {
8433
8622
  fc.setActiveObject(activeObj);
8434
8623
  }
8435
8624
  obj.dirty = true;
@@ -8463,7 +8652,7 @@ const PageCanvas = forwardRef(
8463
8652
  isRebuildingRef.current = false;
8464
8653
  fc.requestRenderAll();
8465
8654
  if (activeBeforeSync && fc.getObjects().includes(activeBeforeSync)) {
8466
- const isCropGroup2 = ((_i = activeBeforeSync._ct) == null ? void 0 : _i.isCropGroup) || activeBeforeSync.__cropGroup;
8655
+ const isCropGroup2 = ((_j = activeBeforeSync._ct) == null ? void 0 : _j.isCropGroup) || activeBeforeSync.__cropGroup;
8467
8656
  if (isCropGroup2) {
8468
8657
  fc.setActiveObject(activeBeforeSync);
8469
8658
  fc.requestRenderAll();
@@ -8885,6 +9074,7 @@ const PageCanvas = forwardRef(
8885
9074
  }
8886
9075
  }
8887
9076
  updateCoverLayout(obj);
9077
+ applyEdgeFadeFrameClipPath(obj, element, elementWidth, elementHeight, ct.shape || "rect", ct.rx || 0);
8888
9078
  obj.setCoords();
8889
9079
  obj.dirty = true;
8890
9080
  if (obj.clipPath) {
@@ -9468,8 +9658,131 @@ const PageCanvas = forwardRef(
9468
9658
  obj.__lastStrokeGradientJson = "null";
9469
9659
  }
9470
9660
  };
9661
+ const clamp2 = (v, min, max) => {
9662
+ return Math.max(min, Math.min(max, v));
9663
+ };
9664
+ const applyEdgeFadeFrameClipPath = (group, element, frameW, frameH, shape, rxRatio) => {
9665
+ var _a, _b, _c;
9666
+ const fadeElement = element;
9667
+ const fadeGroup = group;
9668
+ const inputKey = edgeFadeKey(fadeElement);
9669
+ const hasFade = hasEdgeFade(fadeElement);
9670
+ if (!fadeGroup.__edgeFadeOriginalDrawObject) {
9671
+ fadeGroup.__edgeFadeOriginalDrawObject = group.drawObject;
9672
+ }
9673
+ if (hasFade) {
9674
+ if ((_a = group.clipPath) == null ? void 0 : _a.__edgeFadeMask) {
9675
+ group.clipPath = void 0;
9676
+ updateCoverLayout(group);
9677
+ }
9678
+ fadeGroup.__edgeFadeRenderConfig = {
9679
+ key: `${inputKey}|${Math.round(frameW)}|${Math.round(frameH)}|${shape}|${rxRatio}`,
9680
+ frameW: Math.max(1, Number(frameW) || 1),
9681
+ frameH: Math.max(1, Number(frameH) || 1),
9682
+ topSize: clamp2(Number(fadeElement.fadeTopSize) || 0, 0, 1),
9683
+ topAmount: clamp2(fadeElement.fadeTopAmount == null ? 1 : Number(fadeElement.fadeTopAmount), 0, 1),
9684
+ rightSize: clamp2(Number(fadeElement.fadeRightSize) || 0, 0, 1),
9685
+ rightAmount: clamp2(fadeElement.fadeRightAmount == null ? 1 : Number(fadeElement.fadeRightAmount), 0, 1),
9686
+ bottomSize: clamp2(Number(fadeElement.fadeBottomSize) || 0, 0, 1),
9687
+ bottomAmount: clamp2(fadeElement.fadeBottomAmount == null ? 1 : Number(fadeElement.fadeBottomAmount), 0, 1),
9688
+ leftSize: clamp2(Number(fadeElement.fadeLeftSize) || 0, 0, 1),
9689
+ leftAmount: clamp2(fadeElement.fadeLeftAmount == null ? 1 : Number(fadeElement.fadeLeftAmount), 0, 1),
9690
+ topHardness: clamp2(Number(fadeElement.fadeTopHardness) || 1, 0.1, 5),
9691
+ rightHardness: clamp2(Number(fadeElement.fadeRightHardness) || 1, 0.1, 5),
9692
+ bottomHardness: clamp2(Number(fadeElement.fadeBottomHardness) || 1, 0.1, 5),
9693
+ leftHardness: clamp2(Number(fadeElement.fadeLeftHardness) || 1, 0.1, 5)
9694
+ };
9695
+ const originalDrawObject = fadeGroup.__edgeFadeOriginalDrawObject ?? group.drawObject;
9696
+ group.drawObject = function edgeFadeDrawObject(ctx, forClipping, context) {
9697
+ originalDrawObject.call(this, ctx, forClipping, context);
9698
+ if (forClipping) return;
9699
+ const cfg = this.__edgeFadeRenderConfig;
9700
+ if (!cfg) return;
9701
+ const liveW = Number(this.width) || 0;
9702
+ const liveH = Number(this.height) || 0;
9703
+ const w = liveW > 0 ? liveW : cfg.frameW;
9704
+ const h = liveH > 0 ? liveH : cfg.frameH;
9705
+ const x = -w / 2;
9706
+ const y = -h / 2;
9707
+ const paintBand = (side, size, amount, hardness) => {
9708
+ if (size <= 0 || amount >= 1) return;
9709
+ ctx.save();
9710
+ ctx.globalCompositeOperation = "destination-out";
9711
+ let gradient;
9712
+ const eraseAtEdge = 1 - amount;
9713
+ const STOPS = 24;
9714
+ const addStops = (g) => {
9715
+ for (let i = 0; i <= STOPS; i++) {
9716
+ const t = i / STOPS;
9717
+ const keepProgress = Math.pow(t, hardness);
9718
+ const a = eraseAtEdge * (1 - keepProgress);
9719
+ g.addColorStop(t, `rgba(0,0,0,${a})`);
9720
+ }
9721
+ };
9722
+ if (side === "top") {
9723
+ const band = Math.max(1, h * size);
9724
+ gradient = ctx.createLinearGradient(0, y, 0, y + band);
9725
+ addStops(gradient);
9726
+ ctx.fillStyle = gradient;
9727
+ ctx.fillRect(x, y, w, band);
9728
+ if (amount <= 1e-3) ctx.fillRect(x, y, w, Math.min(2, band));
9729
+ } else if (side === "bottom") {
9730
+ const band = Math.max(1, h * size);
9731
+ gradient = ctx.createLinearGradient(0, y + h, 0, y + h - band);
9732
+ addStops(gradient);
9733
+ ctx.fillStyle = gradient;
9734
+ ctx.fillRect(x, y + h - band, w, band);
9735
+ if (amount <= 1e-3) ctx.fillRect(x, y + h - Math.min(2, band), w, Math.min(2, band));
9736
+ } else if (side === "left") {
9737
+ const band = Math.max(1, w * size);
9738
+ gradient = ctx.createLinearGradient(x, 0, x + band, 0);
9739
+ addStops(gradient);
9740
+ ctx.fillStyle = gradient;
9741
+ ctx.fillRect(x, y, band, h);
9742
+ if (amount <= 1e-3) ctx.fillRect(x, y, Math.min(2, band), h);
9743
+ } else {
9744
+ const band = Math.max(1, w * size);
9745
+ gradient = ctx.createLinearGradient(x + w, 0, x + w - band, 0);
9746
+ addStops(gradient);
9747
+ ctx.fillStyle = gradient;
9748
+ ctx.fillRect(x + w - band, y, band, h);
9749
+ if (amount <= 1e-3) ctx.fillRect(x + w - Math.min(2, band), y, Math.min(2, band), h);
9750
+ }
9751
+ ctx.restore();
9752
+ };
9753
+ paintBand("top", cfg.topSize, cfg.topAmount, cfg.topHardness);
9754
+ paintBand("right", cfg.rightSize, cfg.rightAmount, cfg.rightHardness);
9755
+ paintBand("bottom", cfg.bottomSize, cfg.bottomAmount, cfg.bottomHardness);
9756
+ paintBand("left", cfg.leftSize, cfg.leftAmount, cfg.leftHardness);
9757
+ };
9758
+ group.set({ objectCaching: true, noScaleCache: false });
9759
+ fadeGroup.__edgeFadeKey = fadeGroup.__edgeFadeRenderConfig.key;
9760
+ fadeGroup.__edgeFadeInputKey = inputKey;
9761
+ } else {
9762
+ if (fadeGroup.__edgeFadeOriginalDrawObject) {
9763
+ group.drawObject = fadeGroup.__edgeFadeOriginalDrawObject;
9764
+ }
9765
+ delete fadeGroup.__edgeFadeRenderConfig;
9766
+ delete fadeGroup.__edgeFadeKey;
9767
+ delete fadeGroup.__edgeFadeInputKey;
9768
+ if ((_b = group.clipPath) == null ? void 0 : _b.__edgeFadeMask) {
9769
+ group.clipPath = void 0;
9770
+ updateCoverLayout(group);
9771
+ }
9772
+ }
9773
+ if (group.clipPath) {
9774
+ group.clipPath.dirty = true;
9775
+ group.clipPath.setCoords();
9776
+ }
9777
+ fadeGroup.dirty = true;
9778
+ const children = group.getObjects();
9779
+ children.forEach((child) => {
9780
+ child.dirty = true;
9781
+ });
9782
+ (_c = group.canvas) == null ? void 0 : _c.requestRenderAll();
9783
+ };
9471
9784
  const loadImageAsync = async (element, placeholder, fc) => {
9472
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
9785
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p;
9473
9786
  const imageUrl = element.src || element.imageUrl;
9474
9787
  if (!imageUrl) return;
9475
9788
  const elementId = element.id;
@@ -9482,7 +9795,9 @@ const PageCanvas = forwardRef(
9482
9795
  const existingImg = ct == null ? void 0 : ct._img;
9483
9796
  const existingSrc = placeholder.__imageSrc;
9484
9797
  const existingSvgColorMap = placeholder.__svgColorMap || "";
9485
- if (existingImg && existingSrc === imageUrl && existingSvgColorMap === nextSvgColorMap) {
9798
+ const existingFadeKey = existingImg && existingImg.__edgeFadeKey || placeholder.__edgeFadeKey || "";
9799
+ const nextFadeKey = edgeFadeKey(element);
9800
+ if (existingImg && existingSrc === imageUrl && existingSvgColorMap === nextSvgColorMap && existingFadeKey === nextFadeKey) {
9486
9801
  return placeholder;
9487
9802
  }
9488
9803
  }
@@ -9499,6 +9814,22 @@ const PageCanvas = forwardRef(
9499
9814
  if (!fabricRef.current || !isLatestRequest()) return;
9500
9815
  await normalizeSvgImageDimensions(img, imageUrl, element.sourceFormat);
9501
9816
  if (!isLatestRequest()) return;
9817
+ const imageFitForFade = element.imageFit || ((_a = element.style) == null ? void 0 : _a.imageFit) || "cover";
9818
+ const clipShapeForFade = element.clipShape ?? ((_b = element.style) == null ? void 0 : _b.imageFrameShape) ?? (isPreviewMode ? "rectangle" : "none");
9819
+ const willUseCropGroupForFade = imageFitForFade !== "fill" || clipShapeForFade && clipShapeForFade !== "none";
9820
+ try {
9821
+ if (hasEdgeFade(element) && !willUseCropGroupForFade) {
9822
+ const srcEl = (_c = img.getElement) == null ? void 0 : _c.call(img);
9823
+ if (srcEl) {
9824
+ const baked = bakeEdgeFade(srcEl, element);
9825
+ img.setElement(baked);
9826
+ img.__edgeFadeKey = edgeFadeKey(element);
9827
+ img.dirty = true;
9828
+ }
9829
+ }
9830
+ } catch (e) {
9831
+ console.warn("[edgeFade] bake failed:", e);
9832
+ }
9502
9833
  const isHidden = !element.visible;
9503
9834
  img.set({
9504
9835
  originX: "left",
@@ -9531,8 +9862,8 @@ const PageCanvas = forwardRef(
9531
9862
  });
9532
9863
  img.setCoords();
9533
9864
  } else {
9534
- const imageFit = element.imageFit || ((_a = element.style) == null ? void 0 : _a.imageFit) || "cover";
9535
- const clipShape = element.clipShape ?? ((_b = element.style) == null ? void 0 : _b.imageFrameShape) ?? (isPreviewMode ? "rectangle" : "none");
9865
+ const imageFit = element.imageFit || ((_d = element.style) == null ? void 0 : _d.imageFit) || "cover";
9866
+ const clipShape = element.clipShape ?? ((_e = element.style) == null ? void 0 : _e.imageFrameShape) ?? (isPreviewMode ? "rectangle" : "none");
9536
9867
  const needCropGroup2 = imageFit !== "fill" || clipShape && clipShape !== "none";
9537
9868
  const imgNaturalWidth = img.width || 1;
9538
9869
  const imgNaturalHeight = img.height || 1;
@@ -9559,7 +9890,7 @@ const PageCanvas = forwardRef(
9559
9890
  if (imageFit === "fill" && !needCropGroup2) {
9560
9891
  const finalScaleX = baseScaleX * (element.scaleX ?? 1);
9561
9892
  const finalScaleY = baseScaleY * (element.scaleY ?? 1);
9562
- const pageTreeForCreate = ((pageChildren == null ? void 0 : pageChildren.length) ? pageChildren : (_c = useEditorStore.getState().canvas.pages.find((p) => p.id === pageId)) == null ? void 0 : _c.children) ?? [];
9893
+ const pageTreeForCreate = ((pageChildren == null ? void 0 : pageChildren.length) ? pageChildren : (_f = useEditorStore.getState().canvas.pages.find((p) => p.id === pageId)) == null ? void 0 : _f.children) ?? [];
9563
9894
  const createPos = pageTreeForCreate.length > 0 ? (() => {
9564
9895
  const node = findNodeById(pageTreeForCreate, element.id);
9565
9896
  return node ? getAbsoluteBounds(node, pageTreeForCreate) : { left: element.left ?? 0, top: element.top ?? 0 };
@@ -9604,12 +9935,12 @@ const PageCanvas = forwardRef(
9604
9935
  }
9605
9936
  img.__imageSrc = imageUrl;
9606
9937
  img.__svgColorMap = element.svgColorMap ? JSON.stringify(element.svgColorMap) : "";
9607
- const imageFitFinal = element.imageFit || ((_d = element.style) == null ? void 0 : _d.imageFit) || "cover";
9608
- const clipShapeFinal = element.clipShape ?? ((_e = element.style) == null ? void 0 : _e.imageFrameShape) ?? (isPreviewMode ? "rectangle" : "none");
9938
+ const imageFitFinal = element.imageFit || ((_g = element.style) == null ? void 0 : _g.imageFit) || "cover";
9939
+ const clipShapeFinal = element.clipShape ?? ((_h = element.style) == null ? void 0 : _h.imageFrameShape) ?? (isPreviewMode ? "rectangle" : "none");
9609
9940
  const needCropGroup = imageFitFinal !== "fill" || clipShapeFinal && clipShapeFinal !== "none";
9610
9941
  let finalObject = img;
9611
9942
  if (needCropGroup) {
9612
- const pageTreeForCropResolve = ((pageChildren == null ? void 0 : pageChildren.length) ? pageChildren : (_f = useEditorStore.getState().canvas.pages.find((p) => p.id === pageId)) == null ? void 0 : _f.children) ?? [];
9943
+ const pageTreeForCropResolve = ((pageChildren == null ? void 0 : pageChildren.length) ? pageChildren : (_i = useEditorStore.getState().canvas.pages.find((p) => p.id === pageId)) == null ? void 0 : _i.children) ?? [];
9613
9944
  const nodeForSize = pageTreeForCropResolve.length ? findNodeById(pageTreeForCropResolve, element.id) : null;
9614
9945
  const w = nodeForSize && isElement(nodeForSize) ? nodeForSize.width : element.width;
9615
9946
  const h = nodeForSize && isElement(nodeForSize) ? nodeForSize.height : element.height;
@@ -9641,16 +9972,16 @@ const PageCanvas = forwardRef(
9641
9972
  let panY = element.cropPanY ?? 0.5;
9642
9973
  let zoom2 = element.cropZoom ?? 1;
9643
9974
  if (existingCropGroup) {
9644
- const existingImg = (_g = existingCropGroup.__cropData) == null ? void 0 : _g._img;
9975
+ const existingImg = (_j = existingCropGroup.__cropData) == null ? void 0 : _j._img;
9645
9976
  if (existingImg) {
9646
- panX = ((_h = existingImg._ct) == null ? void 0 : _h.panX) ?? existingImg.__panX ?? panX;
9647
- panY = ((_i = existingImg._ct) == null ? void 0 : _i.panY) ?? existingImg.__panY ?? panY;
9648
- zoom2 = ((_j = existingImg._ct) == null ? void 0 : _j.zoom) ?? zoom2;
9977
+ panX = ((_k = existingImg._ct) == null ? void 0 : _k.panX) ?? existingImg.__panX ?? panX;
9978
+ panY = ((_l = existingImg._ct) == null ? void 0 : _l.panY) ?? existingImg.__panY ?? panY;
9979
+ zoom2 = ((_m = existingImg._ct) == null ? void 0 : _m.zoom) ?? zoom2;
9649
9980
  }
9650
9981
  }
9651
9982
  const isDynamicField = dynamicFieldIds.includes(element.id);
9652
9983
  const canBeEvented = isEditorMode || isPreviewMode && isDynamicField;
9653
- const pageTreeForCrop = ((pageChildren == null ? void 0 : pageChildren.length) ? pageChildren : (_k = useEditorStore.getState().canvas.pages.find((p) => p.id === pageId)) == null ? void 0 : _k.children) ?? [];
9984
+ const pageTreeForCrop = ((pageChildren == null ? void 0 : pageChildren.length) ? pageChildren : (_n = useEditorStore.getState().canvas.pages.find((p) => p.id === pageId)) == null ? void 0 : _n.children) ?? [];
9654
9985
  const createPosForCrop = pageTreeForCrop.length > 0 ? (() => {
9655
9986
  const node = findNodeById(pageTreeForCrop, element.id);
9656
9987
  return node ? getAbsoluteBounds(node, pageTreeForCrop) : { left: element.left ?? 0, top: element.top ?? 0 };
@@ -9701,17 +10032,18 @@ const PageCanvas = forwardRef(
9701
10032
  } else {
9702
10033
  installCanvaMaskControls(cropGroup);
9703
10034
  }
9704
- const cropImg = (_l = cropGroup.__cropData) == null ? void 0 : _l._img;
10035
+ const cropImg = (_o = cropGroup.__cropData) == null ? void 0 : _o._img;
9705
10036
  if (cropImg) {
9706
10037
  cropImg._ct = { panX, panY, zoom: zoom2 };
9707
10038
  updateCoverLayout(cropGroup);
9708
10039
  }
10040
+ applyEdgeFadeFrameClipPath(cropGroup, element, frameW, frameH, shape, rxRatio);
9709
10041
  setObjectData(cropGroup, element.id);
9710
10042
  cropGroup.__imageElement = cropImg;
9711
10043
  cropGroup.__imageSrc = imageUrl;
9712
10044
  cropGroup.__svgColorMap = nextSvgColorMap;
9713
10045
  if (cropImg && element.imageNaturalWidth == null) {
9714
- const el = ((_m = cropImg.getElement) == null ? void 0 : _m.call(cropImg)) ?? cropImg._element;
10046
+ const el = ((_p = cropImg.getElement) == null ? void 0 : _p.call(cropImg)) ?? cropImg._element;
9715
10047
  const orig = typeof cropImg.getOriginalSize === "function" ? cropImg.getOriginalSize() : null;
9716
10048
  const nw = (orig == null ? void 0 : orig.width) ?? (el == null ? void 0 : el.naturalWidth) ?? cropImg.width;
9717
10049
  const nh = (orig == null ? void 0 : orig.height) ?? (el == null ? void 0 : el.naturalHeight) ?? cropImg.height;
@@ -13084,7 +13416,7 @@ function PixldocsPreview(props) {
13084
13416
  !canvasSettled && /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: 0, display: "flex", alignItems: "center", justifyContent: "center", minHeight: 200 }, children: /* @__PURE__ */ jsx("div", { style: { color: "#888", fontSize: 14 }, children: "Loading preview..." }) })
13085
13417
  ] });
13086
13418
  }
13087
- const PACKAGE_VERSION = "0.5.99";
13419
+ const PACKAGE_VERSION = "0.5.101";
13088
13420
  const roundParityValue = (value) => {
13089
13421
  if (typeof value !== "number") return value;
13090
13422
  return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
@@ -13872,12 +14204,69 @@ class PixldocsRenderer {
13872
14204
  { width: canvasWidth, height: canvasHeight },
13873
14205
  { cssOnly: false, backstoreOnly: false }
13874
14206
  );
14207
+ const fadeBakeRecords = [];
14208
+ try {
14209
+ const objs = fabricInstance.getObjects().slice();
14210
+ for (const obj of objs) {
14211
+ const isFadedCropGroup = obj instanceof fabric.Group && (Boolean(obj.__edgeFadeRenderConfig) || Boolean(obj.__edgeFadeKey) || Boolean(obj.__edgeFadeInputKey));
14212
+ if (!isFadedCropGroup) continue;
14213
+ try {
14214
+ const baked = obj.toCanvasElement({
14215
+ multiplier: 2,
14216
+ enableRetinaScaling: false
14217
+ });
14218
+ const rect = obj.getBoundingRect();
14219
+ const replacement = new fabric.FabricImage(baked, {
14220
+ left: rect.left,
14221
+ top: rect.top,
14222
+ originX: "left",
14223
+ originY: "top",
14224
+ scaleX: rect.width / baked.width,
14225
+ scaleY: rect.height / baked.height,
14226
+ selectable: false,
14227
+ evented: false,
14228
+ objectCaching: false
14229
+ });
14230
+ const insertIndex = fabricInstance._objects.indexOf(obj);
14231
+ const prevExclude = obj.excludeFromExport;
14232
+ obj.excludeFromExport = true;
14233
+ if (insertIndex >= 0) {
14234
+ fabricInstance.insertAt(insertIndex + 1, replacement);
14235
+ } else {
14236
+ fabricInstance.add(replacement);
14237
+ }
14238
+ fadeBakeRecords.push({
14239
+ original: obj,
14240
+ replacement,
14241
+ prevExclude,
14242
+ insertIndex
14243
+ });
14244
+ } catch (bakeErr) {
14245
+ console.warn("[canvas-renderer][edgeFade] bake-for-svg failed:", bakeErr);
14246
+ }
14247
+ }
14248
+ if (fadeBakeRecords.length) {
14249
+ fabricInstance.renderAll();
14250
+ console.log(
14251
+ `[canvas-renderer][edgeFade] baked ${fadeBakeRecords.length} faded object(s) for SVG capture`
14252
+ );
14253
+ }
14254
+ } catch (e) {
14255
+ console.warn("[canvas-renderer][edgeFade] bake pass failed:", e);
14256
+ }
13875
14257
  const rawSvgString = fabricInstance.toSVG();
13876
14258
  const svgString = this.normalizeSvgDimensions(
13877
14259
  rawSvgString,
13878
14260
  canvasWidth,
13879
14261
  canvasHeight
13880
14262
  );
14263
+ for (const rec of fadeBakeRecords) {
14264
+ try {
14265
+ fabricInstance.remove(rec.replacement);
14266
+ rec.original.excludeFromExport = rec.prevExclude;
14267
+ } catch {
14268
+ }
14269
+ }
13881
14270
  fabricInstance.enableRetinaScaling = prevRetina;
13882
14271
  fabricInstance.setDimensions(
13883
14272
  { width: prevWidth, height: prevHeight },
@@ -15214,6 +15603,33 @@ const GRADIENT_ATTRS_LINEAR = ["x1", "y1", "x2", "y2", "gradientUnits", "gradien
15214
15603
  const GRADIENT_ATTRS_RADIAL = ["cx", "cy", "r", "fx", "fy", "gradientUnits", "gradientTransform", "spreadMethod"];
15215
15604
  const URL_GRADIENT_RE = /^\s*url\s*\(\s*(['"]?)([^)]+?)\1\s*\)/i;
15216
15605
  const SHADOW_RASTER_ALPHA_COMPENSATION = 0.84;
15606
+ function collectFontSpecsFromMarkup(markup) {
15607
+ const specs = /* @__PURE__ */ new Set();
15608
+ const re = /<(?:text|tspan)\b[^>]*>/gi;
15609
+ let match;
15610
+ while ((match = re.exec(markup)) !== null) {
15611
+ const tag = match[0];
15612
+ const get = (attr) => {
15613
+ const m = tag.match(new RegExp(`\\s${attr}\\s*=\\s*"([^"]*)"`, "i"));
15614
+ if (m) return m[1];
15615
+ const styleM = tag.match(/\sstyle\s*=\s*"([^"]*)"/i);
15616
+ if (styleM) {
15617
+ const sm = styleM[1].match(new RegExp(`${attr}\\s*:\\s*([^;]+)`, "i"));
15618
+ if (sm) return sm[1].trim();
15619
+ }
15620
+ return null;
15621
+ };
15622
+ const family = (get("font-family") || "").split(",")[0].replace(/['"]/g, "").trim();
15623
+ if (!family) continue;
15624
+ const weight = get("font-weight") || "400";
15625
+ const style = get("font-style") || "normal";
15626
+ const size = get("font-size") || "16px";
15627
+ const sizePx = /[a-z%]/i.test(size) ? size : `${size}px`;
15628
+ const famSpec = /\s/.test(family) ? `"${family}"` : family;
15629
+ specs.add(`${style} ${weight} ${sizePx} ${famSpec}`);
15630
+ }
15631
+ return Array.from(specs);
15632
+ }
15217
15633
  function parseColor(color) {
15218
15634
  if (!color) return null;
15219
15635
  const raw = color.trim().toLowerCase();
@@ -16249,7 +16665,7 @@ async function convertSvgTextDecorationsToLinesString(svgStr) {
16249
16665
  }
16250
16666
  }
16251
16667
  async function rasterizeShadowMarkers(svg) {
16252
- var _a, _b, _c, _d, _e;
16668
+ var _a, _b, _c, _d, _e, _f;
16253
16669
  if (typeof window === "undefined" || typeof document === "undefined") return;
16254
16670
  const markers = Array.from(svg.querySelectorAll("g.__pdShadowRaster"));
16255
16671
  if (markers.length === 0) return;
@@ -16274,6 +16690,17 @@ async function rasterizeShadowMarkers(svg) {
16274
16690
  continue;
16275
16691
  }
16276
16692
  const innerXml = Array.from(marker.childNodes).map((n) => n instanceof Element ? new XMLSerializer().serializeToString(n) : "").join("");
16693
+ try {
16694
+ const fontSpecs = collectFontSpecsFromMarkup(innerXml);
16695
+ if (fontSpecs.length > 0 && ((_c = document.fonts) == null ? void 0 : _c.load)) {
16696
+ await Promise.all(
16697
+ fontSpecs.map(
16698
+ (spec) => document.fonts.load(spec).catch(() => void 0)
16699
+ )
16700
+ );
16701
+ }
16702
+ } catch {
16703
+ }
16277
16704
  const scale = 2;
16278
16705
  const pxW = Math.min(4096, Math.max(8, Math.ceil(bw * scale)));
16279
16706
  const pxH = Math.min(4096, Math.max(8, Math.ceil(bh * scale)));
@@ -16283,7 +16710,7 @@ async function rasterizeShadowMarkers(svg) {
16283
16710
  const miniSvg = `<svg xmlns="${SVG_NS}" xmlns:xlink="${XLINK_NS}" width="${pxW}" height="${pxH}" viewBox="${bx} ${by} ${bw} ${bh}">${styleBlock}<defs><filter id="${filterId}" filterUnits="userSpaceOnUse" x="${bx}" y="${by}" width="${bw}" height="${bh}" color-interpolation-filters="sRGB"><feOffset dx="${ox}" dy="${oy}" result="offsetShadow" /><feGaussianBlur in="offsetShadow" stdDeviation="${stdDev}" /></filter></defs><g filter="url(#${filterId})">${innerXml}</g></svg>`;
16284
16711
  const dataUrl = await rasterSvgToPngDataUrl(miniSvg, pxW, pxH);
16285
16712
  if (!dataUrl) {
16286
- (_c = marker.parentNode) == null ? void 0 : _c.removeChild(marker);
16713
+ (_d = marker.parentNode) == null ? void 0 : _d.removeChild(marker);
16287
16714
  continue;
16288
16715
  }
16289
16716
  const img = svg.ownerDocument.createElementNS(SVG_NS, "image");
@@ -16295,11 +16722,11 @@ async function rasterizeShadowMarkers(svg) {
16295
16722
  img.setAttribute("preserveAspectRatio", "none");
16296
16723
  img.setAttributeNS(XLINK_NS, "xlink:href", dataUrl);
16297
16724
  img.setAttribute("href", dataUrl);
16298
- (_d = marker.parentNode) == null ? void 0 : _d.replaceChild(img, marker);
16725
+ (_e = marker.parentNode) == null ? void 0 : _e.replaceChild(img, marker);
16299
16726
  } catch (e) {
16300
16727
  console.warn("[pdf-export] rasterizeShadowMarkers failed for one marker:", e);
16301
16728
  try {
16302
- (_e = marker.parentNode) == null ? void 0 : _e.removeChild(marker);
16729
+ (_f = marker.parentNode) == null ? void 0 : _f.removeChild(marker);
16303
16730
  } catch {
16304
16731
  }
16305
16732
  }