@pixldocs/canvas-renderer 0.5.19 → 0.5.21

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
@@ -11863,6 +11863,89 @@ async function ensureFontsForResolvedConfig(config) {
11863
11863
  });
11864
11864
  }
11865
11865
  }
11866
+ const TEXT_TYPES = /* @__PURE__ */ new Set(["textbox", "text", "i-text"]);
11867
+ function getTextLines(obj) {
11868
+ const lines = Array.isArray(obj.textLines) && obj.textLines.length ? obj.textLines : String(obj.text || "").split("\n");
11869
+ return lines.map((line) => Array.isArray(line) ? line.join("") : String(line ?? ""));
11870
+ }
11871
+ function measureLineWidth(obj, line) {
11872
+ if (typeof document === "undefined") return 0;
11873
+ const canvas = document.createElement("canvas");
11874
+ const ctx = canvas.getContext("2d");
11875
+ if (!ctx) return 0;
11876
+ const fontStyle = obj.fontStyle || "normal";
11877
+ const fontWeight = obj.fontWeight || 400;
11878
+ const fontSize = obj.fontSize || 16;
11879
+ const fontFamily = obj.fontFamily || "sans-serif";
11880
+ ctx.font = `${fontStyle} ${fontWeight} ${fontSize}px ${fontFamily}`;
11881
+ const charSpacing = Number(obj.charSpacing || 0) / 1e3 * fontSize;
11882
+ return ctx.measureText(line).width + Math.max(0, line.length - 1) * charSpacing;
11883
+ }
11884
+ function getDecorationPadding(obj) {
11885
+ var _a;
11886
+ const strokeWidth = Number(obj.strokeWidth || 0);
11887
+ const shadowBlur = Number(((_a = obj.shadow) == null ? void 0 : _a.blur) || 0);
11888
+ return Math.max(0, strokeWidth) + Math.max(0, shadowBlur * 0.25);
11889
+ }
11890
+ function getFabricCanvasFromContainer(container) {
11891
+ const registry2 = window.__fabricCanvasRegistry;
11892
+ if (registry2 instanceof Map) {
11893
+ for (const entry of registry2.values()) {
11894
+ const canvas = (entry == null ? void 0 : entry.canvas) || entry;
11895
+ if (!canvas || typeof canvas.toSVG !== "function") continue;
11896
+ const el = canvas.lowerCanvasEl || canvas.upperCanvasEl;
11897
+ if (el && container.contains(el)) return canvas;
11898
+ }
11899
+ }
11900
+ return null;
11901
+ }
11902
+ function stabilizeFabricTextObjects(fabricInstance) {
11903
+ var _a, _b, _c;
11904
+ if (!(fabricInstance == null ? void 0 : fabricInstance.getObjects)) return;
11905
+ clearFabricCharCache();
11906
+ clearMeasurementCache();
11907
+ const walk = (obj) => {
11908
+ var _a2, _b2, _c2, _d;
11909
+ if (!obj) return;
11910
+ const children = Array.isArray(obj._objects) ? obj._objects : Array.isArray(obj.objects) ? obj.objects : [];
11911
+ if (children.length) children.forEach(walk);
11912
+ const isTextObject = typeof obj.text === "string" && typeof obj.initDimensions === "function" && (TEXT_TYPES.has(obj.type) || obj.isEditing !== void 0);
11913
+ if (!isTextObject) return;
11914
+ const saved = { width: obj.width, scaleX: obj.scaleX, scaleY: obj.scaleY };
11915
+ const reset = () => {
11916
+ var _a3;
11917
+ (_a3 = obj._clearCache) == null ? void 0 : _a3.call(obj);
11918
+ obj.__charBounds = [];
11919
+ obj.__lineWidths = [];
11920
+ obj.__lineHeights = [];
11921
+ obj.__graphemeLines = [];
11922
+ obj._textLines = [];
11923
+ obj.textLines = [];
11924
+ obj._styleMap = null;
11925
+ obj.styleMap = null;
11926
+ obj.dirty = true;
11927
+ };
11928
+ reset();
11929
+ obj.initDimensions();
11930
+ if (saved.width != null) {
11931
+ (_a2 = obj.set) == null ? void 0 : _a2.call(obj, { width: saved.width, scaleX: saved.scaleX, scaleY: saved.scaleY });
11932
+ reset();
11933
+ obj.initDimensions();
11934
+ (_b2 = obj.set) == null ? void 0 : _b2.call(obj, { width: saved.width, scaleX: saved.scaleX, scaleY: saved.scaleY });
11935
+ }
11936
+ if (obj.underline || obj.linethrough) {
11937
+ const decorationPadding = getDecorationPadding(obj);
11938
+ obj.__lineWidths = getTextLines(obj).map((line) => Math.max(0, measureLineWidth(obj, line) + decorationPadding));
11939
+ }
11940
+ obj.dirty = true;
11941
+ (_c2 = obj._clearCache) == null ? void 0 : _c2.call(obj);
11942
+ (_d = obj.setCoords) == null ? void 0 : _d.call(obj);
11943
+ };
11944
+ fabricInstance.getObjects().forEach(walk);
11945
+ (_a = fabricInstance.calcOffset) == null ? void 0 : _a.call(fabricInstance);
11946
+ (_b = fabricInstance.renderAll) == null ? void 0 : _b.call(fabricInstance);
11947
+ (_c = fabricInstance.requestRenderAll) == null ? void 0 : _c.call(fabricInstance);
11948
+ }
11866
11949
  function PixldocsPreview(props) {
11867
11950
  const {
11868
11951
  pageIndex = 0,
@@ -11959,6 +12042,13 @@ function PixldocsPreview(props) {
11959
12042
  () => `${pageIndex}-${fontsReadyVersion}-${stabilizationPass}`,
11960
12043
  [pageIndex, fontsReadyVersion, stabilizationPass]
11961
12044
  );
12045
+ const previewWrapRef = react.useCallback((node) => {
12046
+ if (!node || !config) return;
12047
+ requestAnimationFrame(() => {
12048
+ const fabricCanvas = getFabricCanvasFromContainer(node);
12049
+ if (fabricCanvas) stabilizeFabricTextObjects(fabricCanvas);
12050
+ });
12051
+ }, [config, previewKey]);
11962
12052
  react.useEffect(() => {
11963
12053
  if (isResolveMode) return;
11964
12054
  if (!config) {
@@ -11989,7 +12079,7 @@ function PixldocsPreview(props) {
11989
12079
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className, style: { ...style, display: "flex", alignItems: "center", justifyContent: "center", minHeight: 200 }, children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: "#888", fontSize: 14 }, children: "Loading preview..." }) });
11990
12080
  }
