fabricjs-document-engine 1.0.2 → 1.2.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.
Files changed (118) hide show
  1. package/README.md +220 -11
  2. package/README.zh-CN.md +220 -11
  3. package/dist/assets/asset-manifest.d.cts +3 -0
  4. package/dist/assets/asset-manifest.d.ts +3 -0
  5. package/dist/assets/asset-pipeline.cjs +31 -17
  6. package/dist/assets/asset-pipeline.d.cts +5 -1
  7. package/dist/assets/asset-pipeline.d.ts +5 -1
  8. package/dist/assets/asset-pipeline.js +31 -17
  9. package/dist/assets/font-check.cjs +9 -1
  10. package/dist/assets/font-check.js +9 -1
  11. package/dist/assets/image-check.cjs +89 -6
  12. package/dist/assets/image-check.d.cts +17 -0
  13. package/dist/assets/image-check.d.ts +17 -0
  14. package/dist/assets/image-check.js +89 -7
  15. package/dist/commands/clipboard.cjs +141 -0
  16. package/dist/commands/clipboard.d.cts +47 -0
  17. package/dist/commands/clipboard.d.ts +47 -0
  18. package/dist/commands/clipboard.js +140 -0
  19. package/dist/commands/layers.cjs +97 -0
  20. package/dist/commands/layers.d.cts +39 -0
  21. package/dist/commands/layers.d.ts +39 -0
  22. package/dist/commands/layers.js +92 -0
  23. package/dist/document/document-format.d.cts +15 -0
  24. package/dist/document/document-format.d.ts +15 -0
  25. package/dist/document/validate-document.cjs +8 -0
  26. package/dist/document/validate-document.js +8 -0
  27. package/dist/engine/create-document-engine.cjs +196 -31
  28. package/dist/engine/create-document-engine.d.cts +44 -7
  29. package/dist/engine/create-document-engine.d.ts +44 -7
  30. package/dist/engine/create-document-engine.js +197 -32
  31. package/dist/engine/errors.d.cts +1 -1
  32. package/dist/engine/errors.d.ts +1 -1
  33. package/dist/export/batch-render.cjs +172 -0
  34. package/dist/export/batch-render.d.cts +43 -0
  35. package/dist/export/batch-render.d.ts +43 -0
  36. package/dist/export/batch-render.js +171 -0
  37. package/dist/export/export-options.cjs +21 -1
  38. package/dist/export/export-options.d.cts +29 -0
  39. package/dist/export/export-options.d.ts +29 -0
  40. package/dist/export/export-options.js +21 -2
  41. package/dist/export/preflight-export.cjs +11 -2
  42. package/dist/export/preflight-export.d.cts +1 -1
  43. package/dist/export/preflight-export.d.ts +1 -1
  44. package/dist/export/preflight-export.js +11 -2
  45. package/dist/export/render-export.cjs +57 -4
  46. package/dist/export/render-export.js +57 -5
  47. package/dist/export/svg/clip-masks.cjs +39 -0
  48. package/dist/export/svg/clip-masks.js +37 -0
  49. package/dist/export/svg/embed-assets.cjs +250 -0
  50. package/dist/export/svg/embed-assets.js +250 -0
  51. package/dist/export/svg/overrides.cjs +32 -0
  52. package/dist/export/svg/overrides.js +32 -0
  53. package/dist/export/svg/raster-object.cjs +82 -0
  54. package/dist/export/svg/raster-object.js +81 -0
  55. package/dist/export/svg/text-decorations.cjs +108 -0
  56. package/dist/export/svg/text-decorations.js +107 -0
  57. package/dist/export/svg/text-on-path.cjs +242 -0
  58. package/dist/export/svg/text-on-path.js +240 -0
  59. package/dist/fabric/fabric-adapter.cjs +49 -6
  60. package/dist/fabric/fabric-adapter.js +49 -6
  61. package/dist/fabric/object-registry.cjs +1 -1
  62. package/dist/fabric/object-registry.js +1 -1
  63. package/dist/fabric/page-state.cjs +69 -0
  64. package/dist/fabric/page-state.js +64 -0
  65. package/dist/history/apply-state.cjs +19 -2
  66. package/dist/history/apply-state.js +19 -2
  67. package/dist/history/create-history.cjs +53 -13
  68. package/dist/history/create-history.d.cts +8 -0
  69. package/dist/history/create-history.d.ts +8 -0
  70. package/dist/history/create-history.js +53 -13
  71. package/dist/history/snapshot.cjs +10 -1
  72. package/dist/history/snapshot.js +10 -2
  73. package/dist/import/svg-import.cjs +256 -0
  74. package/dist/import/svg-import.d.cts +50 -0
  75. package/dist/import/svg-import.d.ts +50 -0
  76. package/dist/import/svg-import.js +256 -0
  77. package/dist/index.cjs +12 -0
  78. package/dist/index.d.cts +10 -5
  79. package/dist/index.d.ts +10 -5
  80. package/dist/index.js +4 -1
  81. package/dist/migrations/migrate-document.cjs +2 -1
  82. package/dist/migrations/migrate-document.js +2 -1
  83. package/dist/pdf/export-pdf.cjs +381 -0
  84. package/dist/pdf/export-pdf.d.cts +73 -0
  85. package/dist/pdf/export-pdf.d.ts +73 -0
  86. package/dist/pdf/export-pdf.js +381 -0
  87. package/dist/pdf/fonts.cjs +115 -0
  88. package/dist/pdf/fonts.d.cts +12 -0
  89. package/dist/pdf/fonts.d.ts +12 -0
  90. package/dist/pdf/fonts.js +112 -0
  91. package/dist/pdf/page-layout.cjs +79 -0
  92. package/dist/pdf/page-layout.d.cts +51 -0
  93. package/dist/pdf/page-layout.d.ts +51 -0
  94. package/dist/pdf/page-layout.js +76 -0
  95. package/dist/pdf.cjs +7 -0
  96. package/dist/pdf.d.cts +4 -0
  97. package/dist/pdf.d.ts +4 -0
  98. package/dist/pdf.js +3 -0
  99. package/dist/react/use-layers.cjs +53 -0
  100. package/dist/react/use-layers.d.cts +10 -0
  101. package/dist/react/use-layers.d.ts +10 -0
  102. package/dist/react/use-layers.js +53 -0
  103. package/dist/react.cjs +2 -0
  104. package/dist/react.d.cts +3 -1
  105. package/dist/react.d.ts +3 -1
  106. package/dist/react.js +2 -1
  107. package/dist/recovery/recovery-controller.cjs +148 -29
  108. package/dist/recovery/recovery-controller.d.cts +8 -0
  109. package/dist/recovery/recovery-controller.d.ts +8 -0
  110. package/dist/recovery/recovery-controller.js +148 -29
  111. package/dist/security/content-limits.cjs +43 -3
  112. package/dist/security/content-limits.d.cts +8 -0
  113. package/dist/security/content-limits.d.ts +8 -0
  114. package/dist/security/content-limits.js +39 -4
  115. package/dist/util/concurrency.cjs +41 -0
  116. package/dist/util/concurrency.js +40 -0
  117. package/package.json +30 -3
  118. package/schema/document-v1.schema.json +5 -1
