Bladeren bron

support embedding scene data to PNG/SVG (#2219)

Co-authored-by: Lipis <lipiridis@gmail.com>
vanilla_orig
David Luzar 4 jaren geleden
bovenliggende
commit
5950fa9a40
No account linked to committer's email address

+ 3
- 0
CHANGELOG.md Bestand weergeven

1
+## 2020-10-13
2
+
3
+- Added ability to embed scene source into exported PNG/SVG files so you can import the scene from them (open via `Load` button or drag & drop). #2219

+ 32
- 0
package-lock.json Bestand weergeven

5901
         }
5901
         }
5902
       }
5902
       }
5903
     },
5903
     },
5904
+    "crc-32": {
5905
+      "version": "0.3.0",
5906
+      "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-0.3.0.tgz",
5907
+      "integrity": "sha1-aj02h/W67EH36bmf4ZU6Ll0Zd14="
5908
+    },
5904
     "crc32-stream": {
5909
     "crc32-stream": {
5905
       "version": "3.0.1",
5910
       "version": "3.0.1",
5906
       "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-3.0.1.tgz",
5911
       "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-3.0.1.tgz",
17341
       "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz",
17346
       "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz",
17342
       "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA=="
17347
       "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA=="
17343
     },
17348
     },
17349
+    "png-chunk-text": {
17350
+      "version": "1.0.0",
17351
+      "resolved": "https://registry.npmjs.org/png-chunk-text/-/png-chunk-text-1.0.0.tgz",
17352
+      "integrity": "sha1-HGAG2ONLpHHTjhycVLP1PhCF4Y8="
17353
+    },
17354
+    "png-chunks-encode": {
17355
+      "version": "1.0.0",
17356
+      "resolved": "https://registry.npmjs.org/png-chunks-encode/-/png-chunks-encode-1.0.0.tgz",
17357
+      "integrity": "sha1-2epeNcru7XgmWMGre6+npe2xqHg=",
17358
+      "requires": {
17359
+        "crc-32": "^0.3.0",
17360
+        "sliced": "^1.0.1"
17361
+      }
17362
+    },
17363
+    "png-chunks-extract": {
17364
+      "version": "1.0.0",
17365
+      "resolved": "https://registry.npmjs.org/png-chunks-extract/-/png-chunks-extract-1.0.0.tgz",
17366
+      "integrity": "sha1-+tSpBeZmUhlzUcZeNbksZDEeRy0=",
17367
+      "requires": {
17368
+        "crc-32": "^0.3.0"
17369
+      }
17370
+    },
17344
     "pnp-webpack-plugin": {
17371
     "pnp-webpack-plugin": {
17345
       "version": "1.6.4",
17372
       "version": "1.6.4",
17346
       "resolved": "https://registry.npmjs.org/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.4.tgz",
17373
       "resolved": "https://registry.npmjs.org/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.4.tgz",
20125
         }
20152
         }
20126
       }
20153
       }
20127
     },
20154
     },
20155
+    "sliced": {
20156
+      "version": "1.0.1",
20157
+      "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz",
20158
+      "integrity": "sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E="
20159
+    },
20128
     "snapdragon": {
20160
     "snapdragon": {
20129
       "version": "0.8.2",
20161
       "version": "0.8.2",
20130
       "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
20162
       "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",

+ 3
- 0
package.json Bestand weergeven

35
     "nanoid": "2.1.11",
35
     "nanoid": "2.1.11",
36
     "node-sass": "4.14.1",
36
     "node-sass": "4.14.1",
37
     "open-color": "1.7.0",
37
     "open-color": "1.7.0",
38
+    "png-chunk-text": "1.0.0",
39
+    "png-chunks-encode": "1.0.0",
40
+    "png-chunks-extract": "1.0.0",
38
     "points-on-curve": "0.2.0",
41
     "points-on-curve": "0.2.0",
39
     "pwacompat": "2.0.17",
42
     "pwacompat": "2.0.17",
40
     "react": "16.13.1",
43
     "react": "16.13.1",

+ 20
- 0
src/actions/actionExport.tsx Bestand weergeven

43
   ),
43
   ),
44
 });
44
 });
45
 
45
 
