@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.js CHANGED
@@ -11844,6 +11844,83 @@ async function ensureFontsForResolvedConfig(config) {
11844
11844
  });
11845
11845
  }
11846
11846
  }
11847
+ const TEXT_TYPES = /* @__PURE__ */ new Set(["textbox", "text", "i-text"]);
11848
+ function getTextLines(obj) {
11849
+ const lines = Array.isArray(obj.textLines) && obj.textLines.length ? obj.textLines : String(obj.text || "").split("\n");
11850
+ return lines.map((line) => Array.isArray(line) ? line.join("") : String(line ?? ""));
11851
+ }
11852
+ function measureLineWidth(obj, line) {
11853
+ if (typeof document === "undefined") return 0;
11854
+ const canvas = document.createElement("canvas");
11855
+ const ctx = canvas.getContext("2d");
11856
+ if (!ctx) return 0;
11857
+ const fontStyle = obj.fontStyle || "normal";
11858
+ const fontWeight = obj.fontWeight || 400;
11859
+ const fontSize = obj.fontSize || 16;
11860
+ const fontFamily = obj.fontFamily || "sans-serif";
11861
+ ctx.font = `${fontStyle} ${fontWeight} ${fontSize}px ${fontFamily}`;
11862
+ const charSpacing = Number(obj.charSpacing || 0) / 1e3 * fontSize;
11863
+ return ctx.measureText(line).width + Math.max(0, line.length - 1) * charSpacing;
11864
+ }
11865
+ function getFabricCanvasFromContainer(container) {
11866
+ const registry2 = window.__fabricCanvasRegistry;
11867
+ if (registry2 instanceof Map) {
11868
+ for (const entry of registry2.values()) {
11869
+ const canvas = (entry == null ? void 0 : entry.canvas) || entry;
11870
+ if (!canvas || typeof canvas.toSVG !== "function") continue;
11871
+ const el = canvas.lowerCanvasEl || canvas.upperCanvasEl;
11872
+ if (el && container.contains(el)) return canvas;
11873
+ }
11874
+ }
11875
+ return null;
11876
+ }
11877
+ function stabilizeFabricTextObjects(fabricInstance) {
11878
+ var _a, _b, _c;
11879
+ if (!(fabricInstance == null ? void 0 : fabricInstance.getObjects)) return;
11880
+ clearFabricCharCache();
11881
+ clearMeasurementCache();
11882
+ const walk = (obj) => {
11883
+ var _a2, _b2, _c2, _d;
11884
+ if (!obj) return;
11885
+ const children = Array.isArray(obj._objects) ? obj._objects : Array.isArray(obj.objects) ? obj.objects : [];
11886
+ if (children.length) children.forEach(walk);
11887
+ const isTextObject = typeof obj.text === "string" && typeof obj.initDimensions === "function" && (TEXT_TYPES.has(obj.type) || obj.isEditing !== void 0);
11888
+ if (!isTextObject) return;
11889
+ const saved = { width: obj.width, scaleX: obj.scaleX, scaleY: obj.scaleY };
11890
+ const reset = () => {
11891
+ var _a3;
11892
+ (_a3 = obj._clearCache) == null ? void 0 : _a3.call(obj);
11893
+ obj.__charBounds = [];
11894
+ obj.__lineWidths = [];
11895
+ obj.__lineHeights = [];
11896
+ obj.__graphemeLines = [];
11897
+ obj._textLines = [];
11898
+ obj.textLines = [];
11899
+ obj._styleMap = null;
11900
+ obj.styleMap = null;
11901
+ obj.dirty = true;
11902
+ };
11903
+ reset();
11904
+ obj.initDimensions();
11905
+ if (saved.width != null) {
11906
+ (_a2 = obj.set) == null ? void 0 : _a2.call(obj, { width: saved.width, scaleX: saved.scaleX, scaleY: saved.scaleY });
11907
+ reset();
11908
+ obj.initDimensions();
11909
+ (_b2 = obj.set) == null ? void 0 : _b2.call(obj, { width: saved.width, scaleX: saved.scaleX, scaleY: saved.scaleY });
11910
+ }
11911
+ if (obj.underline || obj.linethrough) {
11912
+ const lineWidths = Array.isArray(obj.__lineWidths) ? obj.__lineWidths : [];
11913
+ obj.__lineWidths = getTextLines(obj).map((line, index) => Math.max(lineWidths[index] || 0, measureLineWidth(obj, line)));
11914
+ }
11915
+ obj.dirty = true;
11916
+ (_c2 = obj._clearCache) == null ? void 0 : _c2.call(obj);
11917
+ (_d = obj.setCoords) == null ? void 0 : _d.call(obj);
11918
+ };
11919
+ fabricInstance.getObjects().forEach(walk);
11920
+ (_a = fabricInstance.calcOffset) == null ? void 0 : _a.call(fabricInstance);
11921
+ (_b = fabricInstance.renderAll) == null ? void 0 : _b.call(fabricInstance);
11922
+ (_c = fabricInstance.requestRenderAll) == null ? void 0 : _c.call(fabricInstance);
11923
+ }
11847
11924
  function PixldocsPreview(props) {
11848
11925
  const {
11849
11926
  pageIndex = 0,
@@ -11940,6 +12017,13 @@ function PixldocsPreview(props) {
11940
12017
  () => `${pageIndex}-${fontsReadyVersion}-${stabilizationPass}`,
11941
12018
  [pageIndex, fontsReadyVersion, stabilizationPass]
11942
12019
  );
12020
+ const previewWrapRef = useCallback((node) => {
12021
+ if (!node || !config) return;
12022
+ requestAnimationFrame(() => {
12023
+ const fabricCanvas = getFabricCanvasFromContainer(node);
12024
+ if (fabricCanvas) stabilizeFabricTextObjects(fabricCanvas);
12025
+ });
12026
+ }, [config, previewKey]);
11943
12027
  useEffect(() => {
11944
12028
  if (isResolveMode) return;
11945
12029
  if (!config) {
@@ -11970,7 +12054,7 @@ function PixldocsPreview(props) {
11970
12054
  return /* @__PURE__ */ jsx("div", { className, style: { ...style, display: "flex", alignItems: "center", justifyContent: "center", minHeight: 200 }, children: /* @__PURE__ */ jsx("div", { style: { color: "#888", fontSize: 14 }, children: "Loading preview..." }) });
11971
12055
  }
11972
12056
  return /* @__PURE__ */ jsxs("div", { className, style: { ...style, position: "relative" }, children: [
11973
- /* @__PURE__ */ jsx("div", { style: { visibility: canvasSettled ? "visible" : "hidden" }, children: /* @__PURE__ */ jsx(
12057
+ /* @__PURE__ */ jsx("div", { ref: previewWrapRef, style: { visibility: canvasSettled ? "visible" : "hidden" }, children: /* @__PURE__ */ jsx(
11974
12058
  PreviewCanvas,
11975
12059
  {
11976
12060
  config,
@@ -12702,16 +12786,7 @@ class PixldocsRenderer {
12702
12786
  * using the global __fabricCanvasRegistry (set by PageCanvas).
12703
12787
  */
12704
12788
  getFabricCanvasFromContainer(container) {
12705
- const registry2 = window.__fabricCanvasRegistry;
12706
- if (registry2 instanceof Map) {
12707
- for (const entry of registry2.values()) {
12708
- const canvas = (entry == null ? void 0 : entry.canvas) || entry;
12709
- if (!canvas || typeof canvas.toSVG !== "function") continue;
12710
- const el = canvas.lowerCanvasEl || canvas.upperCanvasEl;
12711
- if (el && container.contains(el)) return canvas;
12712
- }
12713
- }
12714
- return null;
12789
+ return getFabricCanvasFromContainer(container);
12715
12790
  }
12716
12791
  async waitForStableTextMetrics(container, config) {
12717
12792
  if (typeof document !== "undefined") {
@@ -12725,59 +12800,7 @@ class PixldocsRenderer {
12725
12800
  clearFabricCharCache();
12726
12801
  clearMeasurementCache();
12727
12802
  };
12728
- const reflowTextboxes = () => {
12729
- var _a, _b, _c;
12730
- const walk = (obj) => {
12731
- var _a2, _b2, _c2, _d;
12732
- if (!obj) return;
12733
- const children = Array.isArray(obj._objects) ? obj._objects : Array.isArray(obj.objects) ? obj.objects : [];
12734
- if (children.length) children.forEach(walk);
12735
- const isTextObject = typeof obj.text === "string" && typeof obj.initDimensions === "function" && (obj.type === "textbox" || obj.type === "text" || obj.type === "i-text" || obj.isEditing !== void 0);
12736
- if (isTextObject) {
12737
- const saved = {
12738
- width: obj.width,
12739
- scaleX: obj.scaleX,
12740
- scaleY: obj.scaleY
12741
- };
12742
- const resetTextboxLayoutInternals = () => {
12743
- var _a3;
12744
- (_a3 = obj._clearCache) == null ? void 0 : _a3.call(obj);
12745
- obj.__charBounds = [];
12746
- obj.__lineWidths = [];
12747
- obj.__lineHeights = [];
12748
- obj.__graphemeLines = [];
12749
- obj._textLines = [];
12750
- obj.textLines = [];
12751
- obj._styleMap = null;
12752
- obj.styleMap = null;
12753
- obj.dirty = true;
12754
- };
12755
- resetTextboxLayoutInternals();
12756
- obj.initDimensions();
12757
- if (saved.width != null) {
12758
- (_a2 = obj.set) == null ? void 0 : _a2.call(obj, {
12759
- width: saved.width,
12760
- scaleX: saved.scaleX,
12761
- scaleY: saved.scaleY
12762
- });
12763
- resetTextboxLayoutInternals();
12764
- obj.initDimensions();
12765
- }
12766
- (_b2 = obj.set) == null ? void 0 : _b2.call(obj, {
12767
- width: saved.width,
12768
- scaleX: saved.scaleX,
12769
- scaleY: saved.scaleY,
12770
- dirty: true
12771
- });
12772
- (_c2 = obj._clearCache) == null ? void 0 : _c2.call(obj);
12773
- (_d = obj.setCoords) == null ? void 0 : _d.call(obj);
12774
- }
12775
- };
12776
- fabricInstance.getObjects().forEach(walk);
12777
- (_a = fabricInstance.calcOffset) == null ? void 0 : _a.call(fabricInstance);
12778
- (_b = fabricInstance.renderAll) == null ? void 0 : _b.call(fabricInstance);
12779
- (_c = fabricInstance.requestRenderAll) == null ? void 0 : _c.call(fabricInstance);
12780
- };
12803
+ const reflowTextboxes = () => stabilizeFabricTextObjects(fabricInstance);
12781
12804
  clearTextMetricCaches();
12782
12805
  await waitForPaint();
12783
12806
  reflowTextboxes();
@@ -14533,6 +14556,12 @@ function convertTextDecorationsToLines(svg) {
14533
14556
  } else {
14534
14557
  textWidth = content.length * fontSize * 0.6;
14535
14558
  }
14559
+ if (typeof tspan.getComputedTextLength === "function") {
14560
+ try {
14561
+ textWidth = Math.max(textWidth, tspan.getComputedTextLength());
14562
+ } catch {
14563
+ }
14564
+ }
14536
14565
  const underlineY = y + fontSize * 0.15;
14537
14566
  const thickness = Math.max(0.5, fontSize * 0.066667);
14538
14567
  const line = doc.createElementNS("http://www.w3.org/2000/svg", "line");