11991
12081
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, style: { ...style, position: "relative" }, children: [
11992
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: { visibility: canvasSettled ? "visible" : "hidden" }, children: /* @__PURE__ */ jsxRuntime.jsx(
12082
+ /* @__PURE__ */ jsxRuntime.jsx("div", { ref: previewWrapRef, style: { visibility: canvasSettled ? "visible" : "hidden" }, children: /* @__PURE__ */ jsxRuntime.jsx(
11993
12083
  PreviewCanvas,
11994
12084
  {
11995
12085
  config,
@@ -12556,7 +12646,6 @@ class PixldocsRenderer {
12556
12646
  pageIndex,
12557
12647
  zoom: pixelRatio,
12558
12648
  absoluteZoom: true,
12559
- skipFontReadyWait: true,
12560
12649
  onReady
12561
12650
  })
12562
12651
  );
@@ -12660,7 +12749,6 @@ class PixldocsRenderer {
12660
12749
  zoom: 1,
12661
12750
  // 1:1 — no UI scaling for SVG capture
12662
12751
  absoluteZoom: true,
12663
- skipFontReadyWait: true,
12664
12752
  onReady
12665
12753
  })
12666
12754
  );
@@ -12721,16 +12809,7 @@ class PixldocsRenderer {
12721
12809
  * using the global __fabricCanvasRegistry (set by PageCanvas).
12722
12810
  */
12723
12811
  getFabricCanvasFromContainer(container) {
12724
- const registry2 = window.__fabricCanvasRegistry;
12725
- if (registry2 instanceof Map) {
12726
- for (const entry of registry2.values()) {
12727
- const canvas = (entry == null ? void 0 : entry.canvas) || entry;
12728
- if (!canvas || typeof canvas.toSVG !== "function") continue;
12729
- const el = canvas.lowerCanvasEl || canvas.upperCanvasEl;
12730
- if (el && container.contains(el)) return canvas;
12731
- }
12732
- }
12733
- return null;
12812
+ return getFabricCanvasFromContainer(container);
12734
12813
  }
12735
12814
  async waitForStableTextMetrics(container, config) {
12736
12815
  if (typeof document !== "undefined") {
@@ -12744,59 +12823,7 @@ class PixldocsRenderer {
12744
12823
  clearFabricCharCache();
12745
12824
  clearMeasurementCache();
12746
12825
  };
12747
- const reflowTextboxes = () => {
12748
- var _a, _b, _c;
12749
- const walk = (obj) => {
12750
- var _a2, _b2, _c2, _d;
12751
- if (!obj) return;
12752
- const children = Array.isArray(obj._objects) ? obj._objects : Array.isArray(obj.objects) ? obj.objects : [];
12753
- if (children.length) children.forEach(walk);
12754
- const isTextObject = typeof obj.text === "string" && typeof obj.initDimensions === "function" && (obj.type === "textbox" || obj.type === "text" || obj.type === "i-text" || obj.isEditing !== void 0);
12755
- if (isTextObject) {
12756
- const saved = {
12757
- width: obj.width,
12758
- scaleX: obj.scaleX,
12759
- scaleY: obj.scaleY
12760
- };
12761
- const resetTextboxLayoutInternals = () => {
12762
- var _a3;
12763
- (_a3 = obj._clearCache) == null ? void 0 : _a3.call(obj);
12764
- obj.__charBounds = [];
12765
- obj.__lineWidths = [];
12766
- obj.__lineHeights = [];
12767
- obj.__graphemeLines = [];
12768
- obj._textLines = [];
12769
- obj.textLines = [];
12770
- obj._styleMap = null;
12771
- obj.styleMap = null;
12772
- obj.dirty = true;
12773
- };
12774
- resetTextboxLayoutInternals();
12775
- obj.initDimensions();
12776
- if (saved.width != null) {
12777
- (_a2 = obj.set) == null ? void 0 : _a2.call(obj, {
12778
- width: saved.width,
12779
- scaleX: saved.scaleX,
12780
- scaleY: saved.scaleY
12781
- });
12782
- resetTextboxLayoutInternals();
12783
- obj.initDimensions();
12784
- }
12785
- (_b2 = obj.set) == null ? void 0 : _b2.call(obj, {
12786
- width: saved.width,
12787
- scaleX: saved.scaleX,
12788
- scaleY: saved.scaleY,
12789
- dirty: true
12790
- });
12791
- (_c2 = obj._clearCache) == null ? void 0 : _c2.call(obj);
12792
- (_d = obj.setCoords) == null ? void 0 : _d.call(obj);
12793
- }
12794
- };
12795
- fabricInstance.getObjects().forEach(walk);
12796
- (_a = fabricInstance.calcOffset) == null ? void 0 : _a.call(fabricInstance);
12797
- (_b = fabricInstance.renderAll) == null ? void 0 : _b.call(fabricInstance);
12798
- (_c = fabricInstance.requestRenderAll) == null ? void 0 : _c.call(fabricInstance);
12799
- };
12826
+ const reflowTextboxes = () => stabilizeFabricTextObjects(fabricInstance);
12800
12827
  clearTextMetricCaches();
12801
12828
  await waitForPaint();
12802
12829
  reflowTextboxes();
@@ -14552,6 +14579,12 @@ function convertTextDecorationsToLines(svg) {
14552
14579
  } else {
14553
14580
  textWidth = content.length * fontSize * 0.6;
14554
14581
  }
14582
+ if (typeof tspan.getComputedTextLength === "function") {
14583
+ try {
14584
+ textWidth = Math.max(textWidth, tspan.getComputedTextLength());
14585
+ } catch {
14586
+ }
14587
+ }
14555
14588
  const underlineY = y + fontSize * 0.15;
14556
14589
  const thickness = Math.max(0.5, fontSize * 0.066667);
14557
14590
  const line = doc.createElementNS("http://www.w3.org/2000/svg", "line");