@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.cjs CHANGED
@@ -92,6 +92,12 @@ var ORIGIN_FRACTION = {
92
92
  top: 0,
93
93
  bottom: 1
94
94
  };
95
+ function originFraction(value, fallback = 0) {
96
+ if (value === void 0 || value === null || value === "") return fallback;
97
+ if (typeof value === "number") return Number.isFinite(value) ? value : fallback;
98
+ const named = ORIGIN_FRACTION[String(value).trim().toLowerCase()];
99
+ return named === void 0 ? fallback : named;
100
+ }
95
101
  function pathPlaceOrigin(object, bounds) {
96
102
  if (!bounds) {
97
103
  return { left: num(object.left), top: num(object.top) };
@@ -104,8 +110,8 @@ function pathPlaceOrigin(object, bounds) {
104
110
  const height = bounds.height;
105
111
  const scaleX = Math.abs(normalizeScale(num(object.scaleX, 1) || 1, width));
106
112
  const scaleY = Math.abs(normalizeScale(num(object.scaleY, 1) || 1, height));
107
- const fractionX = ORIGIN_FRACTION[object.originX === void 0 ? "left" : object.originX] || 0;
108
- const fractionY = ORIGIN_FRACTION[object.originY === void 0 ? "top" : object.originY] || 0;
113
+ const fractionX = originFraction(object.originX);
114
+ const fractionY = originFraction(object.originY);
109
115
  const angle = num(object.angle) * Math.PI / 180;
110
116
  const cos = Math.cos(angle);
111
117
  const sin = Math.sin(angle);
@@ -215,8 +221,13 @@ function convertPathData(pathData, object) {
215
221
  }).join(" ");
216
222
  }
217
223
  function aroundOf(object) {
218
- const originX = object.originX === void 0 ? "left" : object.originX;
219
- const originY = object.originY === void 0 ? "top" : object.originY;
224
+ const axisOf = (fraction, low, center, high) => {
225
+ if (fraction <= 0.25) return low;
226
+ if (fraction >= 0.75) return high;
227
+ return center;
228
+ };
229
+ const originX = axisOf(originFraction(object.originX), "left", "center", "right");
230
+ const originY = axisOf(originFraction(object.originY), "top", "center", "bottom");
220
231
  const map = {
221
232
  left: { top: "top-left", center: "left", bottom: "bottom-left" },
222
233
  center: { top: "top", center: "center", bottom: "bottom" },
@@ -339,13 +350,6 @@ function normalizeSizeSign(object) {
339
350
  }
340
351
 
341
352
  // src/attributes/transform.js
342
- var ORIGIN_FRACTION2 = {
343
- left: 0,
344
- center: 0.5,
345
- right: 1,
346
- top: 0,
347
- bottom: 1
348
- };
349
353
  var PATH_PLACED_TYPES = ["polyline", "polygon", "line"];
350
354
  function bakePointsScale(node2, object, scaleX, scaleY) {
351
355
  if (!PATH_PLACED_TYPES.includes(object.type)) return false;
@@ -421,9 +425,8 @@ function applyPosition(node2, object, scaleX, scaleY) {
421
425
  if (PATH_PLACED_TYPES.includes(object.type)) return;
422
426
  const width = num(object.width);
423
427
  const height = num(object.height);
424
- const fractionX = ORIGIN_FRACTION2[object.originX === void 0 ? "left" : object.originX];
425
- const fractionY = ORIGIN_FRACTION2[object.originY === void 0 ? "top" : object.originY];
426
- if (fractionX === void 0 || fractionY === void 0) return;
428
+ const fractionX = originFraction(object.originX);
429
+ const fractionY = originFraction(object.originY);
427
430
  const single = scaleX > 0 && scaleY > 0 && !num(node2.rotation, 0);
428
431
  if (single) return;
429
432
  const angle = num(node2.rotation, 0) * Math.PI / 180;
@@ -645,6 +648,7 @@ function applyPaint(node2, object, ctx) {
645
648
  if (strokeWidth !== void 0) {
646
649
  node2.strokeWidth = strokeWidth;
647
650
  }
651
+ node2.strokeScaleFixed = true;
648
652
  if (Array.isArray(object.strokeDashArray) && object.strokeDashArray.length) {
649
653
  node2.dashPattern = object.strokeDashArray;
650
654
  }
@@ -777,9 +781,13 @@ function cornerRadiusOf(object) {
777
781
  return [Math.max(0, rx || 0), Math.max(0, ry || 0)];
778
782
  }
779
783
  function convertRect(object, ctx, parent) {
784
+ const width = optionalNum(object.width);
785
+ const height = optionalNum(object.height);
780
786
  const rect = node("Rect", {
781
- width: optionalNum(object.width),
782
- height: optionalNum(object.height),
787
+ // Leafer 的 Rect 不给宽高时用默认的 100x100, Fabric 里缺宽高的图元
788
+ // (脏数据 / 没画完的空框)要显式写 0, 否则转出来凭空多出一个 100 的框
789
+ width: width === void 0 ? 0 : width,
790
+ height: height === void 0 ? 0 : height,
783
791
  fill: paintOf(object.fill, ctx),
784
792
  cornerRadius: cornerRadiusOf(object)
785
793
  });
@@ -789,9 +797,12 @@ function convertRect(object, ctx, parent) {
789
797
 
790
798
  // src/elements/ellipse.js
791
799
  function convertEllipse(object, ctx, parent) {
800
+ const width = optionalNum(object.width);
801
+ const height = optionalNum(object.height);
792
802
  const ellipse = node("Ellipse", {
793
- width: optionalNum(object.width),
794
- height: optionalNum(object.height),
803
+ // 同 Rect: 不写宽高会落到 Leafer 的 100x100 默认尺寸
804
+ width: width === void 0 ? 0 : width,
805
+ height: height === void 0 ? 0 : height,
795
806
  fill: paintOf(object.fill, ctx)
796
807
  });
797
808
  place(ellipse, object, parent);
@@ -820,8 +831,9 @@ function convertTriangle(object, ctx, parent) {
820
831
  const w = width || 0;
821
832
  const h = height || 0;
822
833
  const triangle = node("Polygon", {
823
- width,
824
- height,
834
+ // 不写宽高会落到 Leafer 的 100x100 默认尺寸
835
+ width: w,
836
+ height: h,
825
837
  // 顶点在正上方,底边贴底:和 Fabric 的三角形一致
826
838
  points: [w / 2, 0, w, h, 0, h],
827
839
  fill: paintOf(object.fill, ctx),
@@ -972,7 +984,6 @@ function textProps(object, ctx) {
972
984
  letterSpacing: convertLetterSpacing(object.charSpacing, object.fontSize),
973
985
  fill: object.fill ? paintOf(object.fill, ctx) : "#000000",
974
986
  shadow: object.shadow ? convertShadow(object.shadow) : void 0,
975
- verticalAlign: "middle",
976
987
  textDecoration: textDecorationOf(object)
977
988
  };
978
989
  const backgroundColor = object.backgroundColor || object.textBackgroundColor;
@@ -989,22 +1000,64 @@ function textProps(object, ctx) {
989
1000
  }
990
1001
 
991
1002
  // src/elements/text.js
992
- function estimateLineHeight(object) {
1003
+ function textLines(object) {
1004
+ const content = object.text === void 0 ? "" : String(object.text);
1005
+ return Math.max(1, content.split("\n").length);
1006
+ }
1007
+ function ratioFromHeight(object) {
993
1008
  const fontSize = optionalNum(object.fontSize);
994
1009
  const height = optionalNum(object.height);
995
1010
  if (!fontSize || !height) return void 0;
996
- const content = object.text === void 0 ? "" : String(object.text);
997
- const lines = Math.max(1, content.split("\n").length);
998
- const ratio = Math.round(height / (fontSize * lines) * 1e3) / 1e3;
999
- if (!(ratio > 0.8 && ratio < 1.6)) return void 0;
1011
+ const ratio = Math.round(height / (fontSize * textLines(object)) * 1e3) / 1e3;
1012
+ return ratio > 0.8 && ratio < 1.6 ? ratio : void 0;
1013
+ }
1014
+ function estimateLineHeight(object) {
1015
+ const ratio = ratioFromHeight(object);
1016
+ return ratio === void 0 ? void 0 : { type: "percent", value: ratio };
1017
+ }
1018
+ function lineHeightPx(lineHeight, fontSize) {
1019
+ if (lineHeight === void 0) return fontSize * 1.16;
1020
+ if (typeof lineHeight === "number") return lineHeight;
1021
+ if (lineHeight && lineHeight.type === "percent") return fontSize * (Number(lineHeight.value) || 0);
1022
+ return void 0;
1023
+ }
1024
+ function fitLineHeight(props, object) {
1025
+ if (props.textWrap !== "none") return props.lineHeight;
1026
+ const fontSize = optionalNum(object.fontSize);
1027
+ const height = optionalNum(object.height);
1028
+ const ratio = ratioFromHeight(object);
1029
+ if (!fontSize || !height || ratio === void 0) return props.lineHeight;
1030
+ const current = lineHeightPx(props.lineHeight, fontSize);
1031
+ if (current === void 0 || current * textLines(object) <= height + 0.5) return props.lineHeight;
1000
1032
  return { type: "percent", value: ratio };
1001
1033
  }
1034
+ function textLineHeight(props, object) {
1035
+ return fitLineHeight({ ...props, lineHeight: { type: "percent", value: 1 } }, object);
1036
+ }
1037
+ function textboxLineHeight(object) {
1038
+ const fontSize = optionalNum(object.fontSize);
1039
+ const height = optionalNum(object.height);
1040
+ const lines = textLines(object);
1041
+ if (lines > 1 && fontSize && height) {
1042
+ const ratio = Math.round(height / (fontSize * lines) * 1e3) / 1e3;
1043
+ if (ratio > 0.8 && ratio < 2.5) {
1044
+ return { type: "percent", value: ratio };
1045
+ }
1046
+ }
1047
+ return { type: "percent", value: 1 };
1048
+ }
1002
1049
  function isIText(object) {
1003
1050
  return object.type === "i-text" || object.type === "itext";
1004
1051
  }
1005
1052
  function convertText(object, ctx, parent) {
1006
1053
  const props = textProps(object, ctx);
1007
1054
  const itext = isIText(object);
1055
+ if (!itext && object.type === "textbox") {
1056
+ props.lineHeight = textboxLineHeight(object);
1057
+ } else if (!itext) {
1058
+ props.verticalAlign = "top";
1059
+ props.lineHeight = textLineHeight(props, object);
1060
+ }
1008
1061
  const text = itext ? node("IText", {
1009
1062
  ...props,
1010
1063
  lineHeight: props.lineHeight || estimateLineHeight(object)
@@ -1094,7 +1147,7 @@ function stripChildPageActions(object, parentPageActionEvents) {
1094
1147
  }
1095
1148
  return object;
1096
1149
  }
1097
- function originFraction(around) {
1150
+ function originFraction2(around) {
1098
1151
  if (!around) return { x: 0, y: 0 };
1099
1152
  if (typeof around === "object") {
1100
1153
  const x = Number(around.x);
@@ -1137,7 +1190,7 @@ function contentBox(children, offsetX = 0, offsetY = 0) {
1137
1190
  if (!child || typeof child !== "object") return;
1138
1191
  const x = Number.isFinite(Number(child.x)) ? Number(child.x) : 0;
1139
1192
  const y = Number.isFinite(Number(child.y)) ? Number(child.y) : 0;
1140
- const origin = originFraction(child.around);
1193
+ const origin = originFraction2(child.around);
1141
1194
  const size = nodeSize(child);
1142
1195
  const left = offsetX + x - size.width * origin.x;
1143
1196
  const top = offsetY + y - size.height * origin.y;