46
+export const actionChangeExportEmbedScene = register({
47
+  name: "changeExportEmbedScene",
48
+  perform: (_elements, appState, value) => {
49
+    return {
50
+      appState: { ...appState, exportEmbedScene: value },
51
+      commitToHistory: false,
52
+    };
53
+  },
54
+  PanelComponent: ({ appState, updateData }) => (
55
+    <label title={t("labels.exportEmbedScene_details")}>
56
+      <input
57
+        type="checkbox"
58
+        checked={appState.exportEmbedScene}
59
+        onChange={(event) => updateData(event.target.checked)}
60
+      />{" "}
61
+      {t("labels.exportEmbedScene")}
62
+    </label>
63
+  ),
64
+});
65
+
46
 export const actionChangeShouldAddWatermark = register({
66
 export const actionChangeShouldAddWatermark = register({
47
   name: "changeShouldAddWatermark",
67
   name: "changeShouldAddWatermark",
48
   perform: (_elements, appState, value) => {
68
   perform: (_elements, appState, value) => {

+ 1
- 0
src/actions/types.ts Bestand weergeven

44
   | "finalize"
44
   | "finalize"
45
   | "changeProjectName"
45
   | "changeProjectName"
46
   | "changeExportBackground"
46
   | "changeExportBackground"
47
+  | "changeExportEmbedScene"
47
   | "changeShouldAddWatermark"
48
   | "changeShouldAddWatermark"
48
   | "saveScene"
49
   | "saveScene"
49
   | "saveAsScene"
50
   | "saveAsScene"

+ 2
- 0
src/appState.ts Bestand weergeven

25
     elementType: "selection",
25
     elementType: "selection",
26
     elementLocked: false,
26
     elementLocked: false,
27
     exportBackground: true,
27
     exportBackground: true,
28
+    exportEmbedScene: false,
28
     shouldAddWatermark: false,
29
     shouldAddWatermark: false,
29
     currentItemStrokeColor: oc.black,
30
     currentItemStrokeColor: oc.black,
30
     currentItemBackgroundColor: "transparent",
31
     currentItemBackgroundColor: "transparent",
112
   elementType: { browser: true, export: false },
113
   elementType: { browser: true, export: false },
113
   errorMessage: { browser: false, export: false },
114
   errorMessage: { browser: false, export: false },
114
   exportBackground: { browser: true, export: false },
115
   exportBackground: { browser: true, export: false },
116
+  exportEmbedScene: { browser: true, export: false },
115
   gridSize: { browser: true, export: true },
117
   gridSize: { browser: true, export: true },
116
   height: { browser: false, export: false },
118
   height: { browser: false, export: false },
117
   isBindingEnabled: { browser: false, export: false },
119
   isBindingEnabled: { browser: false, export: false },

+ 40
- 0
src/base64.ts Bestand weergeven

1
+// `btoa(unescape(encodeURIComponent(str)))` hack doesn't work in edge cases and
2
+// `unescape` API shouldn't be used anyway.
3
+// This implem is ~10x faster than using fromCharCode in a loop (in Chrome).
4
+const stringToByteString = (str: string): Promise<string> => {
5
+  return new Promise((resolve, reject) => {
6
+    const blob = new Blob([new TextEncoder().encode(str)]);
7
+    const reader = new FileReader();
8
+    reader.onload = function (event) {
9
+      if (!event.target || typeof event.target.result !== "string") {
10
+        return reject(new Error("couldn't convert to byte string"));
11
+      }
12
+      resolve(event.target.result);
13
+    };
14
+    reader.readAsBinaryString(blob);
15
+  });
16
+};
17
+
18
+function byteStringToArrayBuffer(byteString: string) {
19
+  const buffer = new ArrayBuffer(byteString.length);
20
+  const bufferView = new Uint8Array(buffer);
21
+  for (let i = 0, len = byteString.length; i < len; i++) {
22
+    bufferView[i] = byteString.charCodeAt(i);
23
+  }
24
+  return buffer;
25
+}
26
+
27
+const byteStringToString = (byteString: string) => {
28
+  return new TextDecoder("utf-8").decode(byteStringToArrayBuffer(byteString));
29
+};
30
+
31
+// -----------------------------------------------------------------------------
32
+
33
+export const stringToBase64 = async (str: string) => {
34
+  return btoa(await stringToByteString(str));
35
+};
36
+
37
+// async to align with stringToBase64
38
+export const base64ToString = async (base64: string) => {
39
+  return byteStringToString(atob(base64));
40
+};

+ 24
- 4
src/components/App.tsx Bestand weergeven

125
   DEFAULT_VERTICAL_ALIGN,
125
   DEFAULT_VERTICAL_ALIGN,
126
   GRID_SIZE,
126
   GRID_SIZE,
127
   LOCAL_STORAGE_KEY_COLLAB_FORCE_FLAG,
127
   LOCAL_STORAGE_KEY_COLLAB_FORCE_FLAG,
128
+  MIME_TYPES,
128
 } from "../constants";
129
 } from "../constants";
129
 import {
130
 import {
130
   INITIAL_SCENE_UPDATE_TIMEOUT,
131
   INITIAL_SCENE_UPDATE_TIMEOUT,
3788
   private handleCanvasOnDrop = async (
3789
   private handleCanvasOnDrop = async (
3789
     event: React.DragEvent<HTMLCanvasElement>,
3790
     event: React.DragEvent<HTMLCanvasElement>,
3790
   ) => {
3791
   ) => {
3791
-    const libraryShapes = event.dataTransfer.getData(
3792
-      "application/vnd.excalidrawlib+json",
3793
-    );
3792
+    try {
3793
+      const file = event.dataTransfer.files[0];
3794
+      if (file?.type === "image/png" || file?.type === "image/svg+xml") {
3795
+        const { elements, appState } = await loadFromBlob(file, this.state);
3796
+        this.syncActionResult({
3797
+          elements,
3798
+          appState: {
3799
+            ...(appState || this.state),
3800
+            isLoading: false,
3801
+          },
3802
+          commitToHistory: true,
3803
+        });
3804
+        return;
3805
+      }
3806
+    } catch (error) {
3807
+      return this.setState({
3808
+        isLoading: false,
3809
+        errorMessage: error.message,
3810
+      });
3811
+    }
3812
+
3813
+    const libraryShapes = event.dataTransfer.getData(MIME_TYPES.excalidraw);
3794
     if (libraryShapes !== "") {
3814
     if (libraryShapes !== "") {
3795
       this.addElementsFromPasteOrLibrary(
3815
       this.addElementsFromPasteOrLibrary(
3796
         JSON.parse(libraryShapes),
3816
         JSON.parse(libraryShapes),
3835
           this.setState({ isLoading: false, errorMessage: error.message });
3855
           this.setState({ isLoading: false, errorMessage: error.message });
3836
         });
3856
         });
3837
     } else if (
3857
     } else if (
3838
-      file?.type === "application/vnd.excalidrawlib+json" ||
3858
+      file?.type === MIME_TYPES.excalidrawlib ||
3839
       file?.name.endsWith(".excalidrawlib")
3859
       file?.name.endsWith(".excalidrawlib")
3840
     ) {
3860
     ) {
3841
       Library.importLibrary(file)
3861
       Library.importLibrary(file)

+ 1
- 0
src/components/ExportDialog.tsx Bestand weergeven

156
           </Stack.Row>
156
           </Stack.Row>
157
         </div>
157
         </div>
158
         {actionManager.renderAction("changeExportBackground")}
158
         {actionManager.renderAction("changeExportBackground")}
159
+        {actionManager.renderAction("changeExportEmbedScene")}
159
         {someElementIsSelected && (
160
         {someElementIsSelected && (
160
           <div>
161
           <div>
161
             <label>
162
             <label>

+ 2
- 1
src/components/LibraryUnit.tsx Bestand weergeven

6
 import { t } from "../i18n";
6
 import { t } from "../i18n";
7
 import useIsMobile from "../is-mobile";
7
 import useIsMobile from "../is-mobile";
8
 import { LibraryItem } from "../types";
8
 import { LibraryItem } from "../types";
9
+import { MIME_TYPES } from "../constants";
9
 
10
 
10
 // fa-plus
11
 // fa-plus
11
 const PLUS_ICON = (
12
 const PLUS_ICON = (
78
         onDragStart={(event) => {
79
         onDragStart={(event) => {
79
           setIsHovered(false);
80
           setIsHovered(false);
80
           event.dataTransfer.setData(
81
           event.dataTransfer.setData(
81
-            "application/vnd.excalidrawlib+json",
82
+            MIME_TYPES.excalidrawlib,
82
             JSON.stringify(elements),
83
             JSON.stringify(elements),
83
           );
84
           );
84
         }}
85
         }}

+ 5
- 0
src/constants.ts Bestand weergeven

84
 export const GRID_SIZE = 20; // TODO make it configurable?
84
 export const GRID_SIZE = 20; // TODO make it configurable?
85
 
85
 
86
 export const LOCAL_STORAGE_KEY_COLLAB_FORCE_FLAG = "collabLinkForceLoadFlag";
86
 export const LOCAL_STORAGE_KEY_COLLAB_FORCE_FLAG = "collabLinkForceLoadFlag";
87
+
88
+export const MIME_TYPES = {
89
+  excalidraw: "application/vnd.excalidraw+json",
90
+  excalidrawlib: "application/vnd.excalidrawlib+json",
91
+};

+ 45
- 14
src/data/blob.ts Bestand weergeven

4
 import { AppState } from "../types";
4
 import { AppState } from "../types";
5
 import { LibraryData, ImportedDataState } from "./types";
5
 import { LibraryData, ImportedDataState } from "./types";
6
 import { calculateScrollCenter } from "../scene";
6
 import { calculateScrollCenter } from "../scene";
7
+import { MIME_TYPES } from "../constants";
8
+import { base64ToString } from "../base64";
7
 
9
 
8
-const loadFileContents = async (blob: any) => {
10
+export const parseFileContents = async (blob: Blob | File) => {
9
   let contents: string;
11
   let contents: string;
10
-  if ("text" in Blob) {
11
-    contents = await blob.text();
12
+  if (blob.type === "image/png") {
13
+    const { default: decodePng } = await import("png-chunks-extract");
14
+    const { default: tEXt } = await import("png-chunk-text");
15
+    const chunks = decodePng(new Uint8Array(await blob.arrayBuffer()));
16
+
17
+    const metadataChunk = chunks.find((chunk) => chunk.name === "tEXt");
18
+    if (metadataChunk) {
19
+      const metadata = tEXt.decode(metadataChunk.data);
20
+      if (metadata.keyword === MIME_TYPES.excalidraw) {
21
+        return metadata.text;
22
+      }
23
+      throw new Error(t("alerts.imageDoesNotContainScene"));
24
+    } else {
25
+      throw new Error(t("alerts.imageDoesNotContainScene"));
26
+    }
12
   } else {
27
   } else {
13
-    contents = await new Promise((resolve) => {
14
-      const reader = new FileReader();
15
-      reader.readAsText(blob, "utf8");
16
-      reader.onloadend = () => {
17
-        if (reader.readyState === FileReader.DONE) {
18
-          resolve(reader.result as string);
28
+    if ("text" in Blob) {
29
+      contents = await blob.text();
30
+    } else {
31
+      contents = await new Promise((resolve) => {
32
+        const reader = new FileReader();
33
+        reader.readAsText(blob, "utf8");
34
+        reader.onloadend = () => {
35
+          if (reader.readyState === FileReader.DONE) {
36
+            resolve(reader.result as string);
37
+          }
38
+        };
39
+      });
40
+    }
41
+    if (blob.type === "image/svg+xml") {
42
+      if (contents.includes(`payload-type:${MIME_TYPES.excalidraw}`)) {
43
+        const match = contents.match(
44
+          /<!-- payload-start -->(.+?)<!-- payload-end -->/,
45
+        );
46
+        if (!match) {
47
+          throw new Error(t("alerts.imageDoesNotContainScene"));
19
         }
48
         }
20
-      };
21
-    });
49
+        return base64ToString(match[1]);
50
+      }
51
+      throw new Error(t("alerts.imageDoesNotContainScene"));
52
+    }
22
   }
53
   }
23
   return contents;
54
   return contents;
24
 };
55
 };
33
     (window as any).handle = blob.handle;
64
     (window as any).handle = blob.handle;
34
   }
65
   }
35
 
66
 
36
-  const contents = await loadFileContents(blob);
67
+  const contents = await parseFileContents(blob);
37
   try {
68
   try {
38
     const data: ImportedDataState = JSON.parse(contents);
69
     const data: ImportedDataState = JSON.parse(contents);
39
     if (data.type !== "excalidraw") {
70
     if (data.type !== "excalidraw") {
57
   }
88
   }
58
 };
89
 };
59
 
90
 
60
-export const loadLibraryFromBlob = async (blob: any) => {
61
-  const contents = await loadFileContents(blob);
91
+export const loadLibraryFromBlob = async (blob: Blob) => {
92
+  const contents = await parseFileContents(blob);
62
   const data: LibraryData = JSON.parse(contents);
93
   const data: LibraryData = JSON.parse(contents);
63
   if (data.type !== "excalidrawlib") {
94
   if (data.type !== "excalidrawlib") {
64
     throw new Error(t("alerts.couldNotLoadInvalidFile"));
95
     throw new Error(t("alerts.couldNotLoadInvalidFile"));

+ 27
- 1
src/data/index.ts Bestand weergeven

19
 import { ExportType } from "../scene/types";
19
 import { ExportType } from "../scene/types";
20
 import { restore } from "./restore";
20
 import { restore } from "./restore";
21
 import { ImportedDataState } from "./types";
21
 import { ImportedDataState } from "./types";
22
+import { MIME_TYPES } from "../constants";
23
+import { stringToBase64 } from "../base64";
22
 
24
 
23
 export { loadFromBlob } from "./blob";
25
 export { loadFromBlob } from "./blob";
24
 export { saveAsJSON, loadFromJSON } from "./json";
26
 export { saveAsJSON, loadFromJSON } from "./json";
300
     return window.alert(t("alerts.cannotExportEmptyCanvas"));
302
     return window.alert(t("alerts.cannotExportEmptyCanvas"));
301
   }
303
   }
302
   if (type === "svg" || type === "clipboard-svg") {
304
   if (type === "svg" || type === "clipboard-svg") {
305
+    let metadata = "";
306
+
307
+    if (appState.exportEmbedScene && type === "svg") {
308
+      metadata += `<!-- payload-type:${MIME_TYPES.excalidraw} -->`;
309
+      metadata += "<!-- payload-start -->";
310
+      metadata += await stringToBase64(serializeAsJSON(elements, appState));
311
+      metadata += "<!-- payload-end -->";
312
+    }
313
+
303
     const tempSvg = exportToSvg(elements, {
314
     const tempSvg = exportToSvg(elements, {
304
       exportBackground,
315
       exportBackground,
305
       viewBackgroundColor,
316
       viewBackgroundColor,
306
       exportPadding,
317
       exportPadding,
307
       shouldAddWatermark,
318
       shouldAddWatermark,
319
+      metadata,
308
     });
320
     });
309
     if (type === "svg") {
321
     if (type === "svg") {
310
       await fileSave(new Blob([tempSvg.outerHTML], { type: "image/svg+xml" }), {
322
       await fileSave(new Blob([tempSvg.outerHTML], { type: "image/svg+xml" }), {
330
 
342
 
331
   if (type === "png") {
343
   if (type === "png") {
332
     const fileName = `${name}.png`;
344
     const fileName = `${name}.png`;
333
-    tempCanvas.toBlob(async (blob: any) => {
345
+    tempCanvas.toBlob(async (blob) => {
334
       if (blob) {
346
       if (blob) {
347
+        if (appState.exportEmbedScene) {
348
+          const { default: tEXt } = await import("png-chunk-text");
349
+          const { default: encodePng } = await import("png-chunks-encode");
350
+          const { default: decodePng } = await import("png-chunks-extract");
351
+          const chunks = decodePng(new Uint8Array(await blob.arrayBuffer()));
352
+          const metadata = tEXt.encode(
353
+            MIME_TYPES.excalidraw,
354
+            serializeAsJSON(elements, appState),
355
+          );
356
+          // insert metadata before last chunk (iEND)
357
+          chunks.splice(-1, 0, metadata);
358
+          blob = new Blob([encodePng(chunks)], { type: "image/png" });
359
+        }
360
+
335
         await fileSave(blob, {
361
         await fileSave(blob, {
336
           fileName: fileName,
362
           fileName: fileName,
337
           extensions: [".png"],
363
           extensions: [".png"],

+ 4
- 3
src/data/json.ts Bestand weergeven

6
 import { loadFromBlob } from "./blob";
6
 import { loadFromBlob } from "./blob";
7
 import { loadLibrary } from "./localStorage";
7
 import { loadLibrary } from "./localStorage";
8
 import { Library } from "./library";
8
 import { Library } from "./library";
9
+import { MIME_TYPES } from "../constants";
9
 
10
 
10
 export const serializeAsJSON = (
11
 export const serializeAsJSON = (
11
   elements: readonly ExcalidrawElement[],
12
   elements: readonly ExcalidrawElement[],
48
 export const loadFromJSON = async (localAppState: AppState) => {
49
 export const loadFromJSON = async (localAppState: AppState) => {
49
   const blob = await fileOpen({
50
   const blob = await fileOpen({
50
     description: "Excalidraw files",
51
     description: "Excalidraw files",
51
-    extensions: [".json", ".excalidraw"],
52
-    mimeTypes: ["application/json"],
52
+    extensions: [".json", ".excalidraw", ".png", ".svg"],
53
+    mimeTypes: ["application/json", "image/png", "image/svg+xml"],
53
   });
54
   });
54
   return loadFromBlob(blob, localAppState);
55
   return loadFromBlob(blob, localAppState);
55
 };
56
 };
76
   );
77
   );
77
   const fileName = "library.excalidrawlib";
78
   const fileName = "library.excalidrawlib";
78
   const blob = new Blob([serialized], {
79
   const blob = new Blob([serialized], {
79
-    type: "application/vnd.excalidrawlib+json",
80
+    type: MIME_TYPES.excalidrawlib,
80
   });
81
   });
81
   await fileSave(blob, {
82
   await fileSave(blob, {
82
     fileName,
83
     fileName,

+ 22
- 0
src/global.d.ts Bestand weergeven

41
 // https://github.com/krzkaczor/ts-essentials
41
 // https://github.com/krzkaczor/ts-essentials
42
 type MarkOptional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
42
 type MarkOptional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
43
 
43
 
44
+// PNG encoding/decoding
45
+// -----------------------------------------------------------------------------
46
+type TEXtChunk = { name: "tEXt"; data: Uint8Array };
47
+
48
+declare module "png-chunk-text" {
49
+  function encode(
50
+    name: string,
51
+    value: string,
52
+  ): { name: "tEXt"; data: Uint8Array };
53
+  function decode(data: Uint8Array): { keyword: string; text: string };
54
+}
55
+declare module "png-chunks-encode" {
56
+  function encode(chunks: TEXtChunk[]): Uint8Array;
57
+  export = encode;
58
+}
59
+declare module "png-chunks-extract" {
60
+  function extract(buffer: Uint8Array): TEXtChunk[];
61
+  export = extract;
62
+}
63
+// -----------------------------------------------------------------------------
64
+
65
+// -----------------------------------------------------------------------------
44
 // type getter for interface's callable type
66
 // type getter for interface's callable type
45
 // src: https://stackoverflow.com/a/58658851/927631
67
 // src: https://stackoverflow.com/a/58658851/927631
46
 // -----------------------------------------------------------------------------
68
 // -----------------------------------------------------------------------------

+ 1
- 1
src/index-node.ts Bestand weergeven

75
 
75
 
76
 const fs = require("fs");
76
 const fs = require("fs");
77
 const out = fs.createWriteStream("test.png");
77
 const out = fs.createWriteStream("test.png");
78
-const stream = canvas.createPNGStream();
78
+const stream = (canvas as any).createPNGStream();
79
 stream.pipe(out);
79
 stream.pipe(out);
80
 out.on("finish", () => {
80
 out.on("finish", () => {
81
   console.info("test.png was created.");
81
   console.info("test.png was created.");

+ 5
- 1
src/locales/en.json Bestand weergeven

32
     "fontFamily": "Font family",
32
     "fontFamily": "Font family",
33
     "onlySelected": "Only selected",
33
     "onlySelected": "Only selected",
34
     "withBackground": "With Background",
34
     "withBackground": "With Background",
35
+    "exportEmbedScene": "Embed scene into exported file",
36
+    "exportEmbedScene_details": "Scene data will be saved into the exported PNG/SVG file so that the scene can be restored from it.\nWill increase exported file size.",
35
     "addWatermark": "Add \"Made with Excalidraw\"",
37
     "addWatermark": "Add \"Made with Excalidraw\"",
36
     "handDrawn": "Hand-drawn",
38
     "handDrawn": "Hand-drawn",
37
     "normal": "Normal",
39
     "normal": "Normal",
115
     "uploadedSecurly": "The upload has been secured with end-to-end encryption, which means that Excalidraw server and third parties can't read the content.",
117
     "uploadedSecurly": "The upload has been secured with end-to-end encryption, which means that Excalidraw server and third parties can't read the content.",
116
     "loadSceneOverridePrompt": "Loading external drawing will replace your existing content. Do you wish to continue?",
118
     "loadSceneOverridePrompt": "Loading external drawing will replace your existing content. Do you wish to continue?",
117
     "errorLoadingLibrary": "There was an error loading the third party library.",
119
     "errorLoadingLibrary": "There was an error loading the third party library.",
118
-    "confirmAddLibrary": "This will add {{numShapes}} shape(s) to your library. Are you sure?"
120
+    "confirmAddLibrary": "This will add {{numShapes}} shape(s) to your library. Are you sure?",
121
+    "imageDoesNotContainScene": "Image file doesn't contain scene data. Have you enabled this during export?",
122
+    "cannotRestoreFromImage": "Scene couldn't be restored from this image file"
119
   },
123
   },
120
   "toolBar": {
124
   "toolBar": {
121
     "selection": "Selection",
125
     "selection": "Selection",

+ 8
- 2
src/scene/export.ts Bestand weergeven

29
     viewBackgroundColor: string;
29
     viewBackgroundColor: string;
30
     shouldAddWatermark: boolean;
30
     shouldAddWatermark: boolean;
31
   },
31
   },
32
-  createCanvas: (width: number, height: number) => any = (width, height) => {
32
+  createCanvas: (width: number, height: number) => HTMLCanvasElement = (
33
+    width,
34
+    height,
35
+  ) => {
33
     const tempCanvas = document.createElement("canvas");
36
     const tempCanvas = document.createElement("canvas");
34
     tempCanvas.width = width * scale;
37
     tempCanvas.width = width * scale;
35
     tempCanvas.height = height * scale;
38
     tempCanvas.height = height * scale;
44
     shouldAddWatermark,
47
     shouldAddWatermark,
45
   );
48
   );
46
 
49
 
47
-  const tempCanvas: any = createCanvas(width, height);
50
+  const tempCanvas = createCanvas(width, height);
48
 
51
 
49
   renderScene(
52
   renderScene(
50
     sceneElements,
53
     sceneElements,
81
     exportPadding = 10,
84
     exportPadding = 10,
82
     viewBackgroundColor,
85
     viewBackgroundColor,
83
     shouldAddWatermark,
86
     shouldAddWatermark,
87
+    metadata = "",
84
   }: {
88
   }: {
85
     exportBackground: boolean;
89
     exportBackground: boolean;
86
     exportPadding?: number;
90
     exportPadding?: number;
87
     viewBackgroundColor: string;
91
     viewBackgroundColor: string;
88
     shouldAddWatermark: boolean;
92
     shouldAddWatermark: boolean;
93
+    metadata?: string;
89
   },
94
   },
90
 ): SVGSVGElement => {
95
 ): SVGSVGElement => {
91
   const sceneElements = getElementsAndWatermark(elements, shouldAddWatermark);
96
   const sceneElements = getElementsAndWatermark(elements, shouldAddWatermark);
104
 
109
 
105
   svgRoot.innerHTML = `
110
   svgRoot.innerHTML = `
106
   ${SVG_EXPORT_TAG}
111
   ${SVG_EXPORT_TAG}
112
+  ${metadata}
107
   <defs>
113
   <defs>
108
     <style>
114
     <style>
109
       @font-face {
115
       @font-face {

+ 83
- 0
src/tests/__snapshots__/regressionTests.test.tsx.snap Bestand weergeven

27
   "elementType": "selection",
27
   "elementType": "selection",
28
   "errorMessage": null,
28
   "errorMessage": null,
29
   "exportBackground": true,
29
   "exportBackground": true,
30
+  "exportEmbedScene": false,
30
   "gridSize": null,
31
   "gridSize": null,
31
   "height": 768,
32
   "height": 768,
32
   "isBindingEnabled": true,
33
   "isBindingEnabled": true,
473
   "elementType": "selection",
474
   "elementType": "selection",
474
   "errorMessage": null,
475
   "errorMessage": null,
475
   "exportBackground": true,
476
   "exportBackground": true,
477
+  "exportEmbedScene": false,
476
   "gridSize": null,
478
   "gridSize": null,
477
   "height": 768,
479
   "height": 768,
478
   "isBindingEnabled": true,
480
   "isBindingEnabled": true,
925
   "elementType": "selection",
927
   "elementType": "selection",
926
   "errorMessage": null,
928
   "errorMessage": null,
927
   "exportBackground": true,
929
   "exportBackground": true,
930
+  "exportEmbedScene": false,
928
   "gridSize": null,
931
   "gridSize": null,
929
   "height": 768,
932
   "height": 768,
930
   "isBindingEnabled": false,
933
   "isBindingEnabled": false,
1686
   "elementType": "selection",
1689
   "elementType": "selection",
1687
   "errorMessage": null,
1690
   "errorMessage": null,
1688
   "exportBackground": true,
1691
   "exportBackground": true,
1692
+  "exportEmbedScene": false,
1689
   "gridSize": null,
1693
   "gridSize": null,
1690
   "height": 768,
1694
   "height": 768,
1691
   "isBindingEnabled": true,
1695
   "isBindingEnabled": true,
1875
   "elementType": "selection",
1879
   "elementType": "selection",
1876
   "errorMessage": null,
1880
   "errorMessage": null,
1877
   "exportBackground": true,
1881
   "exportBackground": true,
1882
+  "exportEmbedScene": false,
1878
   "gridSize": null,
1883
   "gridSize": null,
1879
   "height": 768,
1884
   "height": 768,
1880
   "isBindingEnabled": true,
1885
   "isBindingEnabled": true,
2318
   "elementType": "selection",
2323
   "elementType": "selection",
2319
   "errorMessage": null,
2324
   "errorMessage": null,
2320
   "exportBackground": true,
2325
   "exportBackground": true,
2326
+  "exportEmbedScene": false,
2321
   "gridSize": null,
2327
   "gridSize": null,
2322
   "height": 768,
2328
   "height": 768,
2323
   "isBindingEnabled": true,
2329
   "isBindingEnabled": true,
2556
   "elementType": "selection",
2562
   "elementType": "selection",
2557
   "errorMessage": null,
2563
   "errorMessage": null,
2558
   "exportBackground": true,
2564
   "exportBackground": true,
2565
+  "exportEmbedScene": false,
2559
   "gridSize": null,
2566
   "gridSize": null,
2560
   "height": 768,
2567
   "height": 768,
2561
   "isBindingEnabled": true,
2568
   "isBindingEnabled": true,
2705
   "elementType": "selection",
2712
   "elementType": "selection",
2706
   "errorMessage": null,
2713
   "errorMessage": null,
2707
   "exportBackground": true,
2714
   "exportBackground": true,
2715
+  "exportEmbedScene": false,
2708
   "gridSize": null,
2716
   "gridSize": null,
2709
   "height": 768,
2717
   "height": 768,
2710
   "isBindingEnabled": true,
2718
   "isBindingEnabled": true,
3167
   "elementType": "selection",
3175
   "elementType": "selection",
3168
   "errorMessage": null,
3176
   "errorMessage": null,
3169
   "exportBackground": true,
3177
   "exportBackground": true,
3178
+  "exportEmbedScene": false,
3170
   "gridSize": null,
3179
   "gridSize": null,
3171
   "height": 768,
3180
   "height": 768,
3172
   "isBindingEnabled": true,
3181
   "isBindingEnabled": true,
3460
   "elementType": "selection",
3469
   "elementType": "selection",
3461
   "errorMessage": null,
3470
   "errorMessage": null,
3462
   "exportBackground": true,
3471
   "exportBackground": true,
3472
+  "exportEmbedScene": false,
3463
   "gridSize": null,
3473
   "gridSize": null,
3464
   "height": 768,
3474
   "height": 768,
3465
   "isBindingEnabled": true,
3475
   "isBindingEnabled": true,
3649
   "elementType": "selection",
3659
   "elementType": "selection",
3650
   "errorMessage": null,
3660
   "errorMessage": null,
3651
   "exportBackground": true,
3661
   "exportBackground": true,
3662
+  "exportEmbedScene": false,
3652
   "gridSize": null,
3663
   "gridSize": null,
3653
   "height": 768,
3664
   "height": 768,
3654
   "isBindingEnabled": true,
3665
   "isBindingEnabled": true,
3878
   "elementType": "selection",
3889
   "elementType": "selection",
3879
   "errorMessage": null,
3890
   "errorMessage": null,
3880
   "exportBackground": true,
3891
   "exportBackground": true,
3892
+  "exportEmbedScene": false,
3881
   "gridSize": null,
3893
   "gridSize": null,
3882
   "height": 768,
3894
   "height": 768,
3883
   "isBindingEnabled": true,
3895
   "isBindingEnabled": true,
4115
   "elementType": "selection",
4127
   "elementType": "selection",
4116
   "errorMessage": null,
4128
   "errorMessage": null,
4117
   "exportBackground": true,
4129
   "exportBackground": true,
4130
+  "exportEmbedScene": false,
4118
   "gridSize": null,
4131
   "gridSize": null,
4119
   "height": 768,
4132
   "height": 768,
4120
   "isBindingEnabled": true,
4133
   "isBindingEnabled": true,
4483
   "elementType": "selection",
4496
   "elementType": "selection",
4484
   "errorMessage": null,
4497
   "errorMessage": null,
4485
   "exportBackground": true,
4498
   "exportBackground": true,
4499
+  "exportEmbedScene": false,
4486
   "gridSize": null,
4500
   "gridSize": null,
4487
   "height": 768,
4501
   "height": 768,
4488
   "isBindingEnabled": true,
4502
   "isBindingEnabled": true,
4763
   "elementType": "selection",
4777
   "elementType": "selection",
4764
   "errorMessage": null,
4778
   "errorMessage": null,
4765
   "exportBackground": true,
4779
   "exportBackground": true,
4780
+  "exportEmbedScene": false,
4766
   "gridSize": null,
4781
   "gridSize": null,
4767
   "height": 768,
4782
   "height": 768,
4768
   "isBindingEnabled": true,
4783
   "isBindingEnabled": true,
5055
   "elementType": "selection",
5070
   "elementType": "selection",
5056
   "errorMessage": null,
5071
   "errorMessage": null,
5057
   "exportBackground": true,
5072
   "exportBackground": true,
5073
+  "exportEmbedScene": false,
5058
   "gridSize": null,
5074
   "gridSize": null,
5059
   "height": 768,
5075
   "height": 768,
5060
   "isBindingEnabled": true,
5076
   "isBindingEnabled": true,
5248
   "elementType": "selection",
5264
   "elementType": "selection",
5249
   "errorMessage": null,
5265
   "errorMessage": null,
5250
   "exportBackground": true,
5266
   "exportBackground": true,
5267
+  "exportEmbedScene": false,
5251
   "gridSize": null,
5268
   "gridSize": null,
5252
   "height": 768,
5269
   "height": 768,
5253
   "isBindingEnabled": true,
5270
   "isBindingEnabled": true,
5397
   "elementType": "selection",
5414
   "elementType": "selection",
5398
   "errorMessage": null,
5415
   "errorMessage": null,
5399
   "exportBackground": true,
5416
   "exportBackground": true,
5417
+  "exportEmbedScene": false,
5400
   "gridSize": null,
5418
   "gridSize": null,
5401
   "height": 768,
5419
   "height": 768,
5402
   "isBindingEnabled": true,
5420
   "isBindingEnabled": true,
5835
   "elementType": "selection",
5853
   "elementType": "selection",
5836
   "errorMessage": null,
5854
   "errorMessage": null,
5837
   "exportBackground": true,
5855
   "exportBackground": true,
5856
+  "exportEmbedScene": false,
5838
   "gridSize": null,
5857
   "gridSize": null,
5839
   "height": 768,
5858
   "height": 768,
5840
   "isBindingEnabled": true,
5859
   "isBindingEnabled": true,
6138
   "elementType": "selection",
6157
   "elementType": "selection",
6139
   "errorMessage": null,
6158
   "errorMessage": null,
6140
   "exportBackground": true,
6159
   "exportBackground": true,
6160
+  "exportEmbedScene": false,
6141
   "gridSize": null,
6161
   "gridSize": null,
6142
   "height": 768,
6162
   "height": 768,
6143
   "isBindingEnabled": true,
6163
   "isBindingEnabled": true,
8103
   "elementType": "selection",
8123
   "elementType": "selection",
8104
   "errorMessage": null,
8124
   "errorMessage": null,
8105
   "exportBackground": true,
8125
   "exportBackground": true,
8126
+  "exportEmbedScene": false,
8106
   "gridSize": null,
8127
   "gridSize": null,
8107
   "height": 768,
8128
   "height": 768,
8108
   "isBindingEnabled": true,
8129
   "isBindingEnabled": true,
8450
   "elementType": "selection",
8471
   "elementType": "selection",
8451
   "errorMessage": null,
8472
   "errorMessage": null,
8452
   "exportBackground": true,
8473
   "exportBackground": true,
8474
+  "exportEmbedScene": false,
8453
   "gridSize": null,
8475
   "gridSize": null,
8454
   "height": 768,
8476
   "height": 768,
8455
   "isBindingEnabled": true,
8477
   "isBindingEnabled": true,
8690
   "elementType": "selection",
8712
   "elementType": "selection",
8691
   "errorMessage": null,
8713
   "errorMessage": null,
8692
   "exportBackground": true,
8714
   "exportBackground": true,
8715
+  "exportEmbedScene": false,
8693
   "gridSize": null,
8716
   "gridSize": null,
8694
   "height": 768,
8717
   "height": 768,
8695
   "isBindingEnabled": true,
8718
   "isBindingEnabled": true,
8928
   "elementType": "selection",
8951
   "elementType": "selection",
8929
   "errorMessage": null,
8952
   "errorMessage": null,
8930
   "exportBackground": true,
8953
   "exportBackground": true,
8954
+  "exportEmbedScene": false,
8931
   "gridSize": null,
8955
   "gridSize": null,
8932
   "height": 768,
8956
   "height": 768,
8933
   "isBindingEnabled": true,
8957
   "isBindingEnabled": true,
9228
   "elementType": "selection",
9252
   "elementType": "selection",
9229
   "errorMessage": null,
9253
   "errorMessage": null,
9230
   "exportBackground": true,
9254
   "exportBackground": true,
9255
+  "exportEmbedScene": false,
9231
   "gridSize": null,
9256
   "gridSize": null,
9232
   "height": 768,
9257
   "height": 768,
9233
   "isBindingEnabled": true,
9258
   "isBindingEnabled": true,
9377
   "elementType": "selection",
9402
   "elementType": "selection",
9378
   "errorMessage": null,
9403
   "errorMessage": null,
9379
   "exportBackground": true,
9404
   "exportBackground": true,
9405
+  "exportEmbedScene": false,
9380
   "gridSize": null,
9406
   "gridSize": null,
9381
   "height": 768,
9407
   "height": 768,
9382
   "isBindingEnabled": true,
9408
   "isBindingEnabled": true,
9526
   "elementType": "selection",
9552
   "elementType": "selection",
9527
   "errorMessage": null,
9553
   "errorMessage": null,
9528
   "exportBackground": true,
9554
   "exportBackground": true,
9555
+  "exportEmbedScene": false,
9529
   "gridSize": null,
9556
   "gridSize": null,
9530
   "height": 768,
9557
   "height": 768,
9531
   "isBindingEnabled": true,
9558
   "isBindingEnabled": true,
9675
   "elementType": "selection",
9702
   "elementType": "selection",
9676
   "errorMessage": null,
9703
   "errorMessage": null,
9677
   "exportBackground": true,
9704
   "exportBackground": true,
9705
+  "exportEmbedScene": false,
9678
   "gridSize": null,
9706
   "gridSize": null,
9679
   "height": 768,
9707
   "height": 768,
9680
   "isBindingEnabled": true,
9708
   "isBindingEnabled": true,
9850
   "elementType": "selection",
9878
   "elementType": "selection",
9851
   "errorMessage": null,
9879
   "errorMessage": null,
9852
   "exportBackground": true,
9880
   "exportBackground": true,
9881
+  "exportEmbedScene": false,
9853
   "gridSize": null,
9882
   "gridSize": null,
9854
   "height": 768,
9883
   "height": 768,
9855
   "isBindingEnabled": true,
9884
   "isBindingEnabled": true,
10025
   "elementType": "selection",
10054
   "elementType": "selection",
10026
   "errorMessage": null,
10055
   "errorMessage": null,
10027
   "exportBackground": true,
10056
   "exportBackground": true,
10057
+  "exportEmbedScene": false,
10028
   "gridSize": null,
10058
   "gridSize": null,
10029
   "height": 768,
10059
   "height": 768,
10030
   "isBindingEnabled": true,
10060
   "isBindingEnabled": true,
10200
   "elementType": "selection",
10230
   "elementType": "selection",
10201
   "errorMessage": null,
10231
   "errorMessage": null,
10202
   "exportBackground": true,
10232
   "exportBackground": true,
10233
+  "exportEmbedScene": false,
10203
   "gridSize": null,
10234
   "gridSize": null,
10204
   "height": 768,
10235
   "height": 768,
10205
   "isBindingEnabled": true,
10236
   "isBindingEnabled": true,
10375
   "elementType": "selection",
10406
   "elementType": "selection",
10376
   "errorMessage": null,
10407
   "errorMessage": null,
10377
   "exportBackground": true,
10408
   "exportBackground": true,
10409
+  "exportEmbedScene": false,
10378
   "gridSize": null,
10410
   "gridSize": null,
10379
   "height": 768,
10411
   "height": 768,
10380
   "isBindingEnabled": true,
10412
   "isBindingEnabled": true,
10524
   "elementType": "selection",
10556
   "elementType": "selection",
10525
   "errorMessage": null,
10557
   "errorMessage": null,
10526
   "exportBackground": true,
10558
   "exportBackground": true,
10559
+  "exportEmbedScene": false,
10527
   "gridSize": null,
10560
   "gridSize": null,
10528
   "height": 768,
10561
   "height": 768,
10529
   "isBindingEnabled": true,
10562
   "isBindingEnabled": true,
10673
   "elementType": "selection",
10706
   "elementType": "selection",
10674
   "errorMessage": null,
10707
   "errorMessage": null,
10675
   "exportBackground": true,
10708
   "exportBackground": true,
10709
+  "exportEmbedScene": false,
10676
   "gridSize": null,
10710
   "gridSize": null,
10677
   "height": 768,
10711
   "height": 768,
10678
   "isBindingEnabled": true,
10712
   "isBindingEnabled": true,
10848
   "elementType": "selection",
10882
   "elementType": "selection",
10849
   "errorMessage": null,
10883
   "errorMessage": null,
10850
   "exportBackground": true,
10884
   "exportBackground": true,
10885
+  "exportEmbedScene": false,
10851
   "gridSize": null,
10886
   "gridSize": null,
10852
   "height": 768,
10887
   "height": 768,
10853
   "isBindingEnabled": true,
10888
   "isBindingEnabled": true,
10997
   "elementType": "selection",
11032
   "elementType": "selection",
10998
   "errorMessage": null,
11033
   "errorMessage": null,
10999
   "exportBackground": true,
11034
   "exportBackground": true,
11035
+  "exportEmbedScene": false,
11000
   "gridSize": null,
11036
   "gridSize": null,
11001
   "height": 768,
11037
   "height": 768,
11002
   "isBindingEnabled": true,
11038
   "isBindingEnabled": true,
11172
   "elementType": "selection",
11208
   "elementType": "selection",
11173
   "errorMessage": null,
11209
   "errorMessage": null,
11174
   "exportBackground": true,
11210
   "exportBackground": true,
11211
+  "exportEmbedScene": false,
11175
   "gridSize": null,
11212
   "gridSize": null,
11176
   "height": 768,
11213
   "height": 768,
11177
   "isBindingEnabled": true,
11214
   "isBindingEnabled": true,
11873
   "elementType": "selection",
11910
   "elementType": "selection",
11874
   "errorMessage": null,
11911
   "errorMessage": null,
11875
   "exportBackground": true,
11912
   "exportBackground": true,
11913
+  "exportEmbedScene": false,
11876
   "gridSize": null,
11914
   "gridSize": null,
11877
   "height": 768,
11915
   "height": 768,
11878
   "isBindingEnabled": true,
11916
   "isBindingEnabled": true,
12111
   "elementType": "selection",
12149
   "elementType": "selection",
12112
   "errorMessage": null,
12150
   "errorMessage": null,
12113
   "exportBackground": true,
12151
   "exportBackground": true,
12152
+  "exportEmbedScene": false,
12114
   "gridSize": null,
12153
   "gridSize": null,
12115
   "height": 768,
12154
   "height": 768,
12116
   "isBindingEnabled": true,
12155
   "isBindingEnabled": true,
12198
   "elementType": "rectangle",
12237
   "elementType": "rectangle",
12199
   "errorMessage": null,
12238
   "errorMessage": null,
12200
   "exportBackground": true,
12239
   "exportBackground": true,
12240
+  "exportEmbedScene": false,
12201
   "gridSize": null,
12241
   "gridSize": null,
12202
   "height": 768,
12242
   "height": 768,
12203
   "isBindingEnabled": true,
12243
   "isBindingEnabled": true,
12283
   "elementType": "selection",
12323
   "elementType": "selection",
12284
   "errorMessage": null,
12324
   "errorMessage": null,
12285
   "exportBackground": true,
12325
   "exportBackground": true,
12326
+  "exportEmbedScene": false,
12286
   "gridSize": null,
12327
   "gridSize": null,
12287
   "height": 768,
12328
   "height": 768,
12288
   "isBindingEnabled": true,
12329
   "isBindingEnabled": true,
13160
   "elementType": "selection",
13201
   "elementType": "selection",
13161
   "errorMessage": null,
13202
   "errorMessage": null,
13162
   "exportBackground": true,
13203
   "exportBackground": true,
13204
+  "exportEmbedScene": false,
13163
   "gridSize": null,
13205
   "gridSize": null,
13164
   "height": 768,
13206
   "height": 768,
13165
   "isBindingEnabled": true,
13207
   "isBindingEnabled": true,
13596
   "elementType": "selection",
13638
   "elementType": "selection",
13597
   "errorMessage": null,
13639
   "errorMessage": null,
13598
   "exportBackground": true,
13640
   "exportBackground": true,
13641
+  "exportEmbedScene": false,
13599
   "gridSize": null,
13642
   "gridSize": null,
13600
   "height": 768,
13643
   "height": 768,
13601
   "isBindingEnabled": true,
13644
   "isBindingEnabled": true,
13945
   "elementType": "selection",
13988
   "elementType": "selection",
13946
   "errorMessage": null,
13989
   "errorMessage": null,
13947
   "exportBackground": true,
13990
   "exportBackground": true,
13991
+  "exportEmbedScene": false,
13948
   "gridSize": null,
13992
   "gridSize": null,
13949
   "height": 768,
13993
   "height": 768,
13950
   "isBindingEnabled": true,
13994
   "isBindingEnabled": true,
14211
   "elementType": "selection",
14255
   "elementType": "selection",
14212
   "errorMessage": null,
14256
   "errorMessage": null,
14213
   "exportBackground": true,
14257
   "exportBackground": true,
14258
+  "exportEmbedScene": false,
14214
   "gridSize": null,
14259
   "gridSize": null,
14215
   "height": 768,
14260
   "height": 768,
14216
   "isBindingEnabled": true,
14261
   "isBindingEnabled": true,
14398
   "elementType": "selection",
14443
   "elementType": "selection",
14399
   "errorMessage": null,
14444
   "errorMessage": null,
14400
   "exportBackground": true,
14445
   "exportBackground": true,
14446
+  "exportEmbedScene": false,
14401
   "gridSize": null,
14447
   "gridSize": null,
14402
   "height": 768,
14448
   "height": 768,
14403
   "isBindingEnabled": true,
14449
   "isBindingEnabled": true,
15222
   "elementType": "selection",
15268
   "elementType": "selection",
15223
   "errorMessage": null,
15269
   "errorMessage": null,
15224
   "exportBackground": true,
15270
   "exportBackground": true,
15271
+  "exportEmbedScene": false,
15225
   "gridSize": null,
15272
   "gridSize": null,
15226
   "height": 768,
15273
   "height": 768,
15227
   "isBindingEnabled": true,
15274
   "isBindingEnabled": true,
15943
   "elementType": "selection",
15990
   "elementType": "selection",
15944
   "errorMessage": null,
15991
   "errorMessage": null,
15945
   "exportBackground": true,
15992
   "exportBackground": true,
15993
+  "exportEmbedScene": false,
15946
   "gridSize": null,
15994
   "gridSize": null,
15947
   "height": 768,
15995
   "height": 768,
15948
   "isBindingEnabled": true,
15996
   "isBindingEnabled": true,
16565
   "elementType": "selection",
16613
   "elementType": "selection",
16566
   "errorMessage": null,
16614
   "errorMessage": null,
16567
   "exportBackground": true,
16615
   "exportBackground": true,
16616
+  "exportEmbedScene": false,
16568
   "gridSize": null,
16617
   "gridSize": null,
16569
   "height": 768,
16618
   "height": 768,
16570
   "isBindingEnabled": true,
16619
   "isBindingEnabled": true,
17092
   "elementType": "selection",
17141
   "elementType": "selection",
17093
   "errorMessage": null,
17142
   "errorMessage": null,
17094
   "exportBackground": true,
17143
   "exportBackground": true,
17144
+  "exportEmbedScene": false,
17095
   "gridSize": null,
17145
   "gridSize": null,
17096
   "height": 768,
17146
   "height": 768,
17097
   "isBindingEnabled": true,
17147
   "isBindingEnabled": true,
17573
   "elementType": "selection",
17623
   "elementType": "selection",
17574
   "errorMessage": null,
17624
   "errorMessage": null,
17575
   "exportBackground": true,
17625
   "exportBackground": true,
17626
+  "exportEmbedScene": false,
17576
   "gridSize": null,
17627
   "gridSize": null,
17577
   "height": 768,
17628
   "height": 768,
17578
   "isBindingEnabled": true,
17629
   "isBindingEnabled": true,
17965
   "elementType": "selection",
18016
   "elementType": "selection",
17966
   "errorMessage": null,
18017
   "errorMessage": null,
17967
   "exportBackground": true,
18018
   "exportBackground": true,
18019
+  "exportEmbedScene": false,
17968
   "gridSize": null,
18020
   "gridSize": null,
17969
   "height": 768,
18021
   "height": 768,
17970
   "isBindingEnabled": true,
18022
   "isBindingEnabled": true,
18272
   "elementType": "selection",
18324
   "elementType": "selection",
18273
   "errorMessage": null,
18325
   "errorMessage": null,
18274
   "exportBackground": true,
18326
   "exportBackground": true,
18327
+  "exportEmbedScene": false,
18275
   "gridSize": null,
18328
   "gridSize": null,
18276
   "height": 768,
18329
   "height": 768,
18277
   "isBindingEnabled": true,
18330
   "isBindingEnabled": true,
18498
   "elementType": "selection",
18551
   "elementType": "selection",
18499
   "errorMessage": null,
18552
   "errorMessage": null,
18500
   "exportBackground": true,
18553
   "exportBackground": true,
18554
+  "exportEmbedScene": false,
18501
   "gridSize": null,
18555
   "gridSize": null,
18502
   "height": 768,
18556
   "height": 768,
18503
   "isBindingEnabled": true,
18557
   "isBindingEnabled": true,
19375
   "elementType": "selection",
19429
   "elementType": "selection",
19376
   "errorMessage": null,
19430
   "errorMessage": null,
19377
   "exportBackground": true,
19431
   "exportBackground": true,
19432
+  "exportEmbedScene": false,
19378
   "gridSize": null,
19433
   "gridSize": null,
19379
   "height": 768,
19434
   "height": 768,
19380
   "isBindingEnabled": true,
19435
   "isBindingEnabled": true,
20147
   "elementType": "selection",
20202
   "elementType": "selection",
20148
   "errorMessage": null,
20203
   "errorMessage": null,
20149
   "exportBackground": true,
20204
   "exportBackground": true,
20205
+  "exportEmbedScene": false,
20150
   "gridSize": null,
20206
   "gridSize": null,
20151
   "height": 768,
20207
   "height": 768,
20152
   "isBindingEnabled": true,
20208
   "isBindingEnabled": true,
20818
   "elementType": "selection",
20874
   "elementType": "selection",
20819
   "errorMessage": null,
20875
   "errorMessage": null,
20820
   "exportBackground": true,
20876
   "exportBackground": true,
20877
+  "exportEmbedScene": false,
20821
   "gridSize": null,
20878
   "gridSize": null,
20822
   "height": 768,
20879
   "height": 768,
20823
   "isBindingEnabled": true,
20880
   "isBindingEnabled": true,
21392
   "elementType": "selection",
21449
   "elementType": "selection",
21393
   "errorMessage": null,
21450
   "errorMessage": null,
21394
   "exportBackground": true,
21451
   "exportBackground": true,
21452
+  "exportEmbedScene": false,
21395
   "gridSize": null,
21453
   "gridSize": null,
21396
   "height": 768,
21454
   "height": 768,
21397
   "isBindingEnabled": true,
21455
   "isBindingEnabled": true,
21541
   "elementType": "selection",
21599
   "elementType": "selection",
21542
   "errorMessage": null,
21600
   "errorMessage": null,
21543
   "exportBackground": true,
21601
   "exportBackground": true,
21602
+  "exportEmbedScene": false,
21544
   "gridSize": null,
21603
   "gridSize": null,
21545
   "height": 768,
21604
   "height": 768,
21546
   "isBindingEnabled": true,
21605
   "isBindingEnabled": true,
21834
   "elementType": "selection",
21893
   "elementType": "selection",
21835
   "errorMessage": null,
21894
   "errorMessage": null,
21836
   "exportBackground": true,
21895
   "exportBackground": true,
21896
+  "exportEmbedScene": false,
21837
   "gridSize": null,
21897
   "gridSize": null,
21838
   "height": 768,
21898
   "height": 768,
21839
   "isBindingEnabled": true,
21899
   "isBindingEnabled": true,
22127
   "elementType": "selection",
22187
   "elementType": "selection",
22128
   "errorMessage": null,
22188
   "errorMessage": null,
22129
   "exportBackground": true,
22189
   "exportBackground": true,
22190
+  "exportEmbedScene": false,
22130
   "gridSize": null,
22191
   "gridSize": null,
22131
   "height": 768,
22192
   "height": 768,
22132
   "isBindingEnabled": true,
22193
   "isBindingEnabled": true,
22276
   "elementType": "selection",
22337
   "elementType": "selection",
22277
   "errorMessage": null,
22338
   "errorMessage": null,
22278
   "exportBackground": true,
22339
   "exportBackground": true,
22340
+  "exportEmbedScene": false,
22279
   "gridSize": null,
22341
   "gridSize": null,
22280
   "height": 768,
22342
   "height": 768,
22281
   "isBindingEnabled": true,
22343
   "isBindingEnabled": true,
22457
   "elementType": "selection",
22519
   "elementType": "selection",
22458
   "errorMessage": null,
22520
   "errorMessage": null,
22459
   "exportBackground": true,
22521
   "exportBackground": true,
22522
+  "exportEmbedScene": false,
22460
   "gridSize": null,
22523
   "gridSize": null,
22461
   "height": 768,
22524
   "height": 768,
22462
   "isBindingEnabled": true,
22525
   "isBindingEnabled": true,
22691
   "elementType": "selection",
22754
   "elementType": "selection",
22692
   "errorMessage": null,
22755
   "errorMessage": null,
22693
   "exportBackground": true,
22756
   "exportBackground": true,
22757
+  "exportEmbedScene": false,
22694
   "gridSize": null,
22758
   "gridSize": null,
22695
   "height": 768,
22759
   "height": 768,
22696
   "isBindingEnabled": true,
22760
   "isBindingEnabled": true,
23000
   "elementType": "selection",
23064
   "elementType": "selection",
23001
   "errorMessage": null,
23065
   "errorMessage": null,
23002
   "exportBackground": true,
23066
   "exportBackground": true,
23067
+  "exportEmbedScene": false,
23003
   "gridSize": null,
23068
   "gridSize": null,
23004
   "height": 768,
23069
   "height": 768,
23005
   "isBindingEnabled": true,
23070
   "isBindingEnabled": true,
23824
   "elementType": "selection",
23889
   "elementType": "selection",
23825
   "errorMessage": null,
23890
   "errorMessage": null,
23826
   "exportBackground": true,
23891
   "exportBackground": true,
23892
+  "exportEmbedScene": false,
23827
   "gridSize": null,
23893
   "gridSize": null,
23828
   "height": 768,
23894
   "height": 768,
23829
   "isBindingEnabled": true,
23895
   "isBindingEnabled": true,
24117
   "elementType": "selection",
24183
   "elementType": "selection",
24118
   "errorMessage": null,
24184
   "errorMessage": null,
24119
   "exportBackground": true,
24185
   "exportBackground": true,
24186
+  "exportEmbedScene": false,
24120
   "gridSize": null,
24187
   "gridSize": null,
24121
   "height": 768,
24188
   "height": 768,
24122
   "isBindingEnabled": true,
24189
   "isBindingEnabled": true,
24410
   "elementType": "selection",
24477
   "elementType": "selection",
24411
   "errorMessage": null,
24478
   "errorMessage": null,
24412
   "exportBackground": true,
24479
   "exportBackground": true,
24480
+  "exportEmbedScene": false,
24413
   "gridSize": null,
24481
   "gridSize": null,
24414
   "height": 768,
24482
   "height": 768,
24415
   "isBindingEnabled": true,
24483
   "isBindingEnabled": true,
24774
   "elementType": "selection",
24842
   "elementType": "selection",
24775
   "errorMessage": null,
24843
   "errorMessage": null,
24776
   "exportBackground": true,
24844
   "exportBackground": true,
24845
+  "exportEmbedScene": false,
24777
   "gridSize": null,
24846
   "gridSize": null,
24778
   "height": 768,
24847
   "height": 768,
24779
   "isBindingEnabled": true,
24848
   "isBindingEnabled": true,
24926
   "elementType": "selection",
24995
   "elementType": "selection",
24927
   "errorMessage": null,
24996
   "errorMessage": null,
24928
   "exportBackground": true,
24997
   "exportBackground": true,
24998
+  "exportEmbedScene": false,
24929
   "gridSize": null,
24999
   "gridSize": null,
24930
   "height": 768,
25000
   "height": 768,
24931
   "isBindingEnabled": true,
25001
   "isBindingEnabled": true,
25232
   "elementType": "selection",
25302
   "elementType": "selection",
25233
   "errorMessage": null,
25303
   "errorMessage": null,
25234
   "exportBackground": true,
25304
   "exportBackground": true,
25305
+  "exportEmbedScene": false,
25235
   "gridSize": null,
25306
   "gridSize": null,
25236
   "height": 768,
25307
   "height": 768,
25237
   "isBindingEnabled": true,
25308
   "isBindingEnabled": true,
25472
   "elementType": "selection",
25543
   "elementType": "selection",
25473
   "errorMessage": null,
25544
   "errorMessage": null,
25474
   "exportBackground": true,
25545
   "exportBackground": true,
25546
+  "exportEmbedScene": false,
25475
   "gridSize": null,
25547
   "gridSize": null,
25476
   "height": 768,
25548
   "height": 768,
25477
   "isBindingEnabled": true,
25549
   "isBindingEnabled": true,
25784
   "elementType": "selection",
25856
   "elementType": "selection",
25785
   "errorMessage": null,
25857
   "errorMessage": null,
25786
   "exportBackground": true,
25858
   "exportBackground": true,
25859
+  "exportEmbedScene": false,
25787
   "gridSize": null,
25860
   "gridSize": null,
25788
   "height": 768,
25861
   "height": 768,
25789
   "isBindingEnabled": true,
25862
   "isBindingEnabled": true,
25869
   "elementType": "selection",
25942
   "elementType": "selection",
25870
   "errorMessage": null,
25943
   "errorMessage": null,
25871
   "exportBackground": true,
25944
   "exportBackground": true,
25945
+  "exportEmbedScene": false,
25872
   "gridSize": null,
25946
   "gridSize": null,
25873
   "height": 768,
25947
   "height": 768,
25874
   "isBindingEnabled": true,
25948
   "isBindingEnabled": true,
26018
   "elementType": "selection",
26092
   "elementType": "selection",
26019
   "errorMessage": null,
26093
   "errorMessage": null,
26020
   "exportBackground": true,
26094
   "exportBackground": true,
26095
+  "exportEmbedScene": false,
26021
   "gridSize": null,
26096
   "gridSize": null,
26022
   "height": 768,
26097
   "height": 768,
26023
   "isBindingEnabled": true,
26098
   "isBindingEnabled": true,
26824
   "elementType": "selection",
26899
   "elementType": "selection",
26825
   "errorMessage": null,
26900
   "errorMessage": null,
26826
   "exportBackground": true,
26901
   "exportBackground": true,
26902
+  "exportEmbedScene": false,
26827
   "gridSize": null,
26903
   "gridSize": null,
26828
   "height": 768,
26904
   "height": 768,
26829
   "isBindingEnabled": true,
26905
   "isBindingEnabled": true,
26909
   "elementType": "selection",
26985
   "elementType": "selection",
26910
   "errorMessage": null,
26986
   "errorMessage": null,
26911
   "exportBackground": true,
26987
   "exportBackground": true,
26988
+  "exportEmbedScene": false,
26912
   "gridSize": null,
26989
   "gridSize": null,
26913
   "height": 768,
26990
   "height": 768,
26914
   "isBindingEnabled": true,
26991
   "isBindingEnabled": true,
27646
   "elementType": "selection",
27723
   "elementType": "selection",
27647
   "errorMessage": null,
27724
   "errorMessage": null,
27648
   "exportBackground": true,
27725
   "exportBackground": true,
27726
+  "exportEmbedScene": false,
27649
   "gridSize": null,
27727
   "gridSize": null,
27650
   "height": 768,
27728
   "height": 768,
27651
   "isBindingEnabled": true,
27729
   "isBindingEnabled": true,
28036
   "elementType": "selection",
28114
   "elementType": "selection",
28037
   "errorMessage": null,
28115
   "errorMessage": null,
28038
   "exportBackground": true,
28116
   "exportBackground": true,
28117
+  "exportEmbedScene": false,
28039
   "gridSize": null,
28118
   "gridSize": null,
28040
   "height": 768,
28119
   "height": 768,
28041
   "isBindingEnabled": true,
28120
   "isBindingEnabled": true,
28294
   "elementType": "selection",
28373
   "elementType": "selection",
28295
   "errorMessage": null,
28374
   "errorMessage": null,
28296
   "exportBackground": true,
28375
   "exportBackground": true,
28376
+  "exportEmbedScene": false,
28297
   "gridSize": null,
28377
   "gridSize": null,
28298
   "height": 768,
28378
   "height": 768,
28299
   "isBindingEnabled": true,
28379
   "isBindingEnabled": true,
28381
   "elementType": "selection",
28461
   "elementType": "selection",
28382
   "errorMessage": null,
28462
   "errorMessage": null,
28383
   "exportBackground": true,
28463
   "exportBackground": true,
28464
+  "exportEmbedScene": false,
28384
   "gridSize": null,
28465
   "gridSize": null,
28385
   "height": 768,
28466
   "height": 768,
28386
   "isBindingEnabled": true,
28467
   "isBindingEnabled": true,
28858
   "elementType": "text",
28939
   "elementType": "text",
28859
   "errorMessage": null,
28940
   "errorMessage": null,
28860
   "exportBackground": true,
28941
   "exportBackground": true,
28942
+  "exportEmbedScene": false,
28861
   "gridSize": null,
28943
   "gridSize": null,
28862
   "height": 768,
28944
   "height": 768,
28863
   "isBindingEnabled": true,
28945
   "isBindingEnabled": true,
28943
   "elementType": "selection",
29025
   "elementType": "selection",
28944
   "errorMessage": null,
29026
   "errorMessage": null,
28945
   "exportBackground": true,
29027
   "exportBackground": true,
29028
+  "exportEmbedScene": false,
28946
   "gridSize": null,
29029
   "gridSize": null,
28947
   "height": 768,
29030
   "height": 768,
28948
   "isBindingEnabled": true,
29031
   "isBindingEnabled": true,

+ 1
- 0
src/types.ts Bestand weergeven

47
   elementType: typeof SHAPES[number]["value"];
47
   elementType: typeof SHAPES[number]["value"];
48
   elementLocked: boolean;
48
   elementLocked: boolean;
49
   exportBackground: boolean;
49
   exportBackground: boolean;
50
+  exportEmbedScene: boolean;
50
   shouldAddWatermark: boolean;
51
   shouldAddWatermark: boolean;
51
   currentItemStrokeColor: string;
52
   currentItemStrokeColor: string;
52
   currentItemBackgroundColor: string;
53
   currentItemBackgroundColor: string;

Laden…
Annuleren
Opslaan