@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 +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 +2 -2
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 =
|
|
120
|
-
const fractionY =
|
|
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
|
|
231
|
-
|
|
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 =
|
|
437
|
-
const fractionY =
|
|
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
|
-
|
|
794
|
-
|
|
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
|
-
|
|
806
|
-
|
|
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
|
-
|
|
836
|
-
|
|
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
|
|
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
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
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
|
|
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 =
|
|
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;
|