@pixldocs/canvas-renderer 0.5.214 → 0.5.216

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.
@@ -6896,9 +6896,19 @@ function buildTextShadow(element) {
6896
6896
  nonScaling: false
6897
6897
  });
6898
6898
  }
6899
- function resolveShadowAlpha(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)) : 100;
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);
6902
6912
  return s / 100;
6903
6913
  }
6904
6914
  function applyTextShadow(textbox, element) {
@@ -6917,6 +6927,8 @@ function applyTextShadow(textbox, element) {
6917
6927
  obj.__pdShadowOY = void 0;
6918
6928
  }
6919
6929
  obj.__pdShadowAlpha = canonicalShadow ? resolveShadowAlpha(element) : 1;
6930
+ obj.__pdShadowBaseColor = canonicalShadow ? String(element.textShadowColor || "") : void 0;
6931
+ obj.__pdShadowSourceSpread = canonicalShadow ? resolveShadowSourceSpread(element) : 0;
6920
6932
  textbox.set("shadow", canonicalShadow ?? null);
6921
6933
  }
6922
6934
  function applyAlphaMultiplier(c, mult) {
@@ -7200,6 +7212,8 @@ function applyTextBackground(obj, cfg) {
7200
7212
  }
7201
7213
  }
7202
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;
7203
7217
  if (suppressShadowOnText) {
7204
7218
  ctx.save();
7205
7219
  ctx.shadowColor = "transparent";
@@ -7208,6 +7222,44 @@ function applyTextBackground(obj, cfg) {
7208
7222
  ctx.shadowOffsetY = 0;
7209
7223
  originalRender(ctx);
7210
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
+ }
7211
7263
  } else {
7212
7264
  originalRender(ctx);
7213
7265
  }
