@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.js CHANGED
@@ -11844,6 +11844,89 @@ 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 getDecorationPadding(obj) {
11866
+ var _a;
11867
+ const strokeWidth = Number(obj.strokeWidth || 0);
11868
+ const shadowBlur = Number(((_a = obj.shadow) == null ? void 0 : _a.blur) || 0);
11869
+ return Math.max(0, strokeWidth) + Math.max(0, shadowBlur * 0.25);
11870
+ }
11871
+ function getFabricCanvasFromContainer(container) {
11872
+ const registry2 = window.__fabricCanvasRegistry;
11873
+ if (registry2 instanceof Map) {
11874
+ for (const entry of registry2.values()) {
11875
+ const canvas = (entry == null ? void 0 : entry.canvas) || entry;
11876
+ if (!canvas || typeof canvas.toSVG !== "function") continue;
11877
+ const el = canvas.lowerCanvasEl || canvas.upperCanvasEl;
11878
+ if (el && container.contains(el)) return canvas;
11879
+ }
11880
+ }
11881
+ return null;
11882
+ }
11883
+ function stabilizeFabricTextObjects(fabricInstance) {
11884
+ var _a, _b, _c;
11885
+ if (!(fabricInstance == null ? void 0 : fabricInstance.getObjects)) return;
11886
+ clearFabricCharCache();
11887
+ clearMeasurementCache();
11888
+ const walk = (obj) => {
11889
+ var _a2, _b2, _c2, _d;
11890
+ if (!obj) return;
11891
+ const children = Array.isArray(obj._objects) ? obj._objects : Array.isArray(obj.objects) ? obj.objects : [];
11892
+ if (children.length) children.forEach(walk);
11893
+ const isTextObject = typeof obj.text === "string" && typeof obj.initDimensions === "function" && (TEXT_TYPES.has(obj.type) || obj.isEditing !== void 0);
11894
+ if (!isTextObject) return;
11895
+ const saved = { width: obj.width, scaleX: obj.scaleX, scaleY: obj.scaleY };
11896
+ const reset = () => {
11897
+ var _a3;
11898
+ (_a3 = obj._clearCache) == null ? void 0 : _a3.call(obj);
11899
+ obj.__charBounds = [];
11900
+ obj.__lineWidths = [];
11901
+ obj.__lineHeights = [];
11902
+ obj.__graphemeLines = [];
11903
+ obj._textLines = [];
11904
+ obj.textLines = [];
11905
+ obj._styleMap = null;
11906
+ obj.styleMap = null;
11907
+ obj.dirty = true;
11908
+ };
11909
+ reset();
11910
+ obj.initDimensions();
11911
+ if (saved.width != null) {
11912
+ (_a2 = obj.set) == null ? void 0 : _a2.call(obj, { width: saved.width, scaleX: saved.scaleX, scaleY: saved.scaleY });
11913
+ reset();
11914
+ obj.initDimensions();
11915
+ (_b2 = obj.set) == null ? void 0 : _b2.call(obj, { width: saved.width, scaleX: saved.scaleX, scaleY: saved.scaleY });
11916
+ }
11917
+ if (obj.underline || obj.linethrough) {
11918
+ const decorationPadding = getDecorationPadding(obj);
11919
+ obj.__lineWidths = getTextLines(obj).map((line) => Math.max(0, measureLineWidth(obj, line) + decorationPadding));
11920
+ }
11921
+ obj.dirty = true;
11922
+ (_c2 = obj._clearCache) == null ? void 0 : _c2.call(obj);
11923
+ (_d = obj.setCoords) == null ? void 0 : _d.call(obj);
11924
+ };
11925
+ fabricInstance.getObjects().forEach(walk);
11926
+ (_a = fabricInstance.calcOffset) == null ? void 0 : _a.call(fabricInstance);
11927
+ (_b = fabricInstance.renderAll) == null ? void 0 : _b.call(fabricInstance);
11928
+ (_c = fabricInstance.requestRenderAll) == null ? void 0 : _c.call(fabricInstance);
11929
+ }
11847
11930
  function PixldocsPreview(props) {
11848
11931
  const {
11849
11932
  pageIndex = 0,
@@ -11940,6 +12023,13 @@ function PixldocsPreview(props) {
11940
12023
  () => `${pageIndex}-${fontsReadyVersion}-${stabilizationPass}`,
11941
12024
  [pageIndex, fontsReadyVersion, stabilizationPass]
11942
12025
  );
12026
+ const previewWrapRef = useCallback((node) => {
12027
+ if (!node || !config) return;
12028
+ requestAnimationFrame(() => {
12029
+ const fabricCanvas = getFabricCanvasFromContainer(node);
12030
+ if (fabricCanvas) stabilizeFabricTextObjects(fabricCanvas);
12031
+ });
12032
+ }, [config, previewKey]);
11943
12033
  useEffect(() => {
11944
12034
  if (isResolveMode) return;
11945
12035
  if (!config) {
@@ -11970,7 +12060,7 @@ function PixldocsPreview(props) {
11970
12060
  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
12061
  }
11972
12062
  return /* @__PURE__ */ jsxs("div", { className, style: { ...style, position: "relative" }, children: [
11973
- /* @__PURE__ */ jsx("div", { style: { visibility: canvasSettled ? "visible" : "hidden" }, children: /* @__PURE__ */ jsx(
12063
+ /* @__PURE__ */ jsx("div", { ref: previewWrapRef, style: { visibility: canvasSettled ? "visible" : "hidden" }, children: /* @__PURE__ */ jsx(
11974
12064
  PreviewCanvas,
11975
12065
  {
11976
12066
  config,
@@ -12537,7 +12627,6 @@ class PixldocsRenderer {
12537
12627
  pageIndex,
12538
12628
  zoom: pixelRatio,
12539
12629
  absoluteZoom: true,
12540
- skipFontReadyWait: true,
12541
12630
  onReady
12542
12631
  })
12543
12632
  );
@@ -12641,7 +12730,6 @@ class PixldocsRenderer {
12641
12730
  zoom: 1,
12642
12731
  // 1:1 — no UI scaling for SVG capture
12643
12732
  absoluteZoom: true,
12644
- skipFontReadyWait: true,
12645
12733
  onReady
12646
12734
  })
12647
12735
  );
@@ -12702,16 +12790,7 @@ class PixldocsRenderer {
12702
12790
  * using the global __fabricCanvasRegistry (set by PageCanvas).
12703
12791
  */
12704
12792
  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;
12793
+ return getFabricCanvasFromContainer(container);
12715
12794
  }
12716
12795
  async waitForStableTextMetrics(container, config) {
12717
12796
  if (typeof document !== "undefined") {
@@ -12725,59 +12804,7 @@ class PixldocsRenderer {
12725
12804
  clearFabricCharCache();
12726
12805
  clearMeasurementCache();
12727
12806
  };
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
- };
12807
+ const reflowTextboxes = () => stabilizeFabricTextObjects(fabricInstance);
12781
12808
  clearTextMetricCaches();
12782
12809
  await waitForPaint();
12783
12810
  reflowTextboxes();
@@ -14533,6 +14560,12 @@ function convertTextDecorationsToLines(svg) {
14533
14560
  } else {
14534
14561
  textWidth = content.length * fontSize * 0.6;
14535
14562
  }
14563
+ if (typeof tspan.getComputedTextLength === "function") {
14564
+ try {
14565
+ textWidth = Math.max(textWidth, tspan.getComputedTextLength());
14566
+ } catch {
14567
+ }
14568
+ }
14536
14569
  const underlineY = y + fontSize * 0.15;
14537
14570
  const thickness = Math.max(0.5, fontSize * 0.066667);
14538
14571
  const line = doc.createElementNS("http://www.w3.org/2000/svg", "line");