@pixldocs/canvas-renderer 0.5.213 → 0.5.215

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.
@@ -6884,9 +6884,9 @@ function buildTextShadow(element) {
6884
6884
  if (!color || color === "transparent") return null;
6885
6885
  const type = element.textShadowType;
6886
6886
  if (type && type !== "drop") return null;
6887
- const { passAlphas } = resolveShadowStrength(element);
6888
- const firstAlpha = passAlphas.length > 0 ? passAlphas[0] : 0;
6889
- const finalColor = applyAlphaMultiplier(String(color), firstAlpha);
6887
+ const alpha = resolveShadowAlpha(element);
6888
+ if (alpha <= 0) return null;
6889
+ const finalColor = applyAlphaMultiplier(String(color), alpha);
6890
6890
  return new fabric.Shadow({
6891
6891
  color: finalColor,
6892
6892
  blur: blur || 0,
@@ -6896,67 +6896,39 @@ function buildTextShadow(element) {
6896
6896
  nonScaling: false
6897
6897
  });
6898
6898
  }
6899
- function resolveShadowStrength(element) {
6899
+ function readShadowStrength(element) {
6900
6900
  const raw = element.textShadowStrength;
6901
- const s = typeof raw === "number" && Number.isFinite(raw) ? Math.max(0, Math.min(100, raw)) : 33.34;
6902
- if (s <= 0) return { passAlphas: [] };
6903
- const intensity = Math.min(3, s / 33.34);
6904
- if (intensity <= 1) return { passAlphas: [intensity] };
6905
- const full = Math.floor(intensity);
6906
- const frac = intensity - full;
6907
- const arr = new Array(full).fill(1);
6908
- if (frac > 0.01) arr.push(frac);
6909
- return { passAlphas: arr };
6901
+ return typeof raw === "number" && Number.isFinite(raw) ? Math.max(0, Math.min(100, raw)) : 100;
6902
+ }
6903
+ function resolveShadowSourceSpread(element) {
6904
+ const strength = readShadowStrength(element);
6905
+ const blur = Math.max(0, Number(element.textShadowBlur ?? 0) || 0);
6906
+ const fontSize = Math.max(1, Number(element.fontSize ?? 16) || 16);
6907
+ const t = Math.max(0, (strength - 35) / 65);
6908
+ return Math.min(8, blur * 0.16, fontSize * 0.08) * t * t;
6909
+ }
6910
+ function resolveShadowAlpha(element) {
6911
+ const s = readShadowStrength(element);
6912
+ return s / 100;
6910
6913
  }
6911
6914
  function applyTextShadow(textbox, element) {
6912
6915
  const canonicalShadow = buildTextShadow(element);
6913
- const { passAlphas } = resolveShadowStrength(element);
6914
- const baseColor = element.textShadowColor;
6915
- const blur = Number(element.textShadowBlur ?? 0) || 0;
6916
- const ox = Number(element.textShadowOffsetX ?? 0) || 0;
6917
- const oy = Number(element.textShadowOffsetY ?? 0) || 0;
6918
- const hasShadow = !!canonicalShadow && passAlphas.length > 0;
6919
6916
  const obj = textbox;
6920
- if (!obj.__pdShadowOrigRender) {
6921
- obj.__pdShadowOrigRender = obj.render.bind(obj);
6922
- obj.render = function(ctx) {
6923
- const alphas = obj.__pdShadowPassAlphas;
6924
- if (!alphas || alphas.length <= 1) {
6925
- obj.__pdShadowOrigRender(ctx);
6926
- return;
6927
- }
6928
- const orig = obj.shadow;
6929
- const baseC = obj.__pdShadowBaseColor || "#000";
6930
- const b = obj.__pdShadowBlur || 0;
6931
- const x = obj.__pdShadowOX || 0;
6932
- const y = obj.__pdShadowOY || 0;
6933
- for (let i = 0; i < alphas.length; i++) {
6934
- obj.shadow = new fabric.Shadow({
6935
- color: applyAlphaMultiplier(String(baseC), alphas[i]),
6936
- blur: b,
6937
- offsetX: x,
6938
- offsetY: y,
6939
- affectStroke: false,
6940
- nonScaling: false
6941
- });
6942
- obj.__pdShadowOrigRender(ctx);
6943
- }
6944
- obj.shadow = orig;
6945
- };
6946
- }
6947
- if (hasShadow) {
6948
- obj.__pdShadowPassAlphas = passAlphas;
6949
- obj.__pdShadowBaseColor = baseColor;
6950
- obj.__pdShadowBlur = blur;
6951
- obj.__pdShadowOX = ox;
6952
- obj.__pdShadowOY = oy;
6953
- obj.__pdShadowPasses = passAlphas.length;
6954
- obj.__pdShadowLastAlpha = passAlphas[passAlphas.length - 1];
6955
- } else {
6917
+ if (obj.__pdShadowOrigRender) {
6918
+ try {
6919
+ obj.render = obj.__pdShadowOrigRender;
6920
+ } catch {
6921
+ }
6922
+ obj.__pdShadowOrigRender = void 0;
6956
6923
  obj.__pdShadowPassAlphas = void 0;
6957
- obj.__pdShadowPasses = 0;
6958
- obj.__pdShadowLastAlpha = 1;
6959
- }
6924
+ obj.__pdShadowBaseColor = void 0;
6925
+ obj.__pdShadowBlur = void 0;
6926
+ obj.__pdShadowOX = void 0;
6927
+ obj.__pdShadowOY = void 0;
6928
+ }
6929
+ obj.__pdShadowAlpha = canonicalShadow ? resolveShadowAlpha(element) : 1;
6930
+ obj.__pdShadowBaseColor = canonicalShadow ? String(element.textShadowColor || "") : void 0;
6931
+ obj.__pdShadowSourceSpread = canonicalShadow ? resolveShadowSourceSpread(element) : 0;
6960
6932
  textbox.set("shadow", canonicalShadow ?? null);
6961
6933
  }
6962
6934
  function applyAlphaMultiplier(c, mult) {
@@ -7240,6 +7212,8 @@ function applyTextBackground(obj, cfg) {
7240
7212
  }
7241
7213
  }
7242
7214
  const suppressShadowOnText = bg && bg.shadowAffectsText === false;
7215
+ const dropShadowSpread = Math.max(0, Number(this.__pdShadowSourceSpread) || 0);
7216
+ const dropShadowActive = dropShadowSpread > 0 && !!this.shadow && !suppressShadowOnText && !blockShadowActive && !lineShadowActive;
7243
7217
  if (suppressShadowOnText) {
7244
7218
  ctx.save();
7245
7219
  ctx.shadowColor = "transparent";
@@ -7248,6 +7222,44 @@ function applyTextBackground(obj, cfg) {
7248
7222
  ctx.shadowOffsetY = 0;
7249
7223
  originalRender(ctx);
7250
7224
  ctx.restore();
7225
+ } else if (dropShadowActive) {
7226
+ const self = this;
7227
+ const shadow = self.shadow;
7228
+ const shadowColor = String((shadow == null ? void 0 : shadow.color) || this.__pdShadowBaseColor || "rgba(0,0,0,1)");
7229
+ const shadowBlur = Math.max(0, Number((shadow == null ? void 0 : shadow.blur) ?? 0) || 0);
7230
+ const shadowOffsetX = Number((shadow == null ? void 0 : shadow.offsetX) ?? 0) || 0;
7231
+ const shadowOffsetY = Number((shadow == null ? void 0 : shadow.offsetY) ?? 0) || 0;
7232
+ const origFill = self.fill;
7233
+ const origStroke = self.stroke;
7234
+ const origStrokeWidth = self.strokeWidth;
7235
+ const origStyles = self.styles;
7236
+ const origShadow = self.shadow;
7237
+ try {
7238
+ ctx.save();
7239
+ self.shadow = null;
7240
+ self.fill = shadowColor;
7241
+ self.styles = {};
7242
+ self.stroke = shadowColor;
7243
+ self.strokeWidth = dropShadowSpread;
7244
+ ctx.translate(shadowOffsetX, shadowOffsetY);
7245
+ if (shadowBlur > 0) ctx.filter = `blur(${shadowBlur / 2}px)`;
7246
+ ctx.lineJoin = "round";
7247
+ ctx.lineCap = "round";
7248
+ originalRender(ctx);
7249
+ ctx.restore();
7250
+ self.fill = origFill;
7251
+ self.stroke = origStroke;
7252
+ self.strokeWidth = origStrokeWidth;
7253
+ self.styles = origStyles;
7254
+ self.shadow = null;
7255
+ originalRender(ctx);
7256
+ } finally {
7257
+ self.fill = origFill;
7258
+ self.stroke = origStroke;
7259
+ self.strokeWidth = origStrokeWidth;
7260
+ self.styles = origStyles;
7261
+ self.shadow = origShadow;
7262
+ }
7251
7263
  } else {
7252
7264
  originalRender(ctx);
7253
7265
  }
@@ -7329,7 +7341,8 @@ function applyTextBackground(obj, cfg) {
7329
7341
  const oy = Number(shadow.offsetY ?? 0) || 0;
7330
7342
  const blur = Math.max(0, Number(shadow.blur ?? 0));
7331
7343
  const shadowColor = String(shadow.color);
7332
- const pad = Math.max(16, Math.ceil(blur * 4) + Math.ceil(Math.max(Math.abs(ox), Math.abs(oy))) + 8);
7344
+ const sourceSpread = Math.max(0, Number(this.__pdShadowSourceSpread) || 0);
7345
+ const pad = Math.max(16, Math.ceil((blur + sourceSpread) * 4) + Math.ceil(Math.max(Math.abs(ox), Math.abs(oy))) + 8);
7333
7346
  const shadowBounds = unionBounds([
7334
7347
  ...rects,
7335
7348
  computeTextVisualBounds(this, w, h)
@@ -7338,10 +7351,9 @@ function applyTextBackground(obj, cfg) {
7338
7351
  const by = shadowBounds.y - pad;
7339
7352
  const bw = shadowBounds.w + pad * 2;
7340
7353
  const bh = shadowBounds.h + pad * 2;
7341
- const passes = Math.max(1, Math.min(8, Number(this.__pdShadowPasses) || 1));
7342
- const lastAlphaRaw = Number(this.__pdShadowLastAlpha);
7343
- const lastAlpha = Number.isFinite(lastAlphaRaw) ? Math.max(0, Math.min(1, lastAlphaRaw)) : 1;
7344
- 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)}" data-passes="${passes}" data-last-alpha="${lastAlpha.toFixed(3)}"`;
7354
+ const alphaRaw = Number(this.__pdShadowAlpha);
7355
+ const shadowAlpha = Number.isFinite(alphaRaw) ? Math.max(0, Math.min(1, alphaRaw)) : 1;
7356
+ const dataAttrs = `data-blur="${blur.toFixed(3)}" data-spread="${sourceSpread.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)}" data-alpha="${shadowAlpha.toFixed(3)}"`;
7345
7357
  const wrapShadow = (markup) => blur <= 0 ? `<g transform="translate(${ox.toFixed(3)} ${oy.toFixed(3)})">${markup}</g>` : `<g class="__pdShadowRaster" ${dataAttrs}>${markup}</g>`;
7346
7358
  if (hasBg && (bg == null ? void 0 : bg.shadowAffectsBg) !== false) {
7347
7359
  const shadowOpacityAttr = bgOpacity < 1 ? ` fill-opacity="${bgOpacity}"` : "";
@@ -7350,7 +7362,7 @@ function applyTextBackground(obj, cfg) {
7350
7362
  }
7351
7363
  if ((bg == null ? void 0 : bg.shadowAffectsText) !== false && !hasActiveTextPath2) {
7352
7364
  const inner = extractGInnerMarkup(svg);
7353
- const recoloredText = recolorSvgFills(inner, shadowColor);
7365
+ const recoloredText = recolorSvgFills(inner, shadowColor, sourceSpread);
7354
7366
  if (recoloredText) textShadowMarker = wrapShadow(recoloredText);
7355
7367
  }
7356
7368
  }
@@ -7420,12 +7432,13 @@ function applyTextBackground(obj, cfg) {
7420
7432
  };
7421
7433
  }
7422
7434
  }
7423
- function recolorSvgFills(svg, color) {
7424
- return _recolorSvgFills(svg, color);
7435
+ function recolorSvgFills(svg, color, strokeWidth = 0) {
7436
+ return _recolorSvgFills(svg, color, strokeWidth);
7425
7437
  }
7426
- function _recolorSvgFills(svg, color) {
7438
+ function _recolorSvgFills(svg, color, strokeWidth = 0) {
7427
7439
  var _a;
7428
7440
  const safe = escapeXmlAttr(color);
7441
+ const spread = Math.max(0, Number(strokeWidth) || 0);
7429
7442
  try {
7430
7443
  const wrapped = `<svg xmlns="http://www.w3.org/2000/svg">${svg}</svg>`;
7431
7444
  const doc = new DOMParser().parseFromString(wrapped, "image/svg+xml");
@@ -7447,6 +7460,12 @@ function _recolorSvgFills(svg, color) {
7447
7460
  else el.removeAttribute("style");
7448
7461
  }
7449
7462
  el.setAttribute("fill", color);
7463
+ if (spread > 0 && /^(text|tspan|path)$/i.test(el.tagName)) {
7464
+ el.setAttribute("stroke", color);
7465
+ el.setAttribute("stroke-width", String(Number(spread.toFixed(3))));
7466
+ el.setAttribute("stroke-linejoin", "round");
7467
+ el.setAttribute("stroke-linecap", "round");
7468
+ }
7450
7469
  }
7451
7470
  let out2 = "";
7452
7471
  for (const child of Array.from(root.childNodes)) {
@@ -7476,6 +7495,13 @@ function _recolorSvgFills(svg, color) {
7476
7495
  if (/style="[^"]*\bfill\s*:/i.test(attrs)) return m;
7477
7496
  return `<${tag}${attrs} fill="${safe}">`;
7478
7497
  });
7498
+ if (spread > 0) {
7499
+ const strokeAttrs = ` stroke="${safe}" stroke-width="${Number(spread.toFixed(3))}" stroke-linejoin="round" stroke-linecap="round"`;
7500
+ out = out.replace(/<(text|tspan|path)\b([^>]*)>/gi, (m, tag, attrs) => {
7501
+ let nextAttrs = String(attrs).replace(/\s(?:stroke|stroke-width|stroke-linejoin|stroke-linecap)="[^"]*"/gi, "");
7502
+ return `<${tag}${nextAttrs}${strokeAttrs}>`;
7503
+ });
7504
+ }
7479
7505
  return out;
7480
7506
  }
7481
7507
  function buildRoundedRectPathD(x, y, w, h, rTL, rTR, rBR, rBL) {
@@ -19057,9 +19083,9 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
19057
19083
  }
19058
19084
  return svgString;
19059
19085
  }
19060
- const resolvedPackageVersion = "0.5.213";
19086
+ const resolvedPackageVersion = "0.5.215";
19061
19087
  const PACKAGE_VERSION = resolvedPackageVersion;
19062
- const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.213";
19088
+ const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.215";
19063
19089
  const roundParityValue = (value) => {
19064
19090
  if (typeof value !== "number") return value;
19065
19091
  return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
@@ -19751,7 +19777,7 @@ class PixldocsRenderer {
19751
19777
  await this.waitForCanvasScene(container, cloned, i);
19752
19778
  }
19753
19779
  console.log(`[canvas-renderer][pdf-unified] mounted ${cloned.pages.length} page(s), handing off to client exportMultiPagePdf`);
19754
- const { exportMultiPagePdf, preparePagesForExport } = await import("./vectorPdfExport-ox1nl4G9.js");
19780
+ const { exportMultiPagePdf, preparePagesForExport } = await import("./vectorPdfExport-CN1fcrfR.js");
19755
19781
  const prepared = preparePagesForExport(
19756
19782
  cloned.pages,
19757
19783
  canvasWidth,
@@ -20606,7 +20632,7 @@ const SVG_STYLE_PROPS = /* @__PURE__ */ new Set([
20606
20632
  const GRADIENT_ATTRS_LINEAR = ["x1", "y1", "x2", "y2", "gradientUnits", "gradientTransform", "spreadMethod"];
20607
20633
  const GRADIENT_ATTRS_RADIAL = ["cx", "cy", "r", "fx", "fy", "gradientUnits", "gradientTransform", "spreadMethod"];
20608
20634
  const URL_GRADIENT_RE = /^\s*url\s*\(\s*(['"]?)([^)]+?)\1\s*\)/i;
20609
- const SHADOW_RASTER_ALPHA_COMPENSATION = 0.84;
20635
+ const SHADOW_RASTER_ALPHA_COMPENSATION = 1;
20610
20636
  function collectFontSpecsFromMarkup(markup) {
20611
20637
  const specs = /* @__PURE__ */ new Set();
20612
20638
  const re = /<(?:text|tspan)\b[^>]*>/gi;
@@ -21673,7 +21699,7 @@ async function convertSvgTextDecorationsToLinesString(svgStr) {
21673
21699
  }
21674
21700
  }
21675
21701
  async function rasterizeShadowMarkers(svg) {
21676
- var _a, _b, _c, _d, _e, _f, _g;
21702
+ var _a, _b, _c, _d, _e, _f;
21677
21703
  if (typeof window === "undefined" || typeof document === "undefined") return;
21678
21704
  const markers = Array.from(svg.querySelectorAll("g.__pdShadowRaster"));
21679
21705
  if (markers.length === 0) return;
@@ -21728,29 +21754,15 @@ async function rasterizeShadowMarkers(svg) {
21728
21754
  img.setAttribute("y", String(by));
21729
21755
  img.setAttribute("width", String(bw));
21730
21756
  img.setAttribute("height", String(bh));
21731
- img.setAttribute("opacity", String(SHADOW_RASTER_ALPHA_COMPENSATION));
21732
21757
  img.setAttribute("preserveAspectRatio", "none");
21733
21758
  img.setAttributeNS(XLINK_NS, "xlink:href", dataUrl);
21734
21759
  img.setAttribute("href", dataUrl);
21735
- const passes = Math.max(1, Math.min(8, parseInt(marker.getAttribute("data-passes") || "1", 10) || 1));
21736
- const lastAlphaRaw = parseFloat(marker.getAttribute("data-last-alpha") || "1");
21737
- const lastAlpha = Number.isFinite(lastAlphaRaw) ? Math.max(0, Math.min(1, lastAlphaRaw)) : 1;
21738
- const baseOpacity = Number(img.getAttribute("opacity") || "1") || 1;
21760
+ img.setAttribute("opacity", String(SHADOW_RASTER_ALPHA_COMPENSATION));
21739
21761
  (_e = marker.parentNode) == null ? void 0 : _e.replaceChild(img, marker);
21740
- for (let i = 1; i < passes; i++) {
21741
- const extra = img.cloneNode(true);
21742
- if (i === passes - 1 && lastAlpha < 1) {
21743
- extra.setAttribute("opacity", String(baseOpacity * lastAlpha));
21744
- }
21745
- (_f = img.parentNode) == null ? void 0 : _f.insertBefore(extra, img.nextSibling);
21746
- }
21747
- if (passes === 1 && lastAlpha < 1) {
21748
- img.setAttribute("opacity", String(baseOpacity * lastAlpha));
21749
- }
21750
21762
  } catch (e) {
21751
21763
  console.warn("[pdf-export] rasterizeShadowMarkers failed for one marker:", e);
21752
21764
  try {
21753
- (_g = marker.parentNode) == null ? void 0 : _g.removeChild(marker);
21765
+ (_f = marker.parentNode) == null ? void 0 : _f.removeChild(marker);
21754
21766
  } catch {
21755
21767
  }
21756
21768
  }
@@ -21951,7 +21963,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
21951
21963
  if (options == null ? void 0 : options.stripPageBackground) stripRootPageBackgroundFromSvg(svgToDraw);
21952
21964
  sanitizeSvgTreeForPdf(svgToDraw);
21953
21965
  try {
21954
- const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await import("./vectorPdfExport-ox1nl4G9.js");
21966
+ const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await import("./vectorPdfExport-CN1fcrfR.js");
21955
21967
  try {
21956
21968
  await logTextMeasurementDiagnostic(svgToDraw);
21957
21969
  } catch {
@@ -22351,4 +22363,4 @@ export {
22351
22363
  buildTeaserBlurFlatKeys as y,
22352
22364
  collectFontDescriptorsFromConfig as z
22353
22365
  };
22354
- //# sourceMappingURL=index-XZf4TDQ-.js.map
22366
+ //# sourceMappingURL=index-BV_MP9XQ.js.map