@@ -0,0 +1,97 @@
1
+ const require_object_ids = require("../fabric/object-ids.cjs");
2
+ //#region src/commands/layers.ts
3
+ function chosenObjects(engine, objects) {
4
+ const list = objects ?? engine.canvas.getActiveObjects?.() ?? [];
5
+ const stack = new Set(engine.canvas.getObjects());
6
+ return new Set(list.filter((object) => stack.has(object)));
7
+ }
8
+ /**
9
+ * Works out the new order on the objects that may move, then puts every
10
+ * pinned object back at the index it had.
11
+ */
12
+ function reorder(engine, label, objects, options, arrange) {
13
+ const stack = engine.canvas.getObjects();
14
+ const isPinned = options.pinned ?? (() => false);
15
+ const chosen = chosenObjects(engine, objects);
16
+ for (const object of chosen) if (isPinned(object)) chosen.delete(object);
17
+ if (chosen.size === 0) return false;
18
+ const pinnedAt = /* @__PURE__ */ new Map();
19
+ const free = [];
20
+ stack.forEach((object, index) => {
21
+ if (isPinned(object)) pinnedAt.set(index, object);
22
+ else free.push(object);
23
+ });
24
+ const arranged = arrange(free, chosen);
25
+ const next = [];
26
+ let freeIndex = 0;
27
+ for (let index = 0; index < stack.length; index += 1) next.push(pinnedAt.get(index) ?? arranged[freeIndex++]);
28
+ if (next.every((object, index) => object === stack[index])) return false;
29
+ engine.transaction(chosen.size === 1 ? label : `${label} (${chosen.size} objects)`, () => {
30
+ next.forEach((object, index) => {
31
+ if (engine.canvas.getObjects()[index] !== object) engine.canvas.moveObjectTo(object, index);
32
+ });
33
+ });
34
+ engine.canvas.requestRenderAll();
35
+ return true;
36
+ }
37
+ /** Moves objects, or the selection, to the top of the stack. Returns false when nothing moved. */
38
+ function bringToFront(engine, objects, options = {}) {
39
+ return reorder(engine, "Bring to front", objects, options, (free, chosen) => [...free.filter((object) => !chosen.has(object)), ...free.filter((object) => chosen.has(object))]);
40
+ }
41
+ /** Moves objects, or the selection, to the bottom of the stack. Returns false when nothing moved. */
42
+ function sendToBack(engine, objects, options = {}) {
43
+ return reorder(engine, "Send to back", objects, options, (free, chosen) => [...free.filter((object) => chosen.has(object)), ...free.filter((object) => !chosen.has(object))]);
44
+ }
45
+ /** Moves objects, or the selection, one step up. Selected objects next to each other move as a block. */
46
+ function bringForward(engine, objects, options = {}) {
47
+ return reorder(engine, "Bring forward", objects, options, (free, chosen) => {
48
+ const next = [...free];
49
+ for (let index = next.length - 2; index >= 0; index -= 1) if (chosen.has(next[index]) && !chosen.has(next[index + 1])) [next[index], next[index + 1]] = [next[index + 1], next[index]];
50
+ return next;
51
+ });
52
+ }
53
+ /** Moves objects, or the selection, one step down. Selected objects next to each other move as a block. */
54
+ function sendBackward(engine, objects, options = {}) {
55
+ return reorder(engine, "Send backward", objects, options, (free, chosen) => {
56
+ const next = [...free];
57
+ for (let index = 1; index < next.length; index += 1) if (chosen.has(next[index]) && !chosen.has(next[index - 1])) [next[index], next[index - 1]] = [next[index - 1], next[index]];
58
+ return next;
59
+ });
60
+ }
61
+ /**
62
+ * Moves objects to `index`, counted from the bottom among objects that are not
63
+ * pinned. Several objects keep their order and land as a block.
64
+ */
65
+ function moveToIndex(engine, objects, index, options = {}) {
66
+ return reorder(engine, "Move layer", objects, options, (free, chosen) => {
67
+ const rest = free.filter((object) => !chosen.has(object));
68
+ const block = free.filter((object) => chosen.has(object));
69
+ const at = Math.max(0, Math.min(Math.floor(index), rest.length));
70
+ return [
71
+ ...rest.slice(0, at),
72
+ ...block,
73
+ ...rest.slice(at)
74
+ ];
75
+ });
76
+ }
77
+ /** The top-level objects from top to bottom, for a layers panel. */
78
+ function getLayers(engine) {
79
+ return engine.canvas.getObjects().map((object, index) => {
80
+ const name = object.name;
81
+ return {
82
+ id: require_object_ids.readObjectId(object),
83
+ type: object.type,
84
+ name: typeof name === "string" ? name : void 0,
85
+ index,
86
+ visible: object.visible !== false,
87
+ locked: object.selectable === false
88
+ };
89
+ }).reverse();
90
+ }
91
+ //#endregion
92
+ exports.bringForward = bringForward;
93
+ exports.bringToFront = bringToFront;
94
+ exports.getLayers = getLayers;
95
+ exports.moveToIndex = moveToIndex;
96
+ exports.sendBackward = sendBackward;
97
+ exports.sendToBack = sendToBack;
@@ -0,0 +1,39 @@
1
+ import { DocumentEngine } from "../engine/create-document-engine.cjs";
2
+ import { FabricObject } from "fabric";
3
+ //#region src/commands/layers.d.ts
4
+ /**
5
+ * Keeps an object where it is in the stack. Use it for a background that must
6
+ * stay at the bottom or a frame that must stay on top.
7
+ */
8
+ export type LayerPin = (object: FabricObject) => boolean;
9
+ export interface LayerOptions {
10
+ /** Objects this returns true for never move, and keep their place in the stack. */
11
+ pinned?: LayerPin;
12
+ }
13
+ export interface LayerInfo {
14
+ id: string | undefined;
15
+ type: string;
16
+ /** The object's `name` property, when it has one. */
17
+ name: string | undefined;
18
+ /** Position from the bottom of the stack, as Fabric counts it. */
19
+ index: number;
20
+ visible: boolean;
21
+ /** True when the object cannot be selected. */
22
+ locked: boolean;
23
+ }
24
+ /** Moves objects, or the selection, to the top of the stack. Returns false when nothing moved. */
25
+ export declare function bringToFront(engine: DocumentEngine, objects?: readonly FabricObject[], options?: LayerOptions): boolean;
26
+ /** Moves objects, or the selection, to the bottom of the stack. Returns false when nothing moved. */
27
+ export declare function sendToBack(engine: DocumentEngine, objects?: readonly FabricObject[], options?: LayerOptions): boolean;
28
+ /** Moves objects, or the selection, one step up. Selected objects next to each other move as a block. */
29
+ export declare function bringForward(engine: DocumentEngine, objects?: readonly FabricObject[], options?: LayerOptions): boolean;
30
+ /** Moves objects, or the selection, one step down. Selected objects next to each other move as a block. */
31
+ export declare function sendBackward(engine: DocumentEngine, objects?: readonly FabricObject[], options?: LayerOptions): boolean;
32
+ /**
33
+ * Moves objects to `index`, counted from the bottom among objects that are not
34
+ * pinned. Several objects keep their order and land as a block.
35
+ */
36
+ export declare function moveToIndex(engine: DocumentEngine, objects: readonly FabricObject[], index: number, options?: LayerOptions): boolean;
37
+ /** The top-level objects from top to bottom, for a layers panel. */
38
+ export declare function getLayers(engine: DocumentEngine): LayerInfo[];
39
+ //#endregion
@@ -0,0 +1,39 @@
1
+ import { DocumentEngine } from "../engine/create-document-engine.js";
2
+ import { FabricObject } from "fabric";
3
+ //#region src/commands/layers.d.ts
4
+ /**
5
+ * Keeps an object where it is in the stack. Use it for a background that must
6
+ * stay at the bottom or a frame that must stay on top.
7
+ */
8
+ export type LayerPin = (object: FabricObject) => boolean;
9
+ export interface LayerOptions {
10
+ /** Objects this returns true for never move, and keep their place in the stack. */
11
+ pinned?: LayerPin;
12
+ }
13
+ export interface LayerInfo {
14
+ id: string | undefined;
15
+ type: string;
16
+ /** The object's `name` property, when it has one. */
17
+ name: string | undefined;
18
+ /** Position from the bottom of the stack, as Fabric counts it. */
19
+ index: number;
20
+ visible: boolean;
21
+ /** True when the object cannot be selected. */
22
+ locked: boolean;
23
+ }
24
+ /** Moves objects, or the selection, to the top of the stack. Returns false when nothing moved. */
25
+ export declare function bringToFront(engine: DocumentEngine, objects?: readonly FabricObject[], options?: LayerOptions): boolean;
26
+ /** Moves objects, or the selection, to the bottom of the stack. Returns false when nothing moved. */
27
+ export declare function sendToBack(engine: DocumentEngine, objects?: readonly FabricObject[], options?: LayerOptions): boolean;
28
+ /** Moves objects, or the selection, one step up. Selected objects next to each other move as a block. */
29
+ export declare function bringForward(engine: DocumentEngine, objects?: readonly FabricObject[], options?: LayerOptions): boolean;
30
+ /** Moves objects, or the selection, one step down. Selected objects next to each other move as a block. */
31
+ export declare function sendBackward(engine: DocumentEngine, objects?: readonly FabricObject[], options?: LayerOptions): boolean;
32
+ /**
33
+ * Moves objects to `index`, counted from the bottom among objects that are not
34
+ * pinned. Several objects keep their order and land as a block.
35
+ */
36
+ export declare function moveToIndex(engine: DocumentEngine, objects: readonly FabricObject[], index: number, options?: LayerOptions): boolean;
37
+ /** The top-level objects from top to bottom, for a layers panel. */
38
+ export declare function getLayers(engine: DocumentEngine): LayerInfo[];
39
+ //#endregion
@@ -0,0 +1,92 @@
1
+ import { readObjectId } from "../fabric/object-ids.js";
2
+ //#region src/commands/layers.ts
3
+ function chosenObjects(engine, objects) {
4
+ const list = objects ?? engine.canvas.getActiveObjects?.() ?? [];
5
+ const stack = new Set(engine.canvas.getObjects());
6
+ return new Set(list.filter((object) => stack.has(object)));
7
+ }
8
+ /**
9
+ * Works out the new order on the objects that may move, then puts every
10
+ * pinned object back at the index it had.
11
+ */
12
+ function reorder(engine, label, objects, options, arrange) {
13
+ const stack = engine.canvas.getObjects();
14
+ const isPinned = options.pinned ?? (() => false);
15
+ const chosen = chosenObjects(engine, objects);
16
+ for (const object of chosen) if (isPinned(object)) chosen.delete(object);
17
+ if (chosen.size === 0) return false;
18
+ const pinnedAt = /* @__PURE__ */ new Map();
19
+ const free = [];
20
+ stack.forEach((object, index) => {
21
+ if (isPinned(object)) pinnedAt.set(index, object);
22
+ else free.push(object);
23
+ });
24
+ const arranged = arrange(free, chosen);
25
+ const next = [];
26
+ let freeIndex = 0;
27
+ for (let index = 0; index < stack.length; index += 1) next.push(pinnedAt.get(index) ?? arranged[freeIndex++]);
28
+ if (next.every((object, index) => object === stack[index])) return false;
29
+ engine.transaction(chosen.size === 1 ? label : `${label} (${chosen.size} objects)`, () => {
30
+ next.forEach((object, index) => {
31
+ if (engine.canvas.getObjects()[index] !== object) engine.canvas.moveObjectTo(object, index);
32
+ });
33
+ });
34
+ engine.canvas.requestRenderAll();
35
+ return true;
36
+ }
37
+ /** Moves objects, or the selection, to the top of the stack. Returns false when nothing moved. */
38
+ function bringToFront(engine, objects, options = {}) {
39
+ return reorder(engine, "Bring to front", objects, options, (free, chosen) => [...free.filter((object) => !chosen.has(object)), ...free.filter((object) => chosen.has(object))]);
40
+ }
41
+ /** Moves objects, or the selection, to the bottom of the stack. Returns false when nothing moved. */
42
+ function sendToBack(engine, objects, options = {}) {
43
+ return reorder(engine, "Send to back", objects, options, (free, chosen) => [...free.filter((object) => chosen.has(object)), ...free.filter((object) => !chosen.has(object))]);
44
+ }
45
+ /** Moves objects, or the selection, one step up. Selected objects next to each other move as a block. */
46
+ function bringForward(engine, objects, options = {}) {
47
+ return reorder(engine, "Bring forward", objects, options, (free, chosen) => {
48
+ const next = [...free];
49
+ for (let index = next.length - 2; index >= 0; index -= 1) if (chosen.has(next[index]) && !chosen.has(next[index + 1])) [next[index], next[index + 1]] = [next[index + 1], next[index]];
50
+ return next;
51
+ });
52
+ }
53
+ /** Moves objects, or the selection, one step down. Selected objects next to each other move as a block. */
54
+ function sendBackward(engine, objects, options = {}) {
55
+ return reorder(engine, "Send backward", objects, options, (free, chosen) => {
56
+ const next = [...free];
57
+ for (let index = 1; index < next.length; index += 1) if (chosen.has(next[index]) && !chosen.has(next[index - 1])) [next[index], next[index - 1]] = [next[index - 1], next[index]];
58
+ return next;
59
+ });
60
+ }
61
+ /**
62
+ * Moves objects to `index`, counted from the bottom among objects that are not
63
+ * pinned. Several objects keep their order and land as a block.
64
+ */
65
+ function moveToIndex(engine, objects, index, options = {}) {
66
+ return reorder(engine, "Move layer", objects, options, (free, chosen) => {
67
+ const rest = free.filter((object) => !chosen.has(object));
68
+ const block = free.filter((object) => chosen.has(object));
69
+ const at = Math.max(0, Math.min(Math.floor(index), rest.length));
70
+ return [
71
+ ...rest.slice(0, at),
72
+ ...block,
73
+ ...rest.slice(at)
74
+ ];
75
+ });
76
+ }
77
+ /** The top-level objects from top to bottom, for a layers panel. */
78
+ function getLayers(engine) {
79
+ return engine.canvas.getObjects().map((object, index) => {
80
+ const name = object.name;
81
+ return {
82
+ id: readObjectId(object),
83
+ type: object.type,
84
+ name: typeof name === "string" ? name : void 0,
85
+ index,
86
+ visible: object.visible !== false,
87
+ locked: object.selectable === false
88
+ };
89
+ }).reverse();
90
+ }
91
+ //#endregion
92
+ export { bringForward, bringToFront, getLayers, moveToIndex, sendBackward, sendToBack };
@@ -11,7 +11,16 @@ export interface SerializedFabricObject {
11
11
  export interface DocumentCanvas {
12
12
  width: number;
13
13
  height: number;
14
+ /** A color, gradient or pattern behind everything. */
14
15
  background?: unknown;
16
+ /** A Fabric image drawn behind every object. */
17
+ backgroundImage?: SerializedFabricObject;
18
+ /** A color, gradient or pattern drawn over every object. */
19
+ overlay?: unknown;
20
+ /** A Fabric image drawn over every object. */
21
+ overlayImage?: SerializedFabricObject;
22
+ /** A Fabric object that clips the whole canvas. */
23
+ clipPath?: SerializedFabricObject;
15
24
  }
16
25
  export interface FabricDocument {
17
26
  schemaVersion: number;
@@ -30,5 +39,11 @@ export interface DocumentInfo {
30
39
  createdAt: string;
31
40
  updatedAt: string;
32
41
  metadata: Record<string, unknown>;
42
+ /**
43
+ * Goes up each time the canvas shows a different document: a load, a new
44
+ * document or a restore. Async work started under one session must not
45
+ * change the canvas in another. Set by `getDocumentInfo()`.
46
+ */
47
+ session?: number;
33
48
  }
34
49
  //#endregion
@@ -11,7 +11,16 @@ export interface SerializedFabricObject {
11
11
  export interface DocumentCanvas {
12
12
  width: number;
13
13
  height: number;
14
+ /** A color, gradient or pattern behind everything. */
14
15
  background?: unknown;
16
+ /** A Fabric image drawn behind every object. */
17
+ backgroundImage?: SerializedFabricObject;
18
+ /** A color, gradient or pattern drawn over every object. */
19
+ overlay?: unknown;
20
+ /** A Fabric image drawn over every object. */
21
+ overlayImage?: SerializedFabricObject;
22
+ /** A Fabric object that clips the whole canvas. */
23
+ clipPath?: SerializedFabricObject;
15
24
  }
16
25
  export interface FabricDocument {
17
26
  schemaVersion: number;
@@ -30,5 +39,11 @@ export interface DocumentInfo {
30
39
  createdAt: string;
31
40
  updatedAt: string;
32
41
  metadata: Record<string, unknown>;
42
+ /**
43
+ * Goes up each time the canvas shows a different document: a load, a new
44
+ * document or a restore. Async work started under one session must not
45
+ * change the canvas in another. Set by `getDocumentInfo()`.
46
+ */
47
+ session?: number;
33
48
  }
34
49
  //#endregion
@@ -67,6 +67,14 @@ function validateDocument(value) {
67
67
  else {
68
68
  if (!isPositiveNumber(value.canvas.width)) issues.push(invalid("canvas.width", "must be a positive number"));
69
69
  if (!isPositiveNumber(value.canvas.height)) issues.push(invalid("canvas.height", "must be a positive number"));
70
+ for (const field of [
71
+ "backgroundImage",
72
+ "overlayImage",
73
+ "clipPath"
74
+ ]) {
75
+ const pageObject = value.canvas[field];
76
+ if (pageObject !== void 0 && pageObject !== null) validateObjectList([pageObject], `canvas.${field}`, issues);
77
+ }
70
78
  }
71
79
  validateObjectList(value.objects, "objects", issues);
72
80
  return issues;
@@ -67,6 +67,14 @@ function validateDocument(value) {
67
67
  else {
68
68
  if (!isPositiveNumber(value.canvas.width)) issues.push(invalid("canvas.width", "must be a positive number"));
69
69
  if (!isPositiveNumber(value.canvas.height)) issues.push(invalid("canvas.height", "must be a positive number"));
70
+ for (const field of [
71
+ "backgroundImage",
72
+ "overlayImage",
73
+ "clipPath"
74
+ ]) {
75
+ const pageObject = value.canvas[field];
76
+ if (pageObject !== void 0 && pageObject !== null) validateObjectList([pageObject], `canvas.${field}`, issues);
77
+ }
70
78
  }
71
79
  validateObjectList(value.objects, "objects", issues);
72
80
  return issues;