fabricjs-document-engine 1.1.0 → 1.3.0
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/README.md +70 -1
- package/README.zh-CN.md +71 -1
- package/dist/assets/asset-pipeline.cjs +10 -8
- package/dist/assets/asset-pipeline.d.cts +1 -1
- package/dist/assets/asset-pipeline.d.ts +1 -1
- package/dist/assets/asset-pipeline.js +10 -8
- package/dist/assets/image-check.cjs +4 -3
- package/dist/assets/image-check.d.cts +1 -1
- package/dist/assets/image-check.d.ts +1 -1
- package/dist/assets/image-check.js +4 -3
- package/dist/commands/clipboard.cjs +5 -0
- package/dist/commands/clipboard.js +5 -0
- package/dist/commands/text-edit.cjs +223 -0
- package/dist/commands/text-edit.d.cts +23 -0
- package/dist/commands/text-edit.d.ts +23 -0
- package/dist/commands/text-edit.js +218 -0
- package/dist/document/document-format.d.cts +15 -0
- package/dist/document/document-format.d.ts +15 -0
- package/dist/document/validate-document.cjs +8 -0
- package/dist/document/validate-document.js +8 -0
- package/dist/engine/create-document-engine.cjs +115 -21
- package/dist/engine/create-document-engine.d.cts +24 -6
- package/dist/engine/create-document-engine.d.ts +24 -6
- package/dist/engine/create-document-engine.js +116 -22
- package/dist/engine/errors.d.cts +1 -1
- package/dist/engine/errors.d.ts +1 -1
- package/dist/export/export-options.cjs +3 -0
- package/dist/export/export-options.d.cts +6 -0
- package/dist/export/export-options.d.ts +6 -0
- package/dist/export/export-options.js +3 -0
- package/dist/export/preflight-export.d.cts +1 -1
- package/dist/export/preflight-export.d.ts +1 -1
- package/dist/export/render-export.cjs +42 -4
- package/dist/export/render-export.js +43 -5
- package/dist/export/svg/clip-masks.cjs +39 -0
- package/dist/export/svg/clip-masks.js +37 -0
- package/dist/export/svg/embed-assets.cjs +1 -0
- package/dist/export/svg/embed-assets.js +1 -0
- package/dist/export/svg/raster-object.cjs +82 -0
- package/dist/export/svg/raster-object.js +81 -0
- package/dist/{pdf → export/svg}/text-decorations.cjs +3 -3
- package/dist/{pdf → export/svg}/text-decorations.js +3 -3
- package/dist/export/svg/text-on-path.cjs +0 -10
- package/dist/export/svg/text-on-path.js +1 -10
- package/dist/fabric/fabric-adapter.cjs +5 -3
- package/dist/fabric/fabric-adapter.js +5 -3
- package/dist/fabric/object-registry.cjs +7 -1
- package/dist/fabric/object-registry.js +7 -1
- package/dist/fabric/page-state.cjs +69 -0
- package/dist/fabric/page-state.js +64 -0
- package/dist/filters/filter-worker.cjs +281 -0
- package/dist/filters/filter-worker.d.cts +26 -0
- package/dist/filters/filter-worker.d.ts +26 -0
- package/dist/filters/filter-worker.js +281 -0
- package/dist/filters/pipeline.cjs +91 -0
- package/dist/filters/pipeline.d.cts +35 -0
- package/dist/filters/pipeline.d.ts +35 -0
- package/dist/filters/pipeline.js +87 -0
- package/dist/filters/worker-entry.cjs +78 -0
- package/dist/filters/worker-entry.d.cts +29 -0
- package/dist/filters/worker-entry.d.ts +29 -0
- package/dist/filters/worker-entry.js +78 -0
- package/dist/filters.cjs +9 -0
- package/dist/filters.d.cts +3 -0
- package/dist/filters.d.ts +3 -0
- package/dist/filters.js +3 -0
- package/dist/history/apply-state.cjs +19 -2
- package/dist/history/apply-state.js +19 -2
- package/dist/history/create-history.cjs +53 -13
- package/dist/history/create-history.d.cts +8 -0
- package/dist/history/create-history.d.ts +8 -0
- package/dist/history/create-history.js +53 -13
- package/dist/history/snapshot.cjs +10 -1
- package/dist/history/snapshot.js +10 -2
- package/dist/import/svg-import.cjs +102 -0
- package/dist/import/svg-import.d.cts +1 -0
- package/dist/import/svg-import.d.ts +1 -0
- package/dist/import/svg-import.js +103 -1
- package/dist/index.cjs +7 -0
- package/dist/index.d.cts +4 -3
- package/dist/index.d.ts +4 -3
- package/dist/index.js +2 -1
- package/dist/migrations/migrate-document.cjs +2 -1
- package/dist/migrations/migrate-document.js +2 -1
- package/dist/pdf/export-pdf.cjs +8 -67
- package/dist/pdf/export-pdf.js +8 -67
- package/dist/performance/batch.cjs +31 -0
- package/dist/performance/batch.d.cts +4 -0
- package/dist/performance/batch.d.ts +4 -0
- package/dist/performance/batch.js +31 -0
- package/dist/performance/dirty-regions.cjs +378 -0
- package/dist/performance/dirty-regions.d.cts +23 -0
- package/dist/performance/dirty-regions.d.ts +23 -0
- package/dist/performance/dirty-regions.js +378 -0
- package/dist/performance/monitor.cjs +60 -0
- package/dist/performance/monitor.d.cts +20 -0
- package/dist/performance/monitor.d.ts +20 -0
- package/dist/performance/monitor.js +60 -0
- package/dist/performance/spatial-index.cjs +78 -0
- package/dist/performance/spatial-index.d.cts +21 -0
- package/dist/performance/spatial-index.d.ts +21 -0
- package/dist/performance/spatial-index.js +78 -0
- package/dist/performance.cjs +9 -0
- package/dist/performance.d.cts +5 -0
- package/dist/performance.d.ts +5 -0
- package/dist/performance.js +5 -0
- package/dist/recovery/recovery-controller.cjs +148 -29
- package/dist/recovery/recovery-controller.d.cts +8 -0
- package/dist/recovery/recovery-controller.d.ts +8 -0
- package/dist/recovery/recovery-controller.js +148 -29
- package/dist/security/content-limits.cjs +43 -3
- package/dist/security/content-limits.d.cts +8 -0
- package/dist/security/content-limits.d.ts +8 -0
- package/dist/security/content-limits.js +39 -4
- package/dist/text/bounded-textbox.cjs +306 -0
- package/dist/text/bounded-textbox.d.cts +53 -0
- package/dist/text/bounded-textbox.d.ts +53 -0
- package/dist/text/bounded-textbox.js +306 -0
- package/dist/text/mobile-input.cjs +146 -0
- package/dist/text/mobile-input.d.cts +19 -0
- package/dist/text/mobile-input.d.ts +19 -0
- package/dist/text/mobile-input.js +145 -0
- package/dist/text/register.cjs +17 -0
- package/dist/text/register.d.cts +5 -0
- package/dist/text/register.d.ts +5 -0
- package/dist/text/register.js +16 -0
- package/dist/text/shaping.cjs +125 -0
- package/dist/text/shaping.d.cts +28 -0
- package/dist/text/shaping.d.ts +28 -0
- package/dist/text/shaping.js +121 -0
- package/dist/text/vertical-orientation.cjs +192 -0
- package/dist/text/vertical-orientation.d.cts +10 -0
- package/dist/text/vertical-orientation.d.ts +10 -0
- package/dist/text/vertical-orientation.js +190 -0
- package/dist/text/vertical-text.cjs +336 -0
- package/dist/text/vertical-text.d.cts +54 -0
- package/dist/text/vertical-text.d.ts +54 -0
- package/dist/text/vertical-text.js +336 -0
- package/dist/text.cjs +21 -0
- package/dist/text.d.cts +7 -0
- package/dist/text.d.ts +7 -0
- package/dist/text.js +7 -0
- package/dist/util/concurrency.cjs +16 -3
- package/dist/util/concurrency.js +16 -3
- package/package.json +31 -1
- package/schema/document-v1.schema.json +5 -1
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
require("../../security/content-limits.cjs");
|
|
2
|
+
const require_walk_objects = require("../../fabric/walk-objects.cjs");
|
|
3
|
+
//#region src/export/svg/raster-object.ts
|
|
4
|
+
/**
|
|
5
|
+
* The largest multiplier that keeps a `width × height` area within the
|
|
6
|
+
* canvas limits. A picture over the browser's limit would fail, so a large
|
|
7
|
+
* page is drawn at a lower resolution instead: O(1) arithmetic.
|
|
8
|
+
*/
|
|
9
|
+
function safeMultiplier(multiplier, width, height, limits) {
|
|
10
|
+
const maxPixels = limits?.maxCanvasPixels ?? 67108864;
|
|
11
|
+
const maxSide = limits?.maxCanvasSide ?? 16384;
|
|
12
|
+
const byArea = Math.sqrt(maxPixels / Math.max(1, width * height));
|
|
13
|
+
const bySide = maxSide / Math.max(1, width, height);
|
|
14
|
+
return Math.min(multiplier, byArea, bySide);
|
|
15
|
+
}
|
|
16
|
+
function shadowPadding(object) {
|
|
17
|
+
let padding = 0;
|
|
18
|
+
require_walk_objects.walkObjects([object], (child) => {
|
|
19
|
+
const shadow = child.shadow;
|
|
20
|
+
if (shadow) padding = Math.max(padding, shadow.blur + Math.max(Math.abs(shadow.offsetX), Math.abs(shadow.offsetY)));
|
|
21
|
+
});
|
|
22
|
+
return padding + 2;
|
|
23
|
+
}
|
|
24
|
+
function usesBlendMode(object) {
|
|
25
|
+
let blends = false;
|
|
26
|
+
require_walk_objects.walkObjects([object], (child) => {
|
|
27
|
+
if (child.globalCompositeOperation && child.globalCompositeOperation !== "source-over") blends = true;
|
|
28
|
+
});
|
|
29
|
+
return blends;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* An object as a picture placed where it sits on the canvas. Usually the
|
|
33
|
+
* object alone, so the vectors underneath show through. An object with a
|
|
34
|
+
* blend mode mixes with what lies below it, so its picture includes
|
|
35
|
+
* everything below it within its bounds, which covers those vectors exactly.
|
|
36
|
+
*/
|
|
37
|
+
function rasterMarkup(canvas, object, multiplier, limits) {
|
|
38
|
+
object.setCoords();
|
|
39
|
+
const bounds = object.getBoundingRect();
|
|
40
|
+
const padding = shadowPadding(object);
|
|
41
|
+
const left = Math.max(0, Math.floor(bounds.left - padding));
|
|
42
|
+
const top = Math.max(0, Math.floor(bounds.top - padding));
|
|
43
|
+
const right = Math.min(canvas.getWidth(), Math.ceil(bounds.left + bounds.width + padding));
|
|
44
|
+
const bottom = Math.min(canvas.getHeight(), Math.ceil(bounds.top + bounds.height + padding));
|
|
45
|
+
if (right <= left || bottom <= top) return "";
|
|
46
|
+
const blends = usesBlendMode(object);
|
|
47
|
+
const stack = canvas.getObjects();
|
|
48
|
+
const position = stack.indexOf(object);
|
|
49
|
+
const saved = {
|
|
50
|
+
backgroundColor: canvas.backgroundColor,
|
|
51
|
+
backgroundImage: canvas.backgroundImage,
|
|
52
|
+
overlayColor: canvas.overlayColor,
|
|
53
|
+
overlayImage: canvas.overlayImage
|
|
54
|
+
};
|
|
55
|
+
if (!blends) {
|
|
56
|
+
canvas.backgroundColor = "";
|
|
57
|
+
canvas.backgroundImage = void 0;
|
|
58
|
+
}
|
|
59
|
+
canvas.overlayColor = "";
|
|
60
|
+
canvas.overlayImage = void 0;
|
|
61
|
+
let element;
|
|
62
|
+
try {
|
|
63
|
+
const filter = (candidate) => blends ? stack.indexOf(candidate) <= position : candidate === object;
|
|
64
|
+
const safe = safeMultiplier(multiplier, right - left, bottom - top, limits);
|
|
65
|
+
element = canvas.toCanvasElement(safe, {
|
|
66
|
+
left,
|
|
67
|
+
top,
|
|
68
|
+
width: right - left,
|
|
69
|
+
height: bottom - top,
|
|
70
|
+
filter
|
|
71
|
+
});
|
|
72
|
+
} finally {
|
|
73
|
+
Object.assign(canvas, saved);
|
|
74
|
+
}
|
|
75
|
+
const url = element.toDataURL("image/png");
|
|
76
|
+
element.width = 0;
|
|
77
|
+
element.height = 0;
|
|
78
|
+
return `<image x="${left}" y="${top}" width="${right - left}" height="${bottom - top}" preserveAspectRatio="none" xlink:href="${url}" />\n`;
|
|
79
|
+
}
|
|
80
|
+
//#endregion
|
|
81
|
+
exports.rasterMarkup = rasterMarkup;
|
|
82
|
+
exports.safeMultiplier = safeMultiplier;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import "../../security/content-limits.js";
|
|
2
|
+
import { walkObjects } from "../../fabric/walk-objects.js";
|
|
3
|
+
//#region src/export/svg/raster-object.ts
|
|
4
|
+
/**
|
|
5
|
+
* The largest multiplier that keeps a `width × height` area within the
|
|
6
|
+
* canvas limits. A picture over the browser's limit would fail, so a large
|
|
7
|
+
* page is drawn at a lower resolution instead: O(1) arithmetic.
|
|
8
|
+
*/
|
|
9
|
+
function safeMultiplier(multiplier, width, height, limits) {
|
|
10
|
+
const maxPixels = limits?.maxCanvasPixels ?? 67108864;
|
|
11
|
+
const maxSide = limits?.maxCanvasSide ?? 16384;
|
|
12
|
+
const byArea = Math.sqrt(maxPixels / Math.max(1, width * height));
|
|
13
|
+
const bySide = maxSide / Math.max(1, width, height);
|
|
14
|
+
return Math.min(multiplier, byArea, bySide);
|
|
15
|
+
}
|
|
16
|
+
function shadowPadding(object) {
|
|
17
|
+
let padding = 0;
|
|
18
|
+
walkObjects([object], (child) => {
|
|
19
|
+
const shadow = child.shadow;
|
|
20
|
+
if (shadow) padding = Math.max(padding, shadow.blur + Math.max(Math.abs(shadow.offsetX), Math.abs(shadow.offsetY)));
|
|
21
|
+
});
|
|
22
|
+
return padding + 2;
|
|
23
|
+
}
|
|
24
|
+
function usesBlendMode(object) {
|
|
25
|
+
let blends = false;
|
|
26
|
+
walkObjects([object], (child) => {
|
|
27
|
+
if (child.globalCompositeOperation && child.globalCompositeOperation !== "source-over") blends = true;
|
|
28
|
+
});
|
|
29
|
+
return blends;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* An object as a picture placed where it sits on the canvas. Usually the
|
|
33
|
+
* object alone, so the vectors underneath show through. An object with a
|
|
34
|
+
* blend mode mixes with what lies below it, so its picture includes
|
|
35
|
+
* everything below it within its bounds, which covers those vectors exactly.
|
|
36
|
+
*/
|
|
37
|
+
function rasterMarkup(canvas, object, multiplier, limits) {
|
|
38
|
+
object.setCoords();
|
|
39
|
+
const bounds = object.getBoundingRect();
|
|
40
|
+
const padding = shadowPadding(object);
|
|
41
|
+
const left = Math.max(0, Math.floor(bounds.left - padding));
|
|
42
|
+
const top = Math.max(0, Math.floor(bounds.top - padding));
|
|
43
|
+
const right = Math.min(canvas.getWidth(), Math.ceil(bounds.left + bounds.width + padding));
|
|
44
|
+
const bottom = Math.min(canvas.getHeight(), Math.ceil(bounds.top + bounds.height + padding));
|
|
45
|
+
if (right <= left || bottom <= top) return "";
|
|
46
|
+
const blends = usesBlendMode(object);
|
|
47
|
+
const stack = canvas.getObjects();
|
|
48
|
+
const position = stack.indexOf(object);
|
|
49
|
+
const saved = {
|
|
50
|
+
backgroundColor: canvas.backgroundColor,
|
|
51
|
+
backgroundImage: canvas.backgroundImage,
|
|
52
|
+
overlayColor: canvas.overlayColor,
|
|
53
|
+
overlayImage: canvas.overlayImage
|
|
54
|
+
};
|
|
55
|
+
if (!blends) {
|
|
56
|
+
canvas.backgroundColor = "";
|
|
57
|
+
canvas.backgroundImage = void 0;
|
|
58
|
+
}
|
|
59
|
+
canvas.overlayColor = "";
|
|
60
|
+
canvas.overlayImage = void 0;
|
|
61
|
+
let element;
|
|
62
|
+
try {
|
|
63
|
+
const filter = (candidate) => blends ? stack.indexOf(candidate) <= position : candidate === object;
|
|
64
|
+
const safe = safeMultiplier(multiplier, right - left, bottom - top, limits);
|
|
65
|
+
element = canvas.toCanvasElement(safe, {
|
|
66
|
+
left,
|
|
67
|
+
top,
|
|
68
|
+
width: right - left,
|
|
69
|
+
height: bottom - top,
|
|
70
|
+
filter
|
|
71
|
+
});
|
|
72
|
+
} finally {
|
|
73
|
+
Object.assign(canvas, saved);
|
|
74
|
+
}
|
|
75
|
+
const url = element.toDataURL("image/png");
|
|
76
|
+
element.width = 0;
|
|
77
|
+
element.height = 0;
|
|
78
|
+
return `<image x="${left}" y="${top}" width="${right - left}" height="${bottom - top}" preserveAspectRatio="none" xlink:href="${url}" />\n`;
|
|
79
|
+
}
|
|
80
|
+
//#endregion
|
|
81
|
+
export { rasterMarkup, safeMultiplier };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region src/
|
|
1
|
+
//#region src/export/svg/text-decorations.ts
|
|
2
2
|
const DECORATIONS = [
|
|
3
3
|
"underline",
|
|
4
4
|
"overline",
|
|
@@ -16,7 +16,7 @@ function isText(object) {
|
|
|
16
16
|
}
|
|
17
17
|
function hasStraightDecorations(object) {
|
|
18
18
|
if (!isText(object)) return false;
|
|
19
|
-
return DECORATIONS.some((type) => object[type] || object.styleHas(type));
|
|
19
|
+
return DECORATIONS.some((type) => object[type] || object.styleHas(type)) || object.styleHas("deltaY");
|
|
20
20
|
}
|
|
21
21
|
function rect(x, y, width, height, color) {
|
|
22
22
|
if (typeof color !== "string" || width <= 0 || height <= 0) return "";
|
|
@@ -90,7 +90,7 @@ function decorationRects(text, type) {
|
|
|
90
90
|
/** SVG for straight text with its decorations as boxes instead of CSS. */
|
|
91
91
|
function decoratedTextToSVG(object, reviver) {
|
|
92
92
|
const text = object;
|
|
93
|
-
const inner = text._toSVG().join("").replace(/\stext-decoration="[^"]*"/g, "").replace(/text-decoration:[^;"]*;?\s*/g, "");
|
|
93
|
+
const inner = text._toSVG().join("").replace(/\stext-decoration="[^"]*"/g, "").replace(/text-decoration:[^;"]*;?\s*/g, "").replace(/baseline-shift:[^;"]*;?\s*/g, "");
|
|
94
94
|
const markup = [
|
|
95
95
|
decorationRects(text, "underline"),
|
|
96
96
|
inner,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region src/
|
|
1
|
+
//#region src/export/svg/text-decorations.ts
|
|
2
2
|
const DECORATIONS = [
|
|
3
3
|
"underline",
|
|
4
4
|
"overline",
|
|
@@ -16,7 +16,7 @@ function isText(object) {
|
|
|
16
16
|
}
|
|
17
17
|
function hasStraightDecorations(object) {
|
|
18
18
|
if (!isText(object)) return false;
|
|
19
|
-
return DECORATIONS.some((type) => object[type] || object.styleHas(type));
|
|
19
|
+
return DECORATIONS.some((type) => object[type] || object.styleHas(type)) || object.styleHas("deltaY");
|
|
20
20
|
}
|
|
21
21
|
function rect(x, y, width, height, color) {
|
|
22
22
|
if (typeof color !== "string" || width <= 0 || height <= 0) return "";
|
|
@@ -90,7 +90,7 @@ function decorationRects(text, type) {
|
|
|
90
90
|
/** SVG for straight text with its decorations as boxes instead of CSS. */
|
|
91
91
|
function decoratedTextToSVG(object, reviver) {
|
|
92
92
|
const text = object;
|
|
93
|
-
const inner = text._toSVG().join("").replace(/\stext-decoration="[^"]*"/g, "").replace(/text-decoration:[^;"]*;?\s*/g, "");
|
|
93
|
+
const inner = text._toSVG().join("").replace(/\stext-decoration="[^"]*"/g, "").replace(/text-decoration:[^;"]*;?\s*/g, "").replace(/baseline-shift:[^;"]*;?\s*/g, "");
|
|
94
94
|
const markup = [
|
|
95
95
|
decorationRects(text, "underline"),
|
|
96
96
|
inner,
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
const require_walk_objects = require("../../fabric/walk-objects.cjs");
|
|
2
|
-
const require_overrides = require("./overrides.cjs");
|
|
3
2
|
let fabric = require("fabric");
|
|
4
3
|
//#region src/export/svg/text-on-path.ts
|
|
5
4
|
function isTextOnPath(object) {
|
|
@@ -230,14 +229,6 @@ function textOnPathToSVG(object, reviver) {
|
|
|
230
229
|
withShadow: true
|
|
231
230
|
});
|
|
232
231
|
}
|
|
233
|
-
/**
|
|
234
|
-
* Runs `work` while every text on a path on the canvas, including text in
|
|
235
|
-
* groups, writes the SVG from this module. Everything else keeps Fabric's
|
|
236
|
-
* SVG. Returns how many objects were handled.
|
|
237
|
-
*/
|
|
238
|
-
function withTextOnPathSVG(canvas, work) {
|
|
239
|
-
return require_overrides.withSvgOverrides(canvas, (object) => isTextOnPath(object) ? (reviver) => textOnPathToSVG(object, reviver) : void 0, work);
|
|
240
|
-
}
|
|
241
232
|
function findTextOnPath(canvas) {
|
|
242
233
|
const found = [];
|
|
243
234
|
require_walk_objects.walkObjects(canvas.getObjects(), (object) => {
|
|
@@ -249,4 +240,3 @@ function findTextOnPath(canvas) {
|
|
|
249
240
|
exports.findTextOnPath = findTextOnPath;
|
|
250
241
|
exports.isTextOnPath = isTextOnPath;
|
|
251
242
|
exports.textOnPathToSVG = textOnPathToSVG;
|
|
252
|
-
exports.withTextOnPathSVG = withTextOnPathSVG;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { walkObjects } from "../../fabric/walk-objects.js";
|
|
2
|
-
import { withSvgOverrides } from "./overrides.js";
|
|
3
2
|
import { Color, util } from "fabric";
|
|
4
3
|
//#region src/export/svg/text-on-path.ts
|
|
5
4
|
function isTextOnPath(object) {
|
|
@@ -230,14 +229,6 @@ function textOnPathToSVG(object, reviver) {
|
|
|
230
229
|
withShadow: true
|
|
231
230
|
});
|
|
232
231
|
}
|
|
233
|
-
/**
|
|
234
|
-
* Runs `work` while every text on a path on the canvas, including text in
|
|
235
|
-
* groups, writes the SVG from this module. Everything else keeps Fabric's
|
|
236
|
-
* SVG. Returns how many objects were handled.
|
|
237
|
-
*/
|
|
238
|
-
function withTextOnPathSVG(canvas, work) {
|
|
239
|
-
return withSvgOverrides(canvas, (object) => isTextOnPath(object) ? (reviver) => textOnPathToSVG(object, reviver) : void 0, work);
|
|
240
|
-
}
|
|
241
232
|
function findTextOnPath(canvas) {
|
|
242
233
|
const found = [];
|
|
243
234
|
walkObjects(canvas.getObjects(), (object) => {
|
|
@@ -246,4 +237,4 @@ function findTextOnPath(canvas) {
|
|
|
246
237
|
return found;
|
|
247
238
|
}
|
|
248
239
|
//#endregion
|
|
249
|
-
export { findTextOnPath, isTextOnPath, textOnPathToSVG
|
|
240
|
+
export { findTextOnPath, isTextOnPath, textOnPathToSVG };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const require_page_state = require("./page-state.cjs");
|
|
1
2
|
const require_concurrency = require("../util/concurrency.cjs");
|
|
2
3
|
const require_walk_objects = require("./walk-objects.cjs");
|
|
3
4
|
let fabric = require("fabric");
|
|
@@ -25,7 +26,7 @@ function serializeCanvas(canvas, propertiesToInclude) {
|
|
|
25
26
|
keepOriginExplicit(canvas.getObjects(), objects);
|
|
26
27
|
return {
|
|
27
28
|
version: serialized.version,
|
|
28
|
-
|
|
29
|
+
...require_page_state.pickPageFields(serialized),
|
|
29
30
|
objects
|
|
30
31
|
};
|
|
31
32
|
}
|
|
@@ -73,11 +74,11 @@ function abortError() {
|
|
|
73
74
|
* freeze the page. Like Fabric, every object is created before the canvas is
|
|
74
75
|
* cleared: a failure or cancel leaves the old content in place.
|
|
75
76
|
*/
|
|
76
|
-
async function loadIntoCanvas(canvas, content, { signal, chunkSize = 100, onObjects }) {
|
|
77
|
+
async function loadIntoCanvas(canvas, content, { signal, chunkSize = 100, onObjects, beforeSwap }) {
|
|
77
78
|
const total = content.objects.length;
|
|
78
79
|
const size = Math.max(1, Math.floor(chunkSize));
|
|
79
80
|
const created = [];
|
|
80
|
-
const background =
|
|
81
|
+
const background = require_page_state.enlivenPage(content, signal);
|
|
81
82
|
background.catch(() => void 0);
|
|
82
83
|
try {
|
|
83
84
|
onObjects?.(0, total);
|
|
@@ -93,6 +94,7 @@ async function loadIntoCanvas(canvas, content, { signal, chunkSize = 100, onObje
|
|
|
93
94
|
}
|
|
94
95
|
const enlivenedBackground = await background;
|
|
95
96
|
if (signal.aborted) throw abortError();
|
|
97
|
+
beforeSwap?.();
|
|
96
98
|
const renderOnAddRemove = canvas.renderOnAddRemove;
|
|
97
99
|
canvas.renderOnAddRemove = false;
|
|
98
100
|
try {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { enlivenPage, pickPageFields } from "./page-state.js";
|
|
1
2
|
import { yieldToEventLoop } from "../util/concurrency.js";
|
|
2
3
|
import { childrenOf, walkObjects } from "./walk-objects.js";
|
|
3
4
|
import { util } from "fabric";
|
|
@@ -25,7 +26,7 @@ function serializeCanvas(canvas, propertiesToInclude) {
|
|
|
25
26
|
keepOriginExplicit(canvas.getObjects(), objects);
|
|
26
27
|
return {
|
|
27
28
|
version: serialized.version,
|
|
28
|
-
|
|
29
|
+
...pickPageFields(serialized),
|
|
29
30
|
objects
|
|
30
31
|
};
|
|
31
32
|
}
|
|
@@ -73,11 +74,11 @@ function abortError() {
|
|
|
73
74
|
* freeze the page. Like Fabric, every object is created before the canvas is
|
|
74
75
|
* cleared: a failure or cancel leaves the old content in place.
|
|
75
76
|
*/
|
|
76
|
-
async function loadIntoCanvas(canvas, content, { signal, chunkSize = 100, onObjects }) {
|
|
77
|
+
async function loadIntoCanvas(canvas, content, { signal, chunkSize = 100, onObjects, beforeSwap }) {
|
|
77
78
|
const total = content.objects.length;
|
|
78
79
|
const size = Math.max(1, Math.floor(chunkSize));
|
|
79
80
|
const created = [];
|
|
80
|
-
const background =
|
|
81
|
+
const background = enlivenPage(content, signal);
|
|
81
82
|
background.catch(() => void 0);
|
|
82
83
|
try {
|
|
83
84
|
onObjects?.(0, total);
|
|
@@ -93,6 +94,7 @@ async function loadIntoCanvas(canvas, content, { signal, chunkSize = 100, onObje
|
|
|
93
94
|
}
|
|
94
95
|
const enlivenedBackground = await background;
|
|
95
96
|
if (signal.aborted) throw abortError();
|
|
97
|
+
beforeSwap?.();
|
|
96
98
|
const renderOnAddRemove = canvas.renderOnAddRemove;
|
|
97
99
|
canvas.renderOnAddRemove = false;
|
|
98
100
|
try {
|
|
@@ -2,7 +2,13 @@ const require_errors = require("../engine/errors.cjs");
|
|
|
2
2
|
let fabric = require("fabric");
|
|
3
3
|
//#region src/fabric/object-registry.ts
|
|
4
4
|
function createObjectRegistry(initialDefinitions = []) {
|
|
5
|
-
const extraProperties = /* @__PURE__ */ new Set([
|
|
5
|
+
const extraProperties = /* @__PURE__ */ new Set([
|
|
6
|
+
"id",
|
|
7
|
+
"name",
|
|
8
|
+
"svgId",
|
|
9
|
+
"svgClass",
|
|
10
|
+
"svgData"
|
|
11
|
+
]);
|
|
6
12
|
function register(definition) {
|
|
7
13
|
const { fabricClass } = definition;
|
|
8
14
|
if (typeof fabricClass?.type !== "string" || fabricClass.type.length === 0) throw new require_errors.DocumentEngineError("INVALID_CUSTOM_OBJECT", "A custom object class needs a static \"type\" string, for example: static type = \"Sticker\"");
|
|
@@ -2,7 +2,13 @@ import { DocumentEngineError } from "../engine/errors.js";
|
|
|
2
2
|
import { classRegistry } from "fabric";
|
|
3
3
|
//#region src/fabric/object-registry.ts
|
|
4
4
|
function createObjectRegistry(initialDefinitions = []) {
|
|
5
|
-
const extraProperties = /* @__PURE__ */ new Set([
|
|
5
|
+
const extraProperties = /* @__PURE__ */ new Set([
|
|
6
|
+
"id",
|
|
7
|
+
"name",
|
|
8
|
+
"svgId",
|
|
9
|
+
"svgClass",
|
|
10
|
+
"svgData"
|
|
11
|
+
]);
|
|
6
12
|
function register(definition) {
|
|
7
13
|
const { fabricClass } = definition;
|
|
8
14
|
if (typeof fabricClass?.type !== "string" || fabricClass.type.length === 0) throw new DocumentEngineError("INVALID_CUSTOM_OBJECT", "A custom object class needs a static \"type\" string, for example: static type = \"Sticker\"");
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
let fabric = require("fabric");
|
|
2
|
+
//#region src/fabric/page-state.ts
|
|
3
|
+
/** The page fields saved with a document, besides its size. */
|
|
4
|
+
const PAGE_FIELDS = [
|
|
5
|
+
"background",
|
|
6
|
+
"backgroundImage",
|
|
7
|
+
"overlay",
|
|
8
|
+
"overlayImage",
|
|
9
|
+
"clipPath"
|
|
10
|
+
];
|
|
11
|
+
/** Copies the page fields that are set from serialized canvas output. */
|
|
12
|
+
function pickPageFields(source) {
|
|
13
|
+
const picked = {};
|
|
14
|
+
for (const field of PAGE_FIELDS) if (source[field] !== void 0 && source[field] !== null) picked[field] = source[field];
|
|
15
|
+
return picked;
|
|
16
|
+
}
|
|
17
|
+
/** The canvas-level objects that can hold images or other objects: background and overlay images and the mask. */
|
|
18
|
+
function pageObjects(page) {
|
|
19
|
+
const objects = [];
|
|
20
|
+
for (const field of [
|
|
21
|
+
"backgroundImage",
|
|
22
|
+
"overlayImage",
|
|
23
|
+
"clipPath"
|
|
24
|
+
]) {
|
|
25
|
+
const value = page[field];
|
|
26
|
+
if (typeof value === "object" && value !== null && typeof value.type === "string") objects.push(value);
|
|
27
|
+
}
|
|
28
|
+
return objects;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Creates the live values for `canvas.set`: Fabric objects for the images
|
|
32
|
+
* and mask, gradients and patterns for colors. Missing fields become
|
|
33
|
+
* `undefined`, which clears them on the canvas.
|
|
34
|
+
*/
|
|
35
|
+
function enlivenPage(page, signal) {
|
|
36
|
+
return fabric.util.enlivenObjectEnlivables({
|
|
37
|
+
backgroundColor: page.background,
|
|
38
|
+
backgroundImage: page.backgroundImage,
|
|
39
|
+
overlayColor: page.overlay,
|
|
40
|
+
overlayImage: page.overlayImage,
|
|
41
|
+
clipPath: page.clipPath
|
|
42
|
+
}, signal ? { signal } : {});
|
|
43
|
+
}
|
|
44
|
+
/** Applies a page state: the size first, then the enlivened fields. */
|
|
45
|
+
async function applyPage(canvas, page) {
|
|
46
|
+
const live = await enlivenPage(page);
|
|
47
|
+
if (canvas.getWidth() !== page.width || canvas.getHeight() !== page.height) canvas.setDimensions({
|
|
48
|
+
width: page.width,
|
|
49
|
+
height: page.height
|
|
50
|
+
});
|
|
51
|
+
canvas.set(live);
|
|
52
|
+
canvas.requestRenderAll();
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Every top-level object a document holds: the objects on the page, then its
|
|
56
|
+
* background image, overlay image and mask. Asset checks, address checks and
|
|
57
|
+
* type checks walk this list, so they cover the page as well.
|
|
58
|
+
*/
|
|
59
|
+
function documentObjects(document) {
|
|
60
|
+
const page = document.canvas ? pageObjects(document.canvas) : [];
|
|
61
|
+
return page.length === 0 ? [...document.objects] : [...document.objects, ...page];
|
|
62
|
+
}
|
|
63
|
+
//#endregion
|
|
64
|
+
exports.PAGE_FIELDS = PAGE_FIELDS;
|
|
65
|
+
exports.applyPage = applyPage;
|
|
66
|
+
exports.documentObjects = documentObjects;
|
|
67
|
+
exports.enlivenPage = enlivenPage;
|
|
68
|
+
exports.pageObjects = pageObjects;
|
|
69
|
+
exports.pickPageFields = pickPageFields;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { util } from "fabric";
|
|
2
|
+
//#region src/fabric/page-state.ts
|
|
3
|
+
/** The page fields saved with a document, besides its size. */
|
|
4
|
+
const PAGE_FIELDS = [
|
|
5
|
+
"background",
|
|
6
|
+
"backgroundImage",
|
|
7
|
+
"overlay",
|
|
8
|
+
"overlayImage",
|
|
9
|
+
"clipPath"
|
|
10
|
+
];
|
|
11
|
+
/** Copies the page fields that are set from serialized canvas output. */
|
|
12
|
+
function pickPageFields(source) {
|
|
13
|
+
const picked = {};
|
|
14
|
+
for (const field of PAGE_FIELDS) if (source[field] !== void 0 && source[field] !== null) picked[field] = source[field];
|
|
15
|
+
return picked;
|
|
16
|
+
}
|
|
17
|
+
/** The canvas-level objects that can hold images or other objects: background and overlay images and the mask. */
|
|
18
|
+
function pageObjects(page) {
|
|
19
|
+
const objects = [];
|
|
20
|
+
for (const field of [
|
|
21
|
+
"backgroundImage",
|
|
22
|
+
"overlayImage",
|
|
23
|
+
"clipPath"
|
|
24
|
+
]) {
|
|
25
|
+
const value = page[field];
|
|
26
|
+
if (typeof value === "object" && value !== null && typeof value.type === "string") objects.push(value);
|
|
27
|
+
}
|
|
28
|
+
return objects;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Creates the live values for `canvas.set`: Fabric objects for the images
|
|
32
|
+
* and mask, gradients and patterns for colors. Missing fields become
|
|
33
|
+
* `undefined`, which clears them on the canvas.
|
|
34
|
+
*/
|
|
35
|
+
function enlivenPage(page, signal) {
|
|
36
|
+
return util.enlivenObjectEnlivables({
|
|
37
|
+
backgroundColor: page.background,
|
|
38
|
+
backgroundImage: page.backgroundImage,
|
|
39
|
+
overlayColor: page.overlay,
|
|
40
|
+
overlayImage: page.overlayImage,
|
|
41
|
+
clipPath: page.clipPath
|
|
42
|
+
}, signal ? { signal } : {});
|
|
43
|
+
}
|
|
44
|
+
/** Applies a page state: the size first, then the enlivened fields. */
|
|
45
|
+
async function applyPage(canvas, page) {
|
|
46
|
+
const live = await enlivenPage(page);
|
|
47
|
+
if (canvas.getWidth() !== page.width || canvas.getHeight() !== page.height) canvas.setDimensions({
|
|
48
|
+
width: page.width,
|
|
49
|
+
height: page.height
|
|
50
|
+
});
|
|
51
|
+
canvas.set(live);
|
|
52
|
+
canvas.requestRenderAll();
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Every top-level object a document holds: the objects on the page, then its
|
|
56
|
+
* background image, overlay image and mask. Asset checks, address checks and
|
|
57
|
+
* type checks walk this list, so they cover the page as well.
|
|
58
|
+
*/
|
|
59
|
+
function documentObjects(document) {
|
|
60
|
+
const page = document.canvas ? pageObjects(document.canvas) : [];
|
|
61
|
+
return page.length === 0 ? [...document.objects] : [...document.objects, ...page];
|
|
62
|
+
}
|
|
63
|
+
//#endregion
|
|
64
|
+
export { PAGE_FIELDS, applyPage, documentObjects, enlivenPage, pageObjects, pickPageFields };
|