@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/leafer.mjs CHANGED
@@ -65,6 +65,12 @@ var ORIGIN_FRACTION = {
65
65
  top: 0,
66
66
  bottom: 1
67
67
  };
68
+ function originFraction(value, fallback = 0) {
69
+ if (value === void 0 || value === null || value === "") return fallback;
70
+ if (typeof value === "number") return Number.isFinite(value) ? value : fallback;
71
+ const named = ORIGIN_FRACTION[String(value).trim().toLowerCase()];
72
+ return named === void 0 ? fallback : named;
73
+ }
68
74
  function pathPlaceOrigin(object, bounds) {
69
75
  if (!bounds) {
70
76
  return { left: num(object.left), top: num(object.top) };
@@ -77,8 +83,8 @@ function pathPlaceOrigin(object, bounds) {
77
83
  const height = bounds.height;
78
84
  const scaleX = Math.abs(normalizeScale(num(object.scaleX, 1) || 1, width));
79
85
  const scaleY = Math.abs(normalizeScale(num(object.scaleY, 1) || 1, height));
80
- const fractionX = ORIGIN_FRACTION[object.originX === void 0 ? "left" : object.originX] || 0;
81
- const fractionY = ORIGIN_FRACTION[object.originY === void 0 ? "top" : object.originY] || 0;
86
+ const fractionX = originFraction(object.originX);
87
+ const fractionY = originFraction(object.originY);
82
88
  const angle = num(object.angle) * Math.PI / 180;
83
89
  const cos = Math.cos(angle);
84
90
  const sin = Math.sin(angle);
@@ -188,8 +194,13 @@ function convertPathData(pathData, object) {
188
194
  }).join(" ");
189
195
  }
190
196
  function aroundOf(object) {
191
- const originX = object.originX === void 0 ? "left" : object.originX;
192
- const originY = object.originY === void 0 ? "top" : object.originY;
197
+ const axisOf = (fraction, low, center, high) => {
198
+ if (fraction <= 0.25) return low;
199
+ if (fraction >= 0.75) return high;
200
+ return center;
201
+ };
202
+ const originX = axisOf(originFraction(object.originX), "left", "center", "right");
203
+ const originY = axisOf(originFraction(object.originY), "top", "center", "bottom");
193
204
  const map = {
194
205
  left: { top: "top-left", center: "left", bottom: "bottom-left" },
195
206
  center: { top: "top", center: "center", bottom: "bottom" },
@@ -312,13 +323,6 @@ function normalizeSizeSign(object) {
312
323
  }
313
324
 
314
325
  // src/attributes/transform.js
315
- var ORIGIN_FRACTION2 = {
316
- left: 0,
317
- center: 0.5,
318
- right: 1,
319
- top: 0,
320
- bottom: 1
321
- };
322
326
  var PATH_PLACED_TYPES = ["polyline", "polygon", "line"];
323
327
  function bakePointsScale(node2, object, scaleX, scaleY) {
324
328
  if (!PATH_PLACED_TYPES.includes(object.type)) return false;
@@ -394,9 +398,8 @@ function applyPosition(node2, object, scaleX, scaleY) {
394
398
  if (PATH_PLACED_TYPES.includes(object.type)) return;
395
399
  const width = num(object.width);
396
400
  const height = num(object.height);
397
- const fractionX = ORIGIN_FRACTION2[object.originX === void 0 ? "left" : object.originX];
398
- const fractionY = ORIGIN_FRACTION2[object.originY === void 0 ? "top" : object.originY];
399
- if (fractionX === void 0 || fractionY === void 0) return;
401
+ const fractionX = originFraction(object.originX);
402
+ const fractionY = originFraction(object.originY);
400
403
  const single = scaleX > 0 && scaleY > 0 && !num(node2.rotation, 0);
401
404
  if (single) return;
402
405
  const angle = num(node2.rotation, 0) * Math.PI / 180;
@@ -618,6 +621,7 @@ function applyPaint(node2, object, ctx) {
618
621
  if (strokeWidth !== void 0) {
619
622
  node2.strokeWidth = strokeWidth;
620
623
  }
624
+ node2.strokeScaleFixed = true;
621
625
  if (Array.isArray(object.strokeDashArray) && object.strokeDashArray.length) {
622
626
  node2.dashPattern = object.strokeDashArray;
623
627
  }
@@ -750,9 +754,13 @@ function cornerRadiusOf(object) {
750
754
  return [Math.max(0, rx || 0), Math.max(0, ry || 0)];
751
755
  }
752
756
  function convertRect(object, ctx, parent) {
757
+ const width = optionalNum(object.width);
758
+ const height = optionalNum(object.height);
753
759
  const rect = node("Rect", {
754
- width: optionalNum(object.width),
755
- height: optionalNum(object.height),
760
+ // Leafer 的 Rect 不给宽高时用默认的 100x100, Fabric 里缺宽高的图元
761
+ // (脏数据 / 没画完的空框)要显式写 0, 否则转出来凭空多出一个 100 的框
762
+ width: width === void 0 ? 0 : width,
763
+ height: height === void 0 ? 0 : height,
756
764
  fill: paintOf(object.fill, ctx),
757
765
  cornerRadius: cornerRadiusOf(object)
758
766
  });
@@ -762,9 +770,12 @@ function convertRect(object, ctx, parent) {
762
770
 
763
771
  // src/elements/ellipse.js
764
772
  function convertEllipse(object, ctx, parent) {
773
+ const width = optionalNum(object.width);
774
+ const height = optionalNum(object.height);
765
775
  const ellipse = node("Ellipse", {
766
- width: optionalNum(object.width),
767
- height: optionalNum(object.height),
776
+ // 同 Rect: 不写宽高会落到 Leafer 的 100x100 默认尺寸
777
+ width: width === void 0 ? 0 : width,
778
+ height: height === void 0 ? 0 : height,
768
779
  fill: paintOf(object.fill, ctx)
769
780
  });
770
781
  place(ellipse, object, parent);
@@ -793,8 +804,9 @@ function convertTriangle(object, ctx, parent) {
793
804
  const w = width || 0;
794
805
  const h = height || 0;
795
806
  const triangle = node("Polygon", {
796
- width,
797
- height,
807
+ // 不写宽高会落到 Leafer 的 100x100 默认尺寸
808
+ width: w,
809
+ height: h,
798
810
  // 顶点在正上方,底边贴底:和 Fabric 的三角形一致
799
811
  points: [w / 2, 0, w, h, 0, h],
800
812
  fill: paintOf(object.fill, ctx),
@@ -945,7 +957,6 @@ function textProps(object, ctx) {
945
957
  letterSpacing: convertLetterSpacing(object.charSpacing, object.fontSize),
946
958
  fill: object.fill ? paintOf(object.fill, ctx) : "#000000",
947
959
  shadow: object.shadow ? convertShadow(object.shadow) : void 0,
948
- verticalAlign: "middle",
949
960
  textDecoration: textDecorationOf(object)
950
961
  };
951
962
  const backgroundColor = object.backgroundColor || object.textBackgroundColor;
@@ -962,22 +973,64 @@ function textProps(object, ctx) {
962
973
  }
963
974
 
964
975
  // src/elements/text.js
965
- function estimateLineHeight(object) {
976
+ function textLines(object) {
977
+ const content = object.text === void 0 ? "" : String(object.text);
978
+ return Math.max(1, content.split("\n").length);
979
+ }
980
+ function ratioFromHeight(object) {
966
981
  const fontSize = optionalNum(object.fontSize);
967
982
  const height = optionalNum(object.height);
968
983
  if (!fontSize || !height) return void 0;
969
- const content = object.text === void 0 ? "" : String(object.text);
970
- const lines = Math.max(1, content.split("\n").length);
971
- const ratio = Math.round(height / (fontSize * lines) * 1e3) / 1e3;
972
- if (!(ratio > 0.8 && ratio < 1.6)) return void 0;
984
+ const ratio = Math.round(height / (fontSize * textLines(object)) * 1e3) / 1e3;
985
+ return ratio > 0.8 && ratio < 1.6 ? ratio : void 0;
986
+ }
987
+ function estimateLineHeight(object) {
988
+ const ratio = ratioFromHeight(object);
989
+ return ratio === void 0 ? void 0 : { type: "percent", value: ratio };
990
+ }
991
+ function lineHeightPx(lineHeight, fontSize) {
992
+ if (lineHeight === void 0) return fontSize * 1.16;
993
+ if (typeof lineHeight === "number") return lineHeight;
994
+ if (lineHeight && lineHeight.type === "percent") return fontSize * (Number(lineHeight.value) || 0);
995
+ return void 0;
996
+ }
997
+ function fitLineHeight(props, object) {
998
+ if (props.textWrap !== "none") return props.lineHeight;
999
+ const fontSize = optionalNum(object.fontSize);
1000
+ const height = optionalNum(object.height);
1001
+ const ratio = ratioFromHeight(object);
1002
+ if (!fontSize || !height || ratio === void 0) return props.lineHeight;
1003
+ const current = lineHeightPx(props.lineHeight, fontSize);
1004
+ if (current === void 0 || current * textLines(object) <= height + 0.5) return props.lineHeight;
973
1005
  return { type: "percent", value: ratio };
974
1006
  }
1007
+ function textLineHeight(props, object) {
1008
+ return fitLineHeight({ ...props, lineHeight: { type: "percent", value: 1 } }, object);
1009
+ }
1010
+ function textboxLineHeight(object) {
1011
+ const fontSize = optionalNum(object.fontSize);
1012
+ const height = optionalNum(object.height);
1013
+ const lines = textLines(object);
1014
+ if (lines > 1 && fontSize && height) {
1015
+ const ratio = Math.round(height / (fontSize * lines) * 1e3) / 1e3;
1016
+ if (ratio > 0.8 && ratio < 2.5) {
1017
+ return { type: "percent", value: ratio };
1018
+ }
1019
+ }
1020
+ return { type: "percent", value: 1 };
1021
+ }
975
1022
  function isIText(object) {
976
1023
  return object.type === "i-text" || object.type === "itext";
977
1024
  }
978
1025
  function convertText(object, ctx, parent) {
979
1026
  const props = textProps(object, ctx);
980
1027
  const itext = isIText(object);
1028
+ if (!itext && object.type === "textbox") {
1029
+ props.lineHeight = textboxLineHeight(object);
1030
+ } else if (!itext) {
1031
+ props.verticalAlign = "top";
1032
+ props.lineHeight = textLineHeight(props, object);
1033
+ }
981
1034
  const text = itext ? node("IText", {
982
1035
  ...props,
983
1036
  lineHeight: props.lineHeight || estimateLineHeight(object)
@@ -1067,7 +1120,7 @@ function stripChildPageActions(object, parentPageActionEvents) {
1067
1120
  }
1068
1121
  return object;
1069
1122
  }
1070
- function originFraction(around) {
1123
+ function originFraction2(around) {
1071
1124
  if (!around) return { x: 0, y: 0 };
1072
1125
  if (typeof around === "object") {
1073
1126
  const x = Number(around.x);
@@ -1110,7 +1163,7 @@ function contentBox(children, offsetX = 0, offsetY = 0) {
1110
1163
  if (!child || typeof child !== "object") return;
1111
1164
  const x = Number.isFinite(Number(child.x)) ? Number(child.x) : 0;
1112
1165
  const y = Number.isFinite(Number(child.y)) ? Number(child.y) : 0;
1113
- const origin = originFraction(child.around);
1166
+ const origin = originFraction2(child.around);
1114
1167
  const size = nodeSize(child);
1115
1168
  const left = offsetX + x - size.width * origin.x;
1116
1169
  const top = offsetY + y - size.height * origin.y;