@xrubik/fabric-to-leafer 1.0.0 → 1.0.1

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.mjs CHANGED
@@ -62,6 +62,12 @@ var ORIGIN_FRACTION = {
62
62
  top: 0,
63
63
  bottom: 1
64
64
  };
65
+ function originFraction(value, fallback = 0) {
66
+ if (value === void 0 || value === null || value === "") return fallback;
67
+ if (typeof value === "number") return Number.isFinite(value) ? value : fallback;
68
+ const named = ORIGIN_FRACTION[String(value).trim().toLowerCase()];
69
+ return named === void 0 ? fallback : named;
70
+ }
65
71
  function pathPlaceOrigin(object, bounds) {
66
72
  if (!bounds) {
67
73
  return { left: num(object.left), top: num(object.top) };
@@ -74,8 +80,8 @@ function pathPlaceOrigin(object, bounds) {
74
80
  const height = bounds.height;
75
81
  const scaleX = Math.abs(normalizeScale(num(object.scaleX, 1) || 1, width));
76
82
  const scaleY = Math.abs(normalizeScale(num(object.scaleY, 1) || 1, height));
77
- const fractionX = ORIGIN_FRACTION[object.originX === void 0 ? "left" : object.originX] || 0;
78
- const fractionY = ORIGIN_FRACTION[object.originY === void 0 ? "top" : object.originY] || 0;
83
+ const fractionX = originFraction(object.originX);
84
+ const fractionY = originFraction(object.originY);
79
85
  const angle = num(object.angle) * Math.PI / 180;
80
86
  const cos = Math.cos(angle);
81
87
  const sin = Math.sin(angle);
@@ -185,8 +191,13 @@ function convertPathData(pathData, object) {
185
191
  }).join(" ");
186
192
  }
187
193
  function aroundOf(object) {
188
- const originX = object.originX === void 0 ? "left" : object.originX;
189
- const originY = object.originY === void 0 ? "top" : object.originY;
194
+ const axisOf = (fraction, low, center, high) => {
195
+ if (fraction <= 0.25) return low;
196
+ if (fraction >= 0.75) return high;
197
+ return center;
198
+ };
199
+ const originX = axisOf(originFraction(object.originX), "left", "center", "right");
200
+ const originY = axisOf(originFraction(object.originY), "top", "center", "bottom");
190
201
  const map = {
191
202
  left: { top: "top-left", center: "left", bottom: "bottom-left" },
192
203
  center: { top: "top", center: "center", bottom: "bottom" },
@@ -309,13 +320,6 @@ function normalizeSizeSign(object) {
309
320
  }
310
321
 
311
322
  // src/attributes/transform.js
312
- var ORIGIN_FRACTION2 = {
313
- left: 0,
314
- center: 0.5,
315
- right: 1,
316
- top: 0,
317
- bottom: 1
318
- };
319
323
  var PATH_PLACED_TYPES = ["polyline", "polygon", "line"];
320
324
  function bakePointsScale(node2, object, scaleX, scaleY) {
321
325
  if (!PATH_PLACED_TYPES.includes(object.type)) return false;
@@ -391,9 +395,8 @@ function applyPosition(node2, object, scaleX, scaleY) {
391
395
  if (PATH_PLACED_TYPES.includes(object.type)) return;
392
396
  const width = num(object.width);
393
397
  const height = num(object.height);
394
- const fractionX = ORIGIN_FRACTION2[object.originX === void 0 ? "left" : object.originX];
395
- const fractionY = ORIGIN_FRACTION2[object.originY === void 0 ? "top" : object.originY];
396
- if (fractionX === void 0 || fractionY === void 0) return;
398
+ const fractionX = originFraction(object.originX);
399
+ const fractionY = originFraction(object.originY);
397
400
  const single = scaleX > 0 && scaleY > 0 && !num(node2.rotation, 0);
398
401
  if (single) return;
399
402
  const angle = num(node2.rotation, 0) * Math.PI / 180;
@@ -615,6 +618,7 @@ function applyPaint(node2, object, ctx) {
615
618
  if (strokeWidth !== void 0) {
616
619
  node2.strokeWidth = strokeWidth;
617
620
  }
621
+ node2.strokeScaleFixed = true;
618
622
  if (Array.isArray(object.strokeDashArray) && object.strokeDashArray.length) {
619
623
  node2.dashPattern = object.strokeDashArray;
620
624
  }
@@ -747,9 +751,13 @@ function cornerRadiusOf(object) {
747
751
  return [Math.max(0, rx || 0), Math.max(0, ry || 0)];
748
752
  }
749
753
  function convertRect(object, ctx, parent) {
754
+ const width = optionalNum(object.width);
755
+ const height = optionalNum(object.height);
750
756
  const rect = node("Rect", {
751
- width: optionalNum(object.width),
752
- height: optionalNum(object.height),
757
+ // Leafer 的 Rect 不给宽高时用默认的 100x100, Fabric 里缺宽高的图元
758
+ // (脏数据 / 没画完的空框)要显式写 0, 否则转出来凭空多出一个 100 的框
759
+ width: width === void 0 ? 0 : width,
760
+ height: height === void 0 ? 0 : height,
753
761
  fill: paintOf(object.fill, ctx),
754
762
  cornerRadius: cornerRadiusOf(object)
755
763
  });
@@ -759,9 +767,12 @@ function convertRect(object, ctx, parent) {
759
767
 
760
768
  // src/elements/ellipse.js
761
769
  function convertEllipse(object, ctx, parent) {
770
+ const width = optionalNum(object.width);
771
+ const height = optionalNum(object.height);
762
772
  const ellipse = node("Ellipse", {
763
- width: optionalNum(object.width),
764
- height: optionalNum(object.height),
773
+ // 同 Rect: 不写宽高会落到 Leafer 的 100x100 默认尺寸
774
+ width: width === void 0 ? 0 : width,
775
+ height: height === void 0 ? 0 : height,
765
776
  fill: paintOf(object.fill, ctx)
766
777
  });
767
778
  place(ellipse, object, parent);
@@ -790,8 +801,9 @@ function convertTriangle(object, ctx, parent) {
790
801
  const w = width || 0;
791
802
  const h = height || 0;
792
803
  const triangle = node("Polygon", {
793
- width,
794
- height,
804
+ // 不写宽高会落到 Leafer 的 100x100 默认尺寸
805
+ width: w,
806
+ height: h,
795
807
  // 顶点在正上方,底边贴底:和 Fabric 的三角形一致
796
808
  points: [w / 2, 0, w, h, 0, h],
797
809
  fill: paintOf(object.fill, ctx),
@@ -942,7 +954,6 @@ function textProps(object, ctx) {
942
954
  letterSpacing: convertLetterSpacing(object.charSpacing, object.fontSize),
943
955
  fill: object.fill ? paintOf(object.fill, ctx) : "#000000",
944
956
  shadow: object.shadow ? convertShadow(object.shadow) : void 0,
945
- verticalAlign: "middle",
946
957
  textDecoration: textDecorationOf(object)
947
958
  };
948
959
  const backgroundColor = object.backgroundColor || object.textBackgroundColor;
@@ -959,22 +970,64 @@ function textProps(object, ctx) {
959
970
  }
960
971
 
961
972
  // src/elements/text.js
962
- function estimateLineHeight(object) {
973
+ function textLines(object) {
974
+ const content = object.text === void 0 ? "" : String(object.text);
975
+ return Math.max(1, content.split("\n").length);
976
+ }
977
+ function ratioFromHeight(object) {
963
978
  const fontSize = optionalNum(object.fontSize);
964
979
  const height = optionalNum(object.height);
965
980
  if (!fontSize || !height) return void 0;
966
- const content = object.text === void 0 ? "" : String(object.text);
967
- const lines = Math.max(1, content.split("\n").length);
968
- const ratio = Math.round(height / (fontSize * lines) * 1e3) / 1e3;
969
- if (!(ratio > 0.8 && ratio < 1.6)) return void 0;
981
+ const ratio = Math.round(height / (fontSize * textLines(object)) * 1e3) / 1e3;
982
+ return ratio > 0.8 && ratio < 1.6 ? ratio : void 0;
983
+ }
984
+ function estimateLineHeight(object) {
985
+ const ratio = ratioFromHeight(object);
986
+ return ratio === void 0 ? void 0 : { type: "percent", value: ratio };
987
+ }
988
+ function lineHeightPx(lineHeight, fontSize) {
989
+ if (lineHeight === void 0) return fontSize * 1.16;
990
+ if (typeof lineHeight === "number") return lineHeight;
991
+ if (lineHeight && lineHeight.type === "percent") return fontSize * (Number(lineHeight.value) || 0);
992
+ return void 0;
993
+ }
994
+ function fitLineHeight(props, object) {
995
+ if (props.textWrap !== "none") return props.lineHeight;
996
+ const fontSize = optionalNum(object.fontSize);
997
+ const height = optionalNum(object.height);
998
+ const ratio = ratioFromHeight(object);
999
+ if (!fontSize || !height || ratio === void 0) return props.lineHeight;
1000
+ const current = lineHeightPx(props.lineHeight, fontSize);
1001
+ if (current === void 0 || current * textLines(object) <= height + 0.5) return props.lineHeight;
970
1002
  return { type: "percent", value: ratio };
971
1003
  }
1004
+ function textLineHeight(props, object) {
1005
+ return fitLineHeight({ ...props, lineHeight: { type: "percent", value: 1 } }, object);
1006
+ }
1007
+ function textboxLineHeight(object) {
1008
+ const fontSize = optionalNum(object.fontSize);
1009
+ const height = optionalNum(object.height);
1010
+ const lines = textLines(object);
1011
+ if (lines > 1 && fontSize && height) {
1012
+ const ratio = Math.round(height / (fontSize * lines) * 1e3) / 1e3;
1013
+ if (ratio > 0.8 && ratio < 2.5) {
1014
+ return { type: "percent", value: ratio };
1015
+ }
1016
+ }
1017
+ return { type: "percent", value: 1 };
1018
+ }
972
1019
  function isIText(object) {
973
1020
  return object.type === "i-text" || object.type === "itext";
974
1021
  }
975
1022
  function convertText(object, ctx, parent) {
976
1023
  const props = textProps(object, ctx);
977
1024
  const itext = isIText(object);
1025
+ if (!itext && object.type === "textbox") {
1026
+ props.lineHeight = textboxLineHeight(object);
1027
+ } else if (!itext) {
1028
+ props.verticalAlign = "top";
1029
+ props.lineHeight = textLineHeight(props, object);
1030
+ }
978
1031
  const text = itext ? node("IText", {
979
1032
  ...props,
980
1033
  lineHeight: props.lineHeight || estimateLineHeight(object)
@@ -1183,7 +1236,7 @@ async function hydrateLeaferDocument(document, loadDefinition) {
1183
1236
  await hydrateLibChildren(document.children, loadDefinition, []);
1184
1237
  return document;
1185
1238
  }
1186
- function originFraction(around) {
1239
+ function originFraction2(around) {
1187
1240
  if (!around) return { x: 0, y: 0 };
1188
1241
  if (typeof around === "object") {
1189
1242
  const x = Number(around.x);
@@ -1226,7 +1279,7 @@ function contentBox(children, offsetX = 0, offsetY = 0) {
1226
1279
  if (!child || typeof child !== "object") return;
1227
1280
  const x = Number.isFinite(Number(child.x)) ? Number(child.x) : 0;
1228
1281
  const y = Number.isFinite(Number(child.y)) ? Number(child.y) : 0;
1229
- const origin = originFraction(child.around);
1282
+ const origin = originFraction2(child.around);
1230
1283
  const size = nodeSize(child);
1231
1284
  const left = offsetX + x - size.width * origin.x;
1232
1285
  const top = offsetY + y - size.height * origin.y;