@xrubik/fabric-to-leafer 0.1.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.cjs CHANGED
@@ -104,6 +104,12 @@ var ORIGIN_FRACTION = {
104
104
  top: 0,
105
105
  bottom: 1
106
106
  };
107
+ function originFraction(value, fallback = 0) {
108
+ if (value === void 0 || value === null || value === "") return fallback;
109
+ if (typeof value === "number") return Number.isFinite(value) ? value : fallback;
110
+ const named = ORIGIN_FRACTION[String(value).trim().toLowerCase()];
111
+ return named === void 0 ? fallback : named;
112
+ }
107
113
  function pathPlaceOrigin(object, bounds) {
108
114
  if (!bounds) {
109
115
  return { left: num(object.left), top: num(object.top) };
@@ -116,8 +122,8 @@ function pathPlaceOrigin(object, bounds) {
116
122
  const height = bounds.height;
117
123
  const scaleX = Math.abs(normalizeScale(num(object.scaleX, 1) || 1, width));
118
124
  const scaleY = Math.abs(normalizeScale(num(object.scaleY, 1) || 1, height));
119
- const fractionX = ORIGIN_FRACTION[object.originX === void 0 ? "left" : object.originX] || 0;
120
- const fractionY = ORIGIN_FRACTION[object.originY === void 0 ? "top" : object.originY] || 0;
125
+ const fractionX = originFraction(object.originX);
126
+ const fractionY = originFraction(object.originY);
121
127
  const angle = num(object.angle) * Math.PI / 180;
122
128
  const cos = Math.cos(angle);
123
129
  const sin = Math.sin(angle);
@@ -227,8 +233,13 @@ function convertPathData(pathData, object) {
227
233
  }).join(" ");
228
234
  }
229
235
  function aroundOf(object) {
230
- const originX = object.originX === void 0 ? "left" : object.originX;
231
- const originY = object.originY === void 0 ? "top" : object.originY;
236
+ const axisOf = (fraction, low, center, high) => {
237
+ if (fraction <= 0.25) return low;
238
+ if (fraction >= 0.75) return high;
239
+ return center;
240
+ };
241
+ const originX = axisOf(originFraction(object.originX), "left", "center", "right");
242
+ const originY = axisOf(originFraction(object.originY), "top", "center", "bottom");
232
243
  const map = {
233
244
  left: { top: "top-left", center: "left", bottom: "bottom-left" },
234
245
  center: { top: "top", center: "center", bottom: "bottom" },
@@ -351,13 +362,6 @@ function normalizeSizeSign(object) {
351
362
  }
352
363
 
353
364
  // src/attributes/transform.js
354
- var ORIGIN_FRACTION2 = {
355
- left: 0,
356
- center: 0.5,
357
- right: 1,
358
- top: 0,
359
- bottom: 1
360
- };
361
365
  var PATH_PLACED_TYPES = ["polyline", "polygon", "line"];
362
366
  function bakePointsScale(node2, object, scaleX, scaleY) {
363
367
  if (!PATH_PLACED_TYPES.includes(object.type)) return false;
@@ -433,9 +437,8 @@ function applyPosition(node2, object, scaleX, scaleY) {
433
437
  if (PATH_PLACED_TYPES.includes(object.type)) return;
434
438
  const width = num(object.width);
435
439
  const height = num(object.height);
436
- const fractionX = ORIGIN_FRACTION2[object.originX === void 0 ? "left" : object.originX];
437
- const fractionY = ORIGIN_FRACTION2[object.originY === void 0 ? "top" : object.originY];
438
- if (fractionX === void 0 || fractionY === void 0) return;
440
+ const fractionX = originFraction(object.originX);
441
+ const fractionY = originFraction(object.originY);
439
442
  const single = scaleX > 0 && scaleY > 0 && !num(node2.rotation, 0);
440
443
  if (single) return;
441
444
  const angle = num(node2.rotation, 0) * Math.PI / 180;
@@ -657,6 +660,7 @@ function applyPaint(node2, object, ctx) {
657
660
  if (strokeWidth !== void 0) {
658
661
  node2.strokeWidth = strokeWidth;
659
662
  }
663
+ node2.strokeScaleFixed = true;
660
664
  if (Array.isArray(object.strokeDashArray) && object.strokeDashArray.length) {
661
665
  node2.dashPattern = object.strokeDashArray;
662
666
  }
@@ -789,9 +793,13 @@ function cornerRadiusOf(object) {
789
793
  return [Math.max(0, rx || 0), Math.max(0, ry || 0)];
790
794
  }
791
795
  function convertRect(object, ctx, parent) {
796
+ const width = optionalNum(object.width);
797
+ const height = optionalNum(object.height);
792
798
  const rect = node("Rect", {
793
- width: optionalNum(object.width),
794
- height: optionalNum(object.height),
799
+ // Leafer 的 Rect 不给宽高时用默认的 100x100, Fabric 里缺宽高的图元
800
+ // (脏数据 / 没画完的空框)要显式写 0, 否则转出来凭空多出一个 100 的框
801
+ width: width === void 0 ? 0 : width,
802
+ height: height === void 0 ? 0 : height,
795
803
  fill: paintOf(object.fill, ctx),
796
804
  cornerRadius: cornerRadiusOf(object)
797
805
  });
@@ -801,9 +809,12 @@ function convertRect(object, ctx, parent) {
801
809
 
802
810
  // src/elements/ellipse.js
803
811
  function convertEllipse(object, ctx, parent) {
812
+ const width = optionalNum(object.width);
813
+ const height = optionalNum(object.height);
804
814
  const ellipse = node("Ellipse", {
805
- width: optionalNum(object.width),
806
- height: optionalNum(object.height),
815
+ // 同 Rect: 不写宽高会落到 Leafer 的 100x100 默认尺寸
816
+ width: width === void 0 ? 0 : width,
817
+ height: height === void 0 ? 0 : height,
807
818
  fill: paintOf(object.fill, ctx)
808
819
  });
809
820
  place(ellipse, object, parent);
@@ -832,8 +843,9 @@ function convertTriangle(object, ctx, parent) {
832
843
  const w = width || 0;
833
844
  const h = height || 0;
834
845
  const triangle = node("Polygon", {
835
- width,
836
- height,
846
+ // 不写宽高会落到 Leafer 的 100x100 默认尺寸
847
+ width: w,
848
+ height: h,
837
849
  // 顶点在正上方,底边贴底:和 Fabric 的三角形一致
838
850
  points: [w / 2, 0, w, h, 0, h],
839
851
  fill: paintOf(object.fill, ctx),
@@ -984,7 +996,6 @@ function textProps(object, ctx) {
984
996
  letterSpacing: convertLetterSpacing(object.charSpacing, object.fontSize),
985
997
  fill: object.fill ? paintOf(object.fill, ctx) : "#000000",
986
998
  shadow: object.shadow ? convertShadow(object.shadow) : void 0,
987
- verticalAlign: "middle",
988
999
  textDecoration: textDecorationOf(object)
989
1000
  };
990
1001
  const backgroundColor = object.backgroundColor || object.textBackgroundColor;
@@ -1001,22 +1012,64 @@ function textProps(object, ctx) {
1001
1012
  }
1002
1013
 
1003
1014
  // src/elements/text.js
1004
- function estimateLineHeight(object) {
1015
+ function textLines(object) {
1016
+ const content = object.text === void 0 ? "" : String(object.text);
1017
+ return Math.max(1, content.split("\n").length);
1018
+ }
1019
+ function ratioFromHeight(object) {
1005
1020
  const fontSize = optionalNum(object.fontSize);
1006
1021
  const height = optionalNum(object.height);
1007
1022
  if (!fontSize || !height) return void 0;
1008
- const content = object.text === void 0 ? "" : String(object.text);
1009
- const lines = Math.max(1, content.split("\n").length);
1010
- const ratio = Math.round(height / (fontSize * lines) * 1e3) / 1e3;
1011
- if (!(ratio > 0.8 && ratio < 1.6)) return void 0;
1023
+ const ratio = Math.round(height / (fontSize * textLines(object)) * 1e3) / 1e3;
1024
+ return ratio > 0.8 && ratio < 1.6 ? ratio : void 0;
1025
+ }
1026
+ function estimateLineHeight(object) {
1027
+ const ratio = ratioFromHeight(object);
1028
+ return ratio === void 0 ? void 0 : { type: "percent", value: ratio };
1029
+ }
1030
+ function lineHeightPx(lineHeight, fontSize) {
1031
+ if (lineHeight === void 0) return fontSize * 1.16;
1032
+ if (typeof lineHeight === "number") return lineHeight;
1033
+ if (lineHeight && lineHeight.type === "percent") return fontSize * (Number(lineHeight.value) || 0);
1034
+ return void 0;
1035
+ }
1036
+ function fitLineHeight(props, object) {
1037
+ if (props.textWrap !== "none") return props.lineHeight;
1038
+ const fontSize = optionalNum(object.fontSize);
1039
+ const height = optionalNum(object.height);
1040
+ const ratio = ratioFromHeight(object);
1041
+ if (!fontSize || !height || ratio === void 0) return props.lineHeight;
1042
+ const current = lineHeightPx(props.lineHeight, fontSize);
1043
+ if (current === void 0 || current * textLines(object) <= height + 0.5) return props.lineHeight;
1012
1044
  return { type: "percent", value: ratio };
1013
1045
  }
1046
+ function textLineHeight(props, object) {
1047
+ return fitLineHeight({ ...props, lineHeight: { type: "percent", value: 1 } }, object);
1048
+ }
1049
+ function textboxLineHeight(object) {
1050
+ const fontSize = optionalNum(object.fontSize);
1051
+ const height = optionalNum(object.height);
1052
+ const lines = textLines(object);
1053
+ if (lines > 1 && fontSize && height) {
1054
+ const ratio = Math.round(height / (fontSize * lines) * 1e3) / 1e3;
1055
+ if (ratio > 0.8 && ratio < 2.5) {
1056
+ return { type: "percent", value: ratio };
1057
+ }
1058
+ }
1059
+ return { type: "percent", value: 1 };
1060
+ }
1014
1061
  function isIText(object) {
1015
1062
  return object.type === "i-text" || object.type === "itext";
1016
1063
  }
1017
1064
  function convertText(object, ctx, parent) {
1018
1065
  const props = textProps(object, ctx);
1019
1066
  const itext = isIText(object);
1067
+ if (!itext && object.type === "textbox") {
1068
+ props.lineHeight = textboxLineHeight(object);
1069
+ } else if (!itext) {
1070
+ props.verticalAlign = "top";
1071
+ props.lineHeight = textLineHeight(props, object);
1072
+ }
1020
1073
  const text = itext ? node("IText", {
1021
1074
  ...props,
1022
1075
  lineHeight: props.lineHeight || estimateLineHeight(object)
@@ -1225,7 +1278,7 @@ async function hydrateLeaferDocument(document, loadDefinition) {
1225
1278
  await hydrateLibChildren(document.children, loadDefinition, []);
1226
1279
  return document;
1227
1280
  }
1228
- function originFraction(around) {
1281
+ function originFraction2(around) {
1229
1282
  if (!around) return { x: 0, y: 0 };
1230
1283
  if (typeof around === "object") {
1231
1284
  const x = Number(around.x);
@@ -1268,7 +1321,7 @@ function contentBox(children, offsetX = 0, offsetY = 0) {
1268
1321
  if (!child || typeof child !== "object") return;
1269
1322
  const x = Number.isFinite(Number(child.x)) ? Number(child.x) : 0;
1270
1323
  const y = Number.isFinite(Number(child.y)) ? Number(child.y) : 0;
1271
- const origin = originFraction(child.around);
1324
+ const origin = originFraction2(child.around);
1272
1325
  const size = nodeSize(child);
1273
1326
  const left = offsetX + x - size.width * origin.x;
1274
1327
  const top = offsetY + y - size.height * origin.y;