index.ts 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. import { fileSave } from "browser-nativefs";
  2. import { EVENT_IO, trackEvent } from "../analytics";
  3. import {
  4. copyCanvasToClipboardAsPng,
  5. copyTextToSystemClipboard,
  6. } from "../clipboard";
  7. import { NonDeletedExcalidrawElement } from "../element/types";
  8. import { t } from "../i18n";
  9. import { exportToCanvas, exportToSvg } from "../scene/export";
  10. import { ExportType } from "../scene/types";
  11. import { canvasToBlob } from "./blob";
  12. import { AppState } from "../types";
  13. import { serializeAsJSON } from "./json";
  14. export { loadFromBlob } from "./blob";
  15. export { loadFromJSON, saveAsJSON } from "./json";
  16. export const exportCanvas = async (
  17. type: ExportType,
  18. elements: readonly NonDeletedExcalidrawElement[],
  19. appState: AppState,
  20. canvas: HTMLCanvasElement,
  21. {
  22. exportBackground,
  23. exportPadding = 10,
  24. viewBackgroundColor,
  25. name,
  26. scale = 1,
  27. shouldAddWatermark,
  28. }: {
  29. exportBackground: boolean;
  30. exportPadding?: number;
  31. viewBackgroundColor: string;
  32. name: string;
  33. scale?: number;
  34. shouldAddWatermark: boolean;
  35. },
  36. ) => {
  37. if (elements.length === 0) {
  38. return window.alert(t("alerts.cannotExportEmptyCanvas"));
  39. }
  40. if (type === "svg" || type === "clipboard-svg") {
  41. const tempSvg = exportToSvg(elements, {
  42. exportBackground,
  43. viewBackgroundColor,
  44. exportPadding,
  45. scale,
  46. shouldAddWatermark,
  47. metadata:
  48. appState.exportEmbedScene && type === "svg"
  49. ? await (
  50. await import(/* webpackChunkName: "image" */ "./image")
  51. ).encodeSvgMetadata({
  52. text: serializeAsJSON(elements, appState),
  53. })
  54. : undefined,
  55. });
  56. if (type === "svg") {
  57. await fileSave(new Blob([tempSvg.outerHTML], { type: "image/svg+xml" }), {
  58. fileName: `${name}.svg`,
  59. extensions: [".svg"],
  60. });
  61. trackEvent(EVENT_IO, "export", "svg");
  62. return;
  63. } else if (type === "clipboard-svg") {
  64. trackEvent(EVENT_IO, "export", "clipboard-svg");
  65. copyTextToSystemClipboard(tempSvg.outerHTML);
  66. return;
  67. }
  68. }
  69. const tempCanvas = exportToCanvas(elements, appState, {
  70. exportBackground,
  71. viewBackgroundColor,
  72. exportPadding,
  73. scale,
  74. shouldAddWatermark,
  75. });
  76. tempCanvas.style.display = "none";
  77. document.body.appendChild(tempCanvas);
  78. if (type === "png") {
  79. const fileName = `${name}.png`;
  80. let blob = await canvasToBlob(tempCanvas);
  81. if (appState.exportEmbedScene) {
  82. blob = await (
  83. await import(/* webpackChunkName: "image" */ "./image")
  84. ).encodePngMetadata({
  85. blob,
  86. metadata: serializeAsJSON(elements, appState),
  87. });
  88. }
  89. await fileSave(blob, {
  90. fileName,
  91. extensions: [".png"],
  92. });
  93. trackEvent(EVENT_IO, "export", "png");
  94. } else if (type === "clipboard") {
  95. try {
  96. await copyCanvasToClipboardAsPng(tempCanvas);
  97. trackEvent(EVENT_IO, "export", "clipboard-png");
  98. } catch (error) {
  99. if (error.name === "CANVAS_POSSIBLY_TOO_BIG") {
  100. throw error;
  101. }
  102. throw new Error(t("alerts.couldNotCopyToClipboard"));
  103. }
  104. }
  105. // clean up the DOM
  106. if (tempCanvas !== canvas) {
  107. tempCanvas.remove();
  108. }
  109. };