@pixldocs/canvas-renderer 0.5.42 → 0.5.44

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.d.ts CHANGED
@@ -221,7 +221,7 @@ export declare function normalizeFontFamily(fontStack: string): string;
221
221
  * Package version banner. Bump alongside package.json so we can confirm
222
222
  * (via browser:log) that the deployed bundle matches the expected build.
223
223
  */
224
- export declare const PACKAGE_VERSION = "0.5.42";
224
+ export declare const PACKAGE_VERSION = "0.5.44";
225
225
 
226
226
  export declare interface PageSettings {
227
227
  backgroundColor?: string;
package/dist/index.js CHANGED
@@ -4785,9 +4785,17 @@ function calculateScaleSnapGuides(scalingObj, corner, canvas, canvasWidth, canva
4785
4785
  const PD_BG_KEY = "__pdBg";
4786
4786
  const PATCHED_KEY = "__pdBgPatched";
4787
4787
  function extractTextBgConfig(element) {
4788
+ const legacy = Math.max(0, Number(element.textBgPadding ?? 0)) || 0;
4789
+ const pick = (v) => {
4790
+ const n = Number(v);
4791
+ return Number.isFinite(n) && n >= 0 ? n : void 0;
4792
+ };
4788
4793
  return {
4789
4794
  color: element.textBgColor,
4790
- padding: Math.max(0, Number(element.textBgPadding ?? 0)) || 0,
4795
+ padTop: pick(element.textBgPaddingTop) ?? legacy,
4796
+ padRight: pick(element.textBgPaddingRight) ?? legacy,
4797
+ padBottom: pick(element.textBgPaddingBottom) ?? legacy,
4798
+ padLeft: pick(element.textBgPaddingLeft) ?? legacy,
4791
4799
  rxTL: Math.max(0, Number(element.textBgRxTL ?? 0)) || 0,
4792
4800
  rxTR: Math.max(0, Number(element.textBgRxTR ?? 0)) || 0,
4793
4801
  rxBR: Math.max(0, Number(element.textBgRxBR ?? 0)) || 0,
@@ -4805,9 +4813,8 @@ function buildTextShadow(element) {
4805
4813
  const ox = Number(element.textShadowOffsetX ?? 0);
4806
4814
  const oy = Number(element.textShadowOffsetY ?? 0);
4807
4815
  if (!color || color === "transparent") return null;
4808
- if (blur === 0 && ox === 0 && oy === 0) return null;
4809
4816
  return new fabric.Shadow({
4810
- color,
4817
+ color: String(color),
4811
4818
  blur: blur || 0,
4812
4819
  offsetX: ox || 0,
4813
4820
  offsetY: oy || 0,
@@ -4844,11 +4851,14 @@ function applyTextBackground(obj, cfg) {
4844
4851
  if (hasTextBackground(bg)) {
4845
4852
  const w = this.width ?? 0;
4846
4853
  const h = this.height ?? 0;
4847
- const pad = Math.max(0, Number(bg.padding ?? 0));
4848
- const x = -w / 2 - pad;
4849
- const y = -h / 2 - pad;
4850
- const bgW = w + pad * 2;
4851
- const bgH = h + pad * 2;
4854
+ const pT = Math.max(0, Number(bg.padTop ?? 0));
4855
+ const pR = Math.max(0, Number(bg.padRight ?? 0));
4856
+ const pB = Math.max(0, Number(bg.padBottom ?? 0));
4857
+ const pL = Math.max(0, Number(bg.padLeft ?? 0));
4858
+ const x = -w / 2 - pL;
4859
+ const y = -h / 2 - pT;
4860
+ const bgW = w + pL + pR;
4861
+ const bgH = h + pT + pB;
4852
4862
  ctx.save();
4853
4863
  buildRoundedRectPath2D(
4854
4864
  ctx,
@@ -4879,37 +4889,107 @@ function applyTextBackground(obj, cfg) {
4879
4889
  const originalToSVG = (_a = obj.toSVG) == null ? void 0 : _a.bind(obj);
4880
4890
  if (typeof originalToSVG === "function") {
4881
4891
  obj.toSVG = function(reviver) {
4882
- const svg = originalToSVG(reviver);
4892
+ let svg = originalToSVG(reviver);
4883
4893
  const bg = this[PD_BG_KEY];
4884
- if (!hasTextBackground(bg)) return svg;
4894
+ const shadow = this.shadow;
4895
+ const hasBg = hasTextBackground(bg);
4896
+ const hasShadow = !!shadow && !!shadow.color && shadow.color !== "transparent";
4897
+ if (!hasBg && !hasShadow) return svg;
4885
4898
  const w = this.width ?? 0;
4886
4899
  const h = this.height ?? 0;
4887
- const pad = Math.max(0, Number(bg.padding ?? 0));
4888
- const x = -w / 2 - pad;
4889
- const y = -h / 2 - pad;
4890
- const bgW = w + pad * 2;
4891
- const bgH = h + pad * 2;
4892
- const d = buildRoundedRectPathD(
4900
+ const pT = Math.max(0, Number((bg == null ? void 0 : bg.padTop) ?? 0));
4901
+ const pR = Math.max(0, Number((bg == null ? void 0 : bg.padRight) ?? 0));
4902
+ const pB = Math.max(0, Number((bg == null ? void 0 : bg.padBottom) ?? 0));
4903
+ const pL = Math.max(0, Number((bg == null ? void 0 : bg.padLeft) ?? 0));
4904
+ const x = -w / 2 - pL;
4905
+ const y = -h / 2 - pT;
4906
+ const bgW = w + pL + pR;
4907
+ const bgH = h + pT + pB;
4908
+ const bgD = buildRoundedRectPathD(
4893
4909
  x,
4894
4910
  y,
4895
4911
  bgW,
4896
4912
  bgH,
4897
- bg.rxTL ?? 0,
4898
- bg.rxTR ?? 0,
4899
- bg.rxBR ?? 0,
4900
- bg.rxBL ?? 0
4913
+ (bg == null ? void 0 : bg.rxTL) ?? 0,
4914
+ (bg == null ? void 0 : bg.rxTR) ?? 0,
4915
+ (bg == null ? void 0 : bg.rxBR) ?? 0,
4916
+ (bg == null ? void 0 : bg.rxBL) ?? 0
4901
4917
  );
4902
- const fill = bg.color;
4903
- const bgPath = `<path d="${d}" fill="${escapeXmlAttr(fill)}" />`;
4918
+ const bgFill = (bg == null ? void 0 : bg.color) || "";
4919
+ const bgPath = hasBg ? `<path d="${bgD}" fill="${escapeXmlAttr(bgFill)}" />` : "";
4920
+ svg = svg.replace(/style="[^"]*filter:\s*url\([^)]+\)[^"]*"/i, "");
4921
+ svg = svg.replace(/<filter[\s\S]*?<\/filter>/gi, "");
4922
+ let shadowLayer = "";
4923
+ if (hasShadow) {
4924
+ const ox = Number(shadow.offsetX ?? 0) || 0;
4925
+ const oy = Number(shadow.offsetY ?? 0) || 0;
4926
+ const blur = Math.max(0, Number(shadow.blur ?? 0));
4927
+ const shadowColor = String(shadow.color);
4928
+ const shadowBgPath = hasBg ? `<path d="${bgD}" fill="${escapeXmlAttr(shadowColor)}" />` : "";
4929
+ const inner = extractGInnerMarkup(svg);
4930
+ const recoloredText = recolorSvgFills(inner, shadowColor);
4931
+ const layers = [];
4932
+ if (blur > 0) {
4933
+ const ringCount = Math.min(6, Math.max(2, Math.round(blur / 2)));
4934
+ for (let i = 1; i <= ringCount; i++) {
4935
+ const t = i / ringCount;
4936
+ const dist = blur * t * 0.6;
4937
+ const op = (0.18 * (1 - t * 0.7)).toFixed(3);
4938
+ const ringOffsets = [
4939
+ [dist, 0],
4940
+ [-dist, 0],
4941
+ [0, dist],
4942
+ [0, -dist],
4943
+ [dist * 0.7, dist * 0.7],
4944
+ [-dist * 0.7, dist * 0.7],
4945
+ [dist * 0.7, -dist * 0.7],
4946
+ [-dist * 0.7, -dist * 0.7]
4947
+ ];
4948
+ for (const [dx, dy] of ringOffsets) {
4949
+ layers.push(
4950
+ `<g transform="translate(${(ox + dx).toFixed(3)} ${(oy + dy).toFixed(3)})" opacity="${op}">${shadowBgPath}${recoloredText}</g>`
4951
+ );
4952
+ }
4953
+ }
4954
+ layers.push(
4955
+ `<g transform="translate(${ox.toFixed(3)} ${oy.toFixed(3)})" opacity="0.25">${shadowBgPath}${recoloredText}</g>`
4956
+ );
4957
+ } else {
4958
+ layers.push(
4959
+ `<g transform="translate(${ox.toFixed(3)} ${oy.toFixed(3)})">${shadowBgPath}${recoloredText}</g>`
4960
+ );
4961
+ }
4962
+ shadowLayer = layers.join("");
4963
+ }
4904
4964
  const openTagMatch = svg.match(/^\s*<g\b[^>]*>/);
4965
+ const inserted = shadowLayer + bgPath;
4905
4966
  if (openTagMatch) {
4906
4967
  const openTag = openTagMatch[0];
4907
- return svg.replace(openTag, openTag + bgPath);
4968
+ return svg.replace(openTag, openTag + inserted);
4908
4969
  }
4909
- return `<g>${bgPath}${svg}</g>`;
4970
+ return `<g>${inserted}${svg}</g>`;
4910
4971
  };
4911
4972
  }
4912
4973
  }
4974
+ function recolorSvgFills(svg, color) {
4975
+ const safe = escapeXmlAttr(color);
4976
+ let out = svg.replace(
4977
+ /(<(?:text|tspan|path|rect)\b[^>]*?\sfill=")([^"]*)("[^>]*>)/gi,
4978
+ (_m, pre, val, post) => {
4979
+ const v = val.trim().toLowerCase();
4980
+ if (v === "none" || v === "transparent") return pre + val + post;
4981
+ return pre + safe + post;
4982
+ }
4983
+ );
4984
+ out = out.replace(
4985
+ /(<(?:text|tspan|path|rect)\b[^>]*?\sstyle=")([^"]*)("[^>]*>)/gi,
4986
+ (_m, pre, styleVal, post) => {
4987
+ const replaced = styleVal.replace(/fill\s*:\s*[^;"]+/gi, `fill: ${color}`);
4988
+ return pre + replaced + post;
4989
+ }
4990
+ );
4991
+ return out;
4992
+ }
4913
4993
  function buildRoundedRectPathD(x, y, w, h, rTL, rTR, rBR, rBL) {
4914
4994
  const maxR = Math.min(w, h) / 2;
4915
4995
  const tl = Math.min(Math.max(0, rTL), maxR);
@@ -4933,6 +5013,13 @@ function buildRoundedRectPathD(x, y, w, h, rTL, rTR, rBR, rBL) {
4933
5013
  function escapeXmlAttr(s) {
4934
5014
  return String(s).replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
4935
5015
  }
5016
+ function extractGInnerMarkup(markup) {
5017
+ const openMatch = markup.match(/^\s*<g\b[^>]*>/);
5018
+ if (!openMatch) return markup;
5019
+ const closeIdx = markup.lastIndexOf("</g>");
5020
+ if (closeIdx <= openMatch[0].length) return markup;
5021
+ return markup.slice(openMatch[0].length, closeIdx);
5022
+ }
4936
5023
  const TRIANGLE_STROKE_MITER_LIMIT = 1e6;
4937
5024
  const toSafeNumber = (value, fallback = 0) => Number.isFinite(value) ? Math.max(0, Number(value)) : fallback;
4938
5025
  function normalizeShapeType(shapeType) {
@@ -7852,6 +7939,10 @@ const PageCanvas = forwardRef(
7852
7939
  JSON.stringify({
7853
7940
  c: element.textBgColor ?? null,
7854
7941
  p: element.textBgPadding ?? 0,
7942
+ pt: element.textBgPaddingTop ?? null,
7943
+ pr: element.textBgPaddingRight ?? null,
7944
+ pb: element.textBgPaddingBottom ?? null,
7945
+ pl: element.textBgPaddingLeft ?? null,
7855
7946
  tl: element.textBgRxTL ?? 0,
7856
7947
  tr: element.textBgRxTR ?? 0,
7857
7948
  br: element.textBgRxBR ?? 0,
@@ -8295,7 +8386,7 @@ const PageCanvas = forwardRef(
8295
8386
  });
8296
8387
  }, [selectedIds, isActive, ready, elements]);
8297
8388
  const updateFabricObject = (obj, element, skipPositionUpdate = false) => {
8298
- var _a, _b;
8389
+ var _a, _b, _c;
8299
8390
  const fc = fabricRef.current;
8300
8391
  if (fc && isTransforming(fc)) {
8301
8392
  return;
@@ -8759,9 +8850,20 @@ const PageCanvas = forwardRef(
8759
8850
  applyTextBackground(obj, extractTextBgConfig(element));
8760
8851
  const shadow = buildTextShadow(element);
8761
8852
  obj.set("shadow", shadow ?? null);
8853
+ try {
8854
+ obj._cacheCanvas = null;
8855
+ obj._cacheContext = null;
8856
+ } catch {
8857
+ }
8858
+ obj.dirty = true;
8859
+ (_c = obj.setCoords) == null ? void 0 : _c.call(obj);
8762
8860
  obj.__lastTextBgShadowJson = JSON.stringify({
8763
8861
  c: element.textBgColor ?? null,
8764
8862
  p: element.textBgPadding ?? 0,
8863
+ pt: element.textBgPaddingTop ?? null,
8864
+ pr: element.textBgPaddingRight ?? null,
8865
+ pb: element.textBgPaddingBottom ?? null,
8866
+ pl: element.textBgPaddingLeft ?? null,
8765
8867
  tl: element.textBgRxTL ?? 0,
8766
8868
  tr: element.textBgRxTR ?? 0,
8767
8869
  br: element.textBgRxBR ?? 0,
@@ -12239,7 +12341,7 @@ function PixldocsPreview(props) {
12239
12341
  !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..." }) })
12240
12342
  ] });
12241
12343
  }
12242
- const PACKAGE_VERSION = "0.5.42";
12344
+ const PACKAGE_VERSION = "0.5.44";
12243
12345
  let __underlineFixInstalled = false;
12244
12346
  function installUnderlineFix(fab) {
12245
12347
  var _a;