@@ -7289,7 +7341,8 @@ function applyTextBackground(obj, cfg) {
7289
7341
  const oy = Number(shadow.offsetY ?? 0) || 0;
7290
7342
  const blur = Math.max(0, Number(shadow.blur ?? 0));
7291
7343
  const shadowColor = String(shadow.color);
7292
- 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);
7293
7346
  const shadowBounds = unionBounds([
7294
7347
  ...rects,
7295
7348
  computeTextVisualBounds(this, w, h)
@@ -7300,7 +7353,7 @@ function applyTextBackground(obj, cfg) {
7300
7353
  const bh = shadowBounds.h + pad * 2;
7301
7354
  const alphaRaw = Number(this.__pdShadowAlpha);
7302
7355
  const shadowAlpha = Number.isFinite(alphaRaw) ? Math.max(0, Math.min(1, alphaRaw)) : 1;
7303
- 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-alpha="${shadowAlpha.toFixed(3)}"`;
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)}"`;
7304
7357
  const wrapShadow = (markup) => blur <= 0 ? `<g transform="translate(${ox.toFixed(3)} ${oy.toFixed(3)})">${markup}</g>` : `<g class="__pdShadowRaster" ${dataAttrs}>${markup}</g>`;
7305
7358
  if (hasBg && (bg == null ? void 0 : bg.shadowAffectsBg) !== false) {
7306
7359
  const shadowOpacityAttr = bgOpacity < 1 ? ` fill-opacity="${bgOpacity}"` : "";
@@ -7309,7 +7362,7 @@ function applyTextBackground(obj, cfg) {
7309
7362
  }
7310
7363
  if ((bg == null ? void 0 : bg.shadowAffectsText) !== false && !hasActiveTextPath2) {
7311
7364
  const inner = extractGInnerMarkup(svg);
7312
- const recoloredText = recolorSvgFills(inner, shadowColor);
7365
+ const recoloredText = recolorSvgFills(inner, shadowColor, sourceSpread);
7313
7366
  if (recoloredText) textShadowMarker = wrapShadow(recoloredText);
7314
7367
  }
7315
7368
  }
@@ -7379,12 +7432,13 @@ function applyTextBackground(obj, cfg) {
7379
7432
  };
7380
7433
  }
7381
7434
  }
7382
- function recolorSvgFills(svg, color) {
7383
- return _recolorSvgFills(svg, color);
7435
+ function recolorSvgFills(svg, color, strokeWidth = 0) {
7436
+ return _recolorSvgFills(svg, color, strokeWidth);
7384
7437
  }
7385
- function _recolorSvgFills(svg, color) {
7438
+ function _recolorSvgFills(svg, color, strokeWidth = 0) {
7386
7439
  var _a;
7387
7440
  const safe = escapeXmlAttr(color);
7441
+ const spread = Math.max(0, Number(strokeWidth) || 0);
7388
7442
  try {
7389
7443
  const wrapped = `<svg xmlns="http://www.w3.org/2000/svg">${svg}</svg>`;
7390
7444
  const doc = new DOMParser().parseFromString(wrapped, "image/svg+xml");
@@ -7406,6 +7460,12 @@ function _recolorSvgFills(svg, color) {
7406
7460
  else el.removeAttribute("style");
7407
7461
  }
7408
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
+ }
7409
7469
  }
7410
7470
  let out2 = "";
7411
7471
  for (const child of Array.from(root.childNodes)) {
@@ -7435,6 +7495,13 @@ function _recolorSvgFills(svg, color) {
7435
7495
  if (/style="[^"]*\bfill\s*:/i.test(attrs)) return m;
7436
7496
  return `<${tag}${attrs} fill="${safe}">`;
7437
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
+ }
7438
7505
  return out;
7439
7506
  }
7440
7507
  function buildRoundedRectPathD(x, y, w, h, rTL, rTR, rBR, rBL) {
@@ -19016,9 +19083,9 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
19016
19083
  }
19017
19084
  return svgString;
19018
19085
  }
19019
- const resolvedPackageVersion = "0.5.214";
19086
+ const resolvedPackageVersion = "0.5.216";
19020
19087
  const PACKAGE_VERSION = resolvedPackageVersion;
19021
- const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.214";
19088
+ const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.216";
19022
19089
  const roundParityValue = (value) => {
19023
19090
  if (typeof value !== "number") return value;
19024
19091
  return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
@@ -19710,7 +19777,7 @@ class PixldocsRenderer {
19710
19777
  await this.waitForCanvasScene(container, cloned, i);
19711
19778
  }
19712
19779
  console.log(`[canvas-renderer][pdf-unified] mounted ${cloned.pages.length} page(s), handing off to client exportMultiPagePdf`);
19713
- const { exportMultiPagePdf, preparePagesForExport } = await import("./vectorPdfExport-CjIUU8on.js");
19780
+ const { exportMultiPagePdf, preparePagesForExport } = await import("./vectorPdfExport-DZD415Qf.js");
19714
19781
  const prepared = preparePagesForExport(
19715
19782
  cloned.pages,
19716
19783
  canvasWidth,
@@ -20565,7 +20632,7 @@ const SVG_STYLE_PROPS = /* @__PURE__ */ new Set([
20565
20632
  const GRADIENT_ATTRS_LINEAR = ["x1", "y1", "x2", "y2", "gradientUnits", "gradientTransform", "spreadMethod"];
20566
20633
  const GRADIENT_ATTRS_RADIAL = ["cx", "cy", "r", "fx", "fy", "gradientUnits", "gradientTransform", "spreadMethod"];
20567
20634
  const URL_GRADIENT_RE = /^\s*url\s*\(\s*(['"]?)([^)]+?)\1\s*\)/i;
20568
- const SHADOW_RASTER_ALPHA_COMPENSATION = 0.84;
20635
+ const SHADOW_RASTER_ALPHA_COMPENSATION = 1;
20569
20636
  function collectFontSpecsFromMarkup(markup) {
20570
20637
  const specs = /* @__PURE__ */ new Set();
20571
20638
  const re = /<(?:text|tspan)\b[^>]*>/gi;
@@ -21652,6 +21719,9 @@ async function rasterizeShadowMarkers(svg) {
21652
21719
  const by = parseFloat(marker.getAttribute("data-by") || "0");
21653
21720
  const bw = parseFloat(marker.getAttribute("data-bw") || "0");
21654
21721
  const bh = parseFloat(marker.getAttribute("data-bh") || "0");
21722
+ const spread = parseFloat(marker.getAttribute("data-spread") || "0");
21723
+ const alphaRaw = parseFloat(marker.getAttribute("data-alpha") || "1");
21724
+ const shadowAlpha = Number.isFinite(alphaRaw) ? Math.max(0, Math.min(1, alphaRaw)) : 1;
21655
21725
  if (!Number.isFinite(bw) || !Number.isFinite(bh) || bw <= 0 || bh <= 0) {
21656
21726
  (_b = marker.parentNode) == null ? void 0 : _b.removeChild(marker);
21657
21727
  continue;
@@ -21675,7 +21745,8 @@ async function rasterizeShadowMarkers(svg) {
21675
21745
  const pxH = Math.min(4096, Math.max(8, Math.ceil(bh * scale)));
21676
21746
  const stdDev = Math.max(0, blur / 2);
21677
21747
  const filterId = `pdShadowBlur_${Math.random().toString(36).slice(2, 9)}`;
21678
- const styleBlock = fontFaceCss ? `<style>${fontFaceCss}</style>` : "";
21748
+ const spreadCss = spread > 0 ? `text,tspan,path{paint-order:stroke fill;stroke:currentColor;stroke-width:${spread.toFixed(3)}px;stroke-linejoin:round;stroke-linecap:round;}` : "";
21749
+ const styleBlock = fontFaceCss || spreadCss ? `<style>${fontFaceCss}${spreadCss}</style>` : "";
21679
21750
  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>`;
21680
21751
  const dataUrl = await rasterSvgToPngDataUrl(miniSvg, pxW, pxH);
21681
21752
  if (!dataUrl) {
@@ -21690,8 +21761,6 @@ async function rasterizeShadowMarkers(svg) {
21690
21761
  img.setAttribute("preserveAspectRatio", "none");
21691
21762
  img.setAttributeNS(XLINK_NS, "xlink:href", dataUrl);
21692
21763
  img.setAttribute("href", dataUrl);
21693
- const alphaRaw = parseFloat(marker.getAttribute("data-alpha") || "1");
21694
- const shadowAlpha = Number.isFinite(alphaRaw) ? Math.max(0, Math.min(1, alphaRaw)) : 1;
21695
21764
  img.setAttribute("opacity", String(SHADOW_RASTER_ALPHA_COMPENSATION * shadowAlpha));
21696
21765
  (_e = marker.parentNode) == null ? void 0 : _e.replaceChild(img, marker);
21697
21766
  } catch (e) {
@@ -21898,7 +21967,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
21898
21967
  if (options == null ? void 0 : options.stripPageBackground) stripRootPageBackgroundFromSvg(svgToDraw);
21899
21968
  sanitizeSvgTreeForPdf(svgToDraw);
21900
21969
  try {
21901
- const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await import("./vectorPdfExport-CjIUU8on.js");
21970
+ const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await import("./vectorPdfExport-DZD415Qf.js");
21902
21971
  try {
21903
21972
  await logTextMeasurementDiagnostic(svgToDraw);
21904
21973
  } catch {
@@ -22298,4 +22367,4 @@ export {
22298
22367
  buildTeaserBlurFlatKeys as y,
22299
22368
  collectFontDescriptorsFromConfig as z
22300
22369
  };
22301
- //# sourceMappingURL=index-CDdfs3IB.js.map
22370
+ //# sourceMappingURL=index-Bpa0-8Hq.js.map