@pixldocs/canvas-renderer 0.5.19 → 0.5.20

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,83 @@ 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 getFabricCanvasFromContainer(container) {
11885
+ const registry2 = window.__fabricCanvasRegistry;
11886
+ if (registry2 instanceof Map) {
11887
+ for (const entry of registry2.values()) {
11888
+ const canvas = (entry == null ? void 0 : entry.canvas) || entry;
11889
+ if (!canvas || typeof canvas.toSVG !== "function") continue;
11890
+ const el = canvas.lowerCanvasEl || canvas.upperCanvasEl;
11891
+ if (el && container.contains(el)) return canvas;
11892
+ }
11893
+ }
11894
+ return null;
11895
+ }
11896
+ function stabilizeFabricTextObjects(fabricInstance) {
11897
+ var _a, _b, _c;
11898
+ if (!(fabricInstance == null ? void 0 : fabricInstance.getObjects)) return;
11899
+ clearFabricCharCache();
11900
+ clearMeasurementCache();
11901
+ const walk = (obj) => {
11902
+ var _a2, _b2, _c2, _d;
11903
+ if (!obj) return;
11904
+ const children = Array.isArray(obj._objects) ? obj._objects : Array.isArray(obj.objects) ? obj.objects : [];
11905
+ if (children.length) children.forEach(walk);
11906
+ const isTextObject = typeof obj.text === "string" && typeof obj.initDimensions === "function" && (TEXT_TYPES.has(obj.type) || obj.isEditing !== void 0);
11907
+ if (!isTextObject) return;
11908
+ const saved = { width: obj.width, scaleX: obj.scaleX, scaleY: obj.scaleY };
11909
+ const reset = () => {
11910
+ var _a3;
11911
+ (_a3 = obj._clearCache) == null ? void 0 : _a3.call(obj);
11912
+ obj.__charBounds = [];
11913
+ obj.__lineWidths = [];
11914
+ obj.__lineHeights = [];
11915
+ obj.__graphemeLines = [];
11916
+ obj._textLines = [];
11917
+ obj.textLines = [];
11918
+ obj._styleMap = null;
11919
+ obj.styleMap = null;
11920
+ obj.dirty = true;
11921
+ };
11922
+ reset();
11923
+ obj.initDimensions();
11924
+ if (saved.width != null) {
11925
+ (_a2 = obj.set) == null ? void 0 : _a2.call(obj, { width: saved.width, scaleX: saved.scaleX, scaleY: saved.scaleY });
11926
+ reset();
11927
+ obj.initDimensions();
11928
+ (_b2 = obj.set) == null ? void 0 : _b2.call(obj, { width: saved.width, scaleX: saved.scaleX, scaleY: saved.scaleY });
11929
+ }
11930
+ if (obj.underline || obj.linethrough) {
11931
+ const lineWidths = Array.isArray(obj.__lineWidths) ? obj.__lineWidths : [];
11932
+ obj.__lineWidths = getTextLines(obj).map((line, index) => Math.max(lineWidths[index] || 0, measureLineWidth(obj, line)));
11933
+ }
11934
+ obj.dirty = true;
11935
+ (_c2 = obj._clearCache) == null ? void 0 : _c2.call(obj);
11936
+ (_d = obj.setCoords) == null ? void 0 : _d.call(obj);
11937
+ };
11938
+ fabricInstance.getObjects().forEach(walk);
11939
+ (_a = fabricInstance.calcOffset) == null ? void 0 : _a.call(fabricInstance);
11940
+ (_b = fabricInstance.renderAll) == null ? void 0 : _b.call(fabricInstance);
11941
+ (_c = fabricInstance.requestRenderAll) == null ? void 0 : _c.call(fabricInstance);
11942
+ }
11866
11943
  function PixldocsPreview(props) {
11867
11944
  const {
11868
11945
  pageIndex = 0,
@@ -11959,6 +12036,13 @@ function PixldocsPreview(props) {
11959
12036
  () => `${pageIndex}-${fontsReadyVersion}-${stabilizationPass}`,
11960
12037
  [pageIndex, fontsReadyVersion, stabilizationPass]
11961
12038
  );
12039
+ const previewWrapRef = react.useCallback((node) => {
12040
+ if (!node || !config) return;
12041
+ requestAnimationFrame(() => {
12042
+ const fabricCanvas = getFabricCanvasFromContainer(node);
12043
+ if (fabricCanvas) stabilizeFabricTextObjects(fabricCanvas);
12044
+ });
12045
+ }, [config, previewKey]);
11962
12046
  react.useEffect(() => {
11963
12047
  if (isResolveMode) return;
11964
12048
  if (!config) {
@@ -11989,7 +12073,7 @@ function PixldocsPreview(props) {
11989
12073
  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
12074
  }
11991
12075
  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(
12076
+ /* @__PURE__ */ jsxRuntime.jsx("div", { ref: previewWrapRef, style: { visibility: canvasSettled ? "visible" : "hidden" }, children: /* @__PURE__ */ jsxRuntime.jsx(
11993
12077
  PreviewCanvas,
11994
12078
  {
11995
12079
  config,
@@ -12721,16 +12805,7 @@ class PixldocsRenderer {
12721
12805
  * using the global __fabricCanvasRegistry (set by PageCanvas).
12722
12806
  */
12723
12807
  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;
12808
+ return getFabricCanvasFromContainer(container);
12734
12809
  }
12735
12810
  async waitForStableTextMetrics(container, config) {
12736
12811
  if (typeof document !== "undefined") {
@@ -12744,59 +12819,7 @@ class PixldocsRenderer {
12744
12819
  clearFabricCharCache();
12745
12820
  clearMeasurementCache();
12746
12821
  };
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
- };
12822
+ const reflowTextboxes = () => stabilizeFabricTextObjects(fabricInstance);
12800
12823
  clearTextMetricCaches();
12801
12824
  await waitForPaint();
12802
12825
  reflowTextboxes();
@@ -14552,6 +14575,12 @@ function convertTextDecorationsToLines(svg) {
14552
14575
  } else {
14553
14576
  textWidth = content.length * fontSize * 0.6;
14554
14577
  }
14578
+ if (typeof tspan.getComputedTextLength === "function") {
14579
+ try {
14580
+ textWidth = Math.max(textWidth, tspan.getComputedTextLength());
14581
+ } catch {
14582
+ }
14583
+ }
14555
14584
  const underlineY = y + fontSize * 0.15;
14556
14585
  const thickness = Math.max(0.5, fontSize * 0.066667);
14557
14586
  const line = doc.createElementNS("http://www.w3.org/2000/svg", "line");