@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.cjs +81 -28
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +81 -28
- package/dist/index.mjs.map +1 -1
- package/dist/leafer.cjs +81 -28
- package/dist/leafer.cjs.map +1 -1
- package/dist/leafer.mjs +81 -28
- package/dist/leafer.mjs.map +1 -1
- package/package.json +1 -1
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 =
|
|
78
|
-
const fractionY =
|
|
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
|
|
189
|
-
|
|
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 =
|
|
395
|
-
const fractionY =
|
|
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
|
-
|
|
752
|
-
|
|
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
|
-
|
|
764
|
-
|
|
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
|
-
|
|
794
|
-
|
|
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
|
|
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
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
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
|
|
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 =
|
|
